qtimetrap 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/app/components/entries/branch_hierarchy_helpers.rb +4 -4
  4. data/app/components/entries/leaf_archive_helpers.rb +6 -6
  5. data/app/components/entries/leaf_note_helpers.rb +11 -11
  6. data/app/components/entries/leaf_task_display_helpers.rb +20 -0
  7. data/app/components/entries/leaf_task_editor_state_helpers.rb +69 -0
  8. data/app/components/entries/leaf_task_helpers.rb +91 -46
  9. data/app/components/entries/leaf_time_helpers.rb +9 -9
  10. data/app/components/entries/list_component.rb +8 -7
  11. data/app/components/entries/list_host_helpers.rb +3 -3
  12. data/app/components/entries/list_state_helpers.rb +60 -4
  13. data/app/components/entries/list_task_editor_helpers.rb +31 -0
  14. data/app/components/entries/render_helpers.rb +2 -2
  15. data/app/components/entries/tree_helpers.rb +9 -7
  16. data/app/components/entries/tree_toolbar_helpers.rb +12 -12
  17. data/app/components/project_sidebar/archive_toggle_helpers.rb +6 -6
  18. data/app/components/project_sidebar/component.rb +7 -7
  19. data/app/components/project_sidebar/logo_helpers.rb +12 -12
  20. data/app/components/project_sidebar/project_button_helpers.rb +7 -7
  21. data/app/components/project_sidebar/task_helpers.rb +11 -11
  22. data/app/components/qt_ui_helpers.rb +9 -9
  23. data/app/components/tracker_controls/component.rb +8 -8
  24. data/app/components/tracker_controls/layout_builder.rb +19 -11
  25. data/app/components/tracker_controls/layout_helpers.rb +5 -5
  26. data/app/styles/themes/dark/entries_list.qss +23 -3
  27. data/app/styles/themes/light/entries_list.qss +23 -3
  28. data/app/views/main_window.rb +2 -3
  29. data/app/views/main_window_layout_builder.rb +6 -6
  30. data/app/views/main_window_runtime.rb +2 -2
  31. data/app/views/main_window_runtime_key_helpers.rb +24 -5
  32. data/app/views/main_window_splitter_toggle_bootstrap_helpers.rb +1 -1
  33. data/app/views/main_window_splitter_toggle_helpers.rb +10 -10
  34. data/app/views/main_window_splitter_toggle_hover_helpers.rb +2 -2
  35. data/app/views/main_window_ui_helpers.rb +3 -3
  36. data/app/views/window_icon_loader.rb +1 -1
  37. data/lib/qtimetrap/version.rb +1 -1
  38. data/lib/rubocop/cop/qt/prefer_ruby_style_setter.rb +50 -0
  39. metadata +9 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c354f5a65349384bd932fceb8f62cb7b43927c789b67bcd57a5c064baf6754f
4
- data.tar.gz: a0b067c516cfc16874afcd1f98088e8e8dbf023c079bc26cdb390c2e6e0c6b58
3
+ metadata.gz: 5287d989740a397f7ed84b151eb5d3d48ab8edd480bb580517cfdedc8e20bb54
4
+ data.tar.gz: 6234dfac9679d2b577999f83b561226d03bc70201743f8df8c4668d0e8c5fe57
5
5
  SHA512:
6
- metadata.gz: b8a21804d830a6b48bedd2bcd3bf53b7ddc00db6d9b38174a3c6ff0a7fc63dc7b41e61ba2ac301a4aeef5c9b9d432c096bd1c1bce614ada07fb7c50a19e4d840
7
- data.tar.gz: ba9ba9ff36f90346913699562b13c0842281e5c27bab6e22f5e326638b9c49bfceca7f51039345d84a994831065c1a73bfe8891ebed147ddfea2abb0a534a11d
6
+ metadata.gz: 140de50f2fde21743d1f58e108ceed523906dc79d2971ae519a45dda8fbdce3e5850f6a179c926580d6b3436450b2dd56f96a725619dc09fc8191fb3127db18c
7
+ data.tar.gz: 1cb985651e8bc995f9ad5d9fe554c5ef151a11a2bdcd88c31882c872d17bf765ab3cb3f1aaaf6924dc1bd44aef947c47a991b55447a76f2c5f29c93f950f9ec2
data/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.2] - 2026-03-17
6
+
7
+ ### Added
8
+ - Editable task reassignment for individual entries.
9
+ - Archive mode with archive/restore flow for entry rows.
10
+ - Multi-select project filtering in the sidebar with recent-activity ordering for projects and tasks.
11
+ - Date/time range filtering for rendered entry nodes.
12
+ - Custom RuboCop enforcement for Ruby-style Qt setter usage in `app/**`.
13
+
14
+ ### Changed
15
+ - Switched project license to BSD-2-Clause.
16
+ - Moved specs under `spec/qtimetrap/**` and removed the RuboCop todo file.
17
+ - Improved README, packaging metadata, and release preparation.
18
+ - Tracker inputs now take focus on direct click when editing is allowed.
19
+
20
+ ### Fixed
21
+ - Preserved running tracker inputs when selecting sidebar tasks.
22
+ - Closed inline editors on outside click without relying on application-level hit-testing workarounds.
23
+ - Restored entry task editor styling and display-field behavior.
24
+ - Reduced UI regressions around inline task editing, scrolling, and blur handling.
25
+
26
+ ## [0.1.1] - 2026-03-14
27
+
28
+ ### Added
29
+ - Fedora/COPR packaging, desktop entry installation, and application icons.
30
+ - Sidebar task filtering, multi-project filtering, and archive support groundwork.
31
+ - Editable note and time updates for entry leaves.
32
+
33
+ ### Changed
34
+ - Updated runtime dependency to newer `qt` releases in the 0.1 series.
35
+
36
+ ## [0.1.0] - 2026-03-05
37
+
38
+ ### Added
39
+ - Initial release of QTimetrap as a Ruby gem with a Qt desktop UI.
40
+ - MVVM application structure with Zeitwerk autoloading, themed QSS styling, and Timetrap integration.
@@ -8,10 +8,10 @@ module QTimetrap
8
8
 
9
9
  def build_children_container(parent_widget)
10
10
  container = QWidget.new(parent_widget)
11
- container.set_object_name('entry_node_children')
11
+ container.object_name = 'entry_node_children'
12
12
  layout = QVBoxLayout.new(container)
13
13
  layout.set_contents_margins(0, 0, 0, 0)
14
- layout.set_spacing(2)
14
+ layout.spacing = 2
15
15
  [container, layout]
16
16
  end
17
17
 
@@ -32,9 +32,9 @@ module QTimetrap
32
32
  binding = branch_bindings[node_id]
33
33
  return unless binding
34
34
 
35
- binding.fetch(:children_container).set_visible(visible)
35
+ binding.fetch(:children_container).visible = visible
36
36
  text = branch_button_text(binding.fetch(:level), binding.fetch(:label), visible)
37
- binding.fetch(:button).set_text(text)
37
+ binding.fetch(:button).text = text
38
38
  end
39
39
  end
40
40
  end
@@ -8,12 +8,12 @@ module QTimetrap
8
8
 
9
9
  def build_entry_archive_button(row, node)
10
10
  QPushButton.new(row).tap do |button|
11
- button.set_object_name('entry_node_entry_archive')
12
- button.set_text('🗃')
13
- button.set_tool_tip('Toggle archive state')
14
- button.set_focus_policy(Qt::NoFocus)
15
- button.set_fixed_width(28)
16
- button.set_fixed_height(24)
11
+ button.object_name = 'entry_node_entry_archive'
12
+ button.text = '🗃'
13
+ button.tool_tip = 'Toggle archive state'
14
+ button.focus_policy = Qt::NoFocus
15
+ button.fixed_width = 28
16
+ button.fixed_height = 24
17
17
  entry_id = resolve_entry_id(node)
18
18
  button.connect('clicked') { |_| on_entry_archive&.call(entry_id) }
19
19
  end
@@ -31,32 +31,32 @@ module QTimetrap
31
31
 
32
32
  def build_entry_row(parent_widget)
33
33
  QWidget.new(parent_widget).tap do |row|
34
- row.set_object_name('entry_node_entry_row')
35
- row.set_fixed_width(branch_button_width)
36
- row.set_fixed_height(32)
34
+ row.object_name = 'entry_node_entry_row'
35
+ row.fixed_width = branch_button_width
36
+ row.fixed_height = 32
37
37
  end
38
38
  end
39
39
 
40
40
  def build_entry_row_layout(row)
41
41
  QHBoxLayout.new(row).tap do |layout|
42
42
  layout.set_contents_margins(8, 0, 8, 0)
43
- layout.set_spacing(6)
43
+ layout.spacing = 6
44
44
  end
45
45
  end
46
46
 
47
47
  def build_entry_prefix_label(row, node, level)
48
48
  QLabel.new(row).tap do |prefix_label|
49
- prefix_label.set_object_name('entry_node_entry_prefix')
50
- prefix_label.set_text("#{indent(level)}#{node.fetch(:prefix, node.fetch(:label))}")
49
+ prefix_label.object_name = 'entry_node_entry_prefix'
50
+ prefix_label.text = "#{indent(level)}#{node.fetch(:prefix, node.fetch(:label))}"
51
51
  end
52
52
  end
53
53
 
54
54
  def build_entry_note_input(row, node)
55
55
  QLineEdit.new(row).tap do |note_input|
56
- note_input.set_object_name('entry_node_entry_note')
56
+ note_input.object_name = 'entry_node_entry_note'
57
57
  note_input.text = node.fetch(:note, '')
58
- note_input.set_placeholder_text('(no note)')
59
- note_input.set_read_only(true)
58
+ note_input.placeholder_text = '(no note)'
59
+ note_input.read_only = true
60
60
  bind_entry_note_input_events(note_input, resolve_entry_id(node))
61
61
  end
62
62
  end
@@ -83,12 +83,12 @@ module QTimetrap
83
83
  end
84
84
 
85
85
  def activate_entry_note_input(note_input)
86
- note_input.set_read_only(false)
86
+ note_input.read_only = false
87
87
  note_input.set_focus
88
88
  end
89
89
 
90
90
  def deactivate_entry_note_input(note_input)
91
- note_input.set_read_only(true)
91
+ note_input.read_only = true
92
92
  note_input.clear_focus
93
93
  end
94
94
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Entries
5
+ # Display-field helpers for entry task controls.
6
+ module LeafTaskDisplayHelpers
7
+ private
8
+
9
+ def reset_task_display_viewport(task_input)
10
+ task_input.cursor_position = 0
11
+ end
12
+
13
+ def sync_task_display_field(task_input, task_name)
14
+ task_input.text = task_name
15
+ task_input.tool_tip = task_name
16
+ reset_task_display_viewport(task_input)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Entries
5
+ # State transitions for entry task display/editor widgets.
6
+ module LeafTaskEditorStateHelpers
7
+ private
8
+
9
+ def activate_entry_task_input(task_container)
10
+ close_previous_task_editor(task_container)
11
+ show_entry_task_editor(task_container)
12
+ self.active_task_container = task_container
13
+ editor = entry_task_editor(task_container)
14
+ editor_input = entry_task_editor_line_edit(editor)
15
+ editor_input.read_only = false
16
+ editor.set_focus
17
+ editor_input.set_focus
18
+ editor.show_popup
19
+ end
20
+
21
+ def deactivate_entry_task_input(task_container)
22
+ editor = entry_task_editor(task_container)
23
+ editor_input = entry_task_editor_line_edit(editor)
24
+ editor_input.read_only = true
25
+ editor.hide_popup
26
+ editor.clear_focus
27
+ show_entry_task_display(task_container)
28
+ end
29
+
30
+ def handle_entry_task_focus_out(task_container)
31
+ return unless task_container
32
+
33
+ reset_entry_task_editor(task_container)
34
+ deactivate_entry_task_input(task_container)
35
+ return unless active_task_container
36
+ return unless task_container_matches?(active_task_container, task_container)
37
+
38
+ self.active_task_container = nil
39
+ end
40
+
41
+ def close_previous_task_editor(task_container)
42
+ return unless active_task_container
43
+ return if task_container_matches?(active_task_container, task_container)
44
+
45
+ handle_entry_task_focus_out(active_task_container)
46
+ end
47
+
48
+ def reset_entry_task_editor(task_container)
49
+ entry_task_editor(task_container).current_text = entry_task_display_input(task_container).text.to_s
50
+ end
51
+
52
+ def show_entry_task_editor(task_container)
53
+ entry_task_display_input(task_container).hide
54
+ entry_task_editor(task_container).show
55
+ end
56
+
57
+ def show_entry_task_display(task_container)
58
+ entry_task_editor(task_container).hide
59
+ entry_task_display_input(task_container).show
60
+ end
61
+
62
+ def sync_entry_task_display(task_container, task_name)
63
+ sync_task_display_field(entry_task_display_input(task_container), task_name)
64
+ entry_task_editor(task_container).current_text = task_name
65
+ entry_task_editor(task_container).tool_tip = task_name
66
+ end
67
+ end
68
+ end
69
+ end
@@ -2,79 +2,124 @@
2
2
 
3
3
  module QTimetrap
4
4
  module Entries
5
- # Entry-leaf helpers for editable task combo-box controls.
5
+ # Entry-leaf helpers for editable task controls.
6
6
  module LeafTaskHelpers
7
7
  private
8
8
 
9
9
  def build_entry_task_input(row, node)
10
- QComboBox.new(row).tap do |task_input|
11
- task_input.set_object_name('entry_node_entry_task')
12
- task_input.set_editable(true)
13
- task_input.instance_variable_set(:@qtimetrap_task_line_edit, build_entry_task_line_edit(row))
14
- task_input.set_line_edit(entry_task_line_edit(task_input))
15
- task_input.set_placeholder_text('task')
16
- task_input.set_minimum_width(220)
17
- populate_entry_task_options(task_input, node)
18
- bind_entry_task_input_events(task_input, resolve_entry_id(node))
10
+ task_name = node.fetch(:task_name, '').to_s
11
+ QWidget.new(row).tap do |task_container|
12
+ initialize_entry_task_container(task_container, task_name, node)
13
+ bind_entry_task_input_events(task_container, resolve_entry_id(node))
14
+ show_entry_task_display(task_container)
19
15
  end
20
16
  end
21
17
 
22
- def build_entry_task_line_edit(row)
23
- QLineEdit.new(row).tap do |line_edit|
24
- line_edit.set_object_name('entry_node_entry_task_input')
18
+ def initialize_entry_task_container(task_container, task_name, node)
19
+ task_container.object_name = 'entry_node_entry_task_container'
20
+ build_entry_task_layout(task_container)
21
+ display_input = build_entry_task_display(task_container, task_name)
22
+ editor, line_edit = build_entry_task_editor(task_container, task_name, node)
23
+ register_task_widgets(task_container, display_input, editor, line_edit)
24
+ end
25
+
26
+ def build_entry_task_layout(task_container)
27
+ QHBoxLayout.new(task_container).tap do |layout|
28
+ layout.set_contents_margins(0, 0, 0, 0)
29
+ layout.spacing = 0
25
30
  end
26
31
  end
27
32
 
28
- def populate_entry_task_options(task_input, node)
29
- current_task = node.fetch(:task_name, '').to_s
30
- suggestions = Array(task_suggestions_for_project&.call(node.fetch(:project_name, '').to_s))
31
- options = ([current_task] + suggestions.map(&:to_s)).reject(&:empty?).uniq
32
- options.each { |task_name| task_input.add_item(task_name) }
33
- task_input.set_current_text(current_task)
34
- deactivate_entry_task_input(task_input)
33
+ def build_entry_task_display(task_container, task_name)
34
+ QLineEdit.new(task_container).tap do |task_input|
35
+ task_input.object_name = 'entry_node_entry_task'
36
+ task_input.text = task_name
37
+ task_input.tool_tip = task_name
38
+ task_input.placeholder_text = 'task'
39
+ task_input.minimum_width = 220
40
+ task_input.read_only = true
41
+ sync_task_display_field(task_input, task_name)
42
+ task_container.layout.add_widget(task_input)
43
+ end
35
44
  end
36
45
 
37
- def bind_entry_task_input_events(task_input, entry_id)
38
- input = entry_task_line_edit(task_input)
39
- input.on(:mouse_button_press) { |_| activate_entry_task_input(task_input) }
40
- input.on(:key_press) { |event| handle_entry_task_key_press(task_input, entry_id, event) }
41
- input.connect('returnPressed') { |_| handle_entry_task_commit(task_input, entry_id) }
42
- input.on(:focus_out) { |_| handle_entry_task_focus_out(task_input) }
43
- task_input.connect('textActivated') { |_| handle_entry_task_commit(task_input, entry_id, force: true) }
46
+ def build_entry_task_editor(task_container, task_name, node)
47
+ QComboBox.new(task_container).tap do |editor|
48
+ line_edit = configure_entry_task_editor(editor, task_container, task_name, node)
49
+ task_container.layout.add_widget(editor)
50
+ editor.hide
51
+ return [editor, line_edit]
52
+ end
44
53
  end
45
54
 
46
- def activate_entry_task_input(task_input)
47
- entry_task_line_edit(task_input).set_read_only(false)
48
- task_input.set_focus
55
+ def configure_entry_task_editor(editor, task_container, task_name, node)
56
+ editor.object_name = 'entry_node_entry_task_editor'
57
+ editor.editable = true
58
+ editor.focus_policy = Qt::StrongFocus
59
+ editor.minimum_width = 220
60
+ editor.tool_tip = task_name
61
+ line_edit = build_entry_task_line_edit(task_container)
62
+ editor.instance_variable_set(:@qtimetrap_task_line_edit, line_edit)
63
+ editor.line_edit = line_edit
64
+ populate_entry_task_options(editor, task_name, node)
65
+ line_edit
49
66
  end
50
67
 
51
- def deactivate_entry_task_input(task_input)
52
- entry_task_line_edit(task_input).set_read_only(true)
53
- task_input.clear_focus
68
+ def build_entry_task_line_edit(task_container)
69
+ QLineEdit.new(task_container).tap do |line_edit|
70
+ line_edit.object_name = 'entry_node_entry_task_editor_input'
71
+ end
54
72
  end
55
73
 
56
- def handle_entry_task_key_press(task_input, entry_id, event)
57
- return unless enter_key?(event)
74
+ def populate_entry_task_options(editor, task_name, node)
75
+ suggestions = Array(task_suggestions_for_project&.call(node.fetch(:project_name, '').to_s))
76
+ ([task_name] + suggestions.map(&:to_s)).reject(&:empty?).uniq.each do |suggestion|
77
+ editor.add_item(suggestion)
78
+ end
79
+ editor.current_text = task_name
80
+ end
81
+
82
+ def bind_entry_task_input_events(task_container, entry_id)
83
+ display_input = entry_task_display_input(task_container)
84
+ editor = entry_task_editor(task_container)
85
+ editor_input = entry_task_editor_line_edit(editor)
58
86
 
59
- handle_entry_task_commit(task_input, entry_id)
87
+ display_input.on(:mouse_button_press) { |_| activate_entry_task_input(task_container) }
88
+ editor_input.on(:key_press) { |event| handle_entry_task_key_press(task_container, entry_id, event) }
89
+ editor_input.connect('returnPressed') { |_| handle_entry_task_commit(task_container, entry_id) }
90
+ editor_input.on(:focus_out) { |_| handle_entry_task_focus_out(task_container) }
91
+ editor.connect('textActivated') { |_| handle_entry_task_commit(task_container, entry_id, force: true) }
60
92
  end
61
93
 
62
- def handle_entry_task_commit(task_input, entry_id, force: false)
63
- return if entry_task_line_edit(task_input).is_read_only && !force
94
+ def entry_task_display_input(task_container)
95
+ task_display_input_for(task_container)
96
+ end
64
97
 
65
- task_name = task_input.current_text.to_s
66
- deactivate_entry_task_input(task_input)
67
- return unless on_entry_task_change
98
+ def entry_task_editor(task_container)
99
+ task_editor_for(task_container)
100
+ end
68
101
 
69
- on_entry_task_change.call(entry_id, task_name)
102
+ def entry_task_editor_line_edit(editor)
103
+ task_editor_line_edit_for(editor) || editor.instance_variable_get(:@qtimetrap_task_line_edit)
70
104
  end
71
105
 
72
- def handle_entry_task_focus_out(task_input)
73
- deactivate_entry_task_input(task_input)
106
+ def handle_entry_task_key_press(task_container, entry_id, event)
107
+ return unless enter_key?(event)
108
+
109
+ handle_entry_task_commit(task_container, entry_id)
74
110
  end
75
111
 
76
- def entry_task_line_edit(task_input)
77
- task_input.instance_variable_get(:@qtimetrap_task_line_edit)
112
+ def handle_entry_task_commit(task_container, entry_id, force: false)
113
+ editor = entry_task_editor(task_container)
114
+ editor_input = entry_task_editor_line_edit(editor)
115
+ return if editor_input.is_read_only && !force
116
+
117
+ task_name = editor.current_text.to_s
118
+ sync_entry_task_display(task_container, task_name)
119
+ deactivate_entry_task_input(task_container)
120
+ return unless on_entry_task_change
121
+
122
+ on_entry_task_change.call(entry_id, task_name)
78
123
  end
79
124
  end
80
125
  end
@@ -25,10 +25,10 @@ module QTimetrap
25
25
 
26
26
  def build_entry_time_group(row, start_input, end_input)
27
27
  QWidget.new(row).tap do |time_group|
28
- time_group.set_object_name('entry_node_entry_time_group')
28
+ time_group.object_name = 'entry_node_entry_time_group'
29
29
  QHBoxLayout.new(time_group).tap do |time_layout|
30
30
  time_layout.set_contents_margins(0, 0, 0, 0)
31
- time_layout.set_spacing(4)
31
+ time_layout.spacing = 4
32
32
  time_layout.add_widget(start_input)
33
33
  time_layout.add_widget(build_entry_time_separator(time_group))
34
34
  time_layout.add_widget(end_input)
@@ -38,19 +38,19 @@ module QTimetrap
38
38
 
39
39
  def build_entry_time_separator(row)
40
40
  QLabel.new(row).tap do |separator|
41
- separator.set_object_name('entry_node_entry_time_sep')
42
- separator.set_text('-')
41
+ separator.object_name = 'entry_node_entry_time_sep'
42
+ separator.text = '-'
43
43
  end
44
44
  end
45
45
 
46
46
  def build_entry_time_input(row, object_name:, value:, placeholder:)
47
47
  QLineEdit.new(row).tap do |time_input|
48
- time_input.set_object_name(object_name)
48
+ time_input.object_name = object_name
49
49
  time_input.text = value.to_s
50
- time_input.set_placeholder_text(placeholder)
51
- time_input.set_alignment(Qt::AlignCenter)
52
- time_input.set_read_only(true)
53
- time_input.set_fixed_width(58)
50
+ time_input.placeholder_text = placeholder
51
+ time_input.alignment = Qt::AlignCenter
52
+ time_input.read_only = true
53
+ time_input.fixed_width = 58
54
54
  end
55
55
  end
56
56
 
@@ -6,6 +6,7 @@ module QTimetrap
6
6
  class ListComponent
7
7
  include ListHostHelpers
8
8
  include ListStateHelpers
9
+ include ListTaskEditorHelpers
9
10
  include QtUiHelpers
10
11
  include TreeHelpers
11
12
  include RenderHelpers
@@ -73,7 +74,7 @@ module QTimetrap
73
74
 
74
75
  def build
75
76
  @widget = QWidget.new(parent)
76
- widget.set_object_name('entries_panel')
77
+ widget.object_name = 'entries_panel'
77
78
  panel_layout = build_panel_layout
78
79
  @time_filter_debounce = build_time_filter_debounce_timer
79
80
  panel_layout.add_widget(build_toolbar(parent_widget: widget))
@@ -91,22 +92,22 @@ module QTimetrap
91
92
 
92
93
  def adjust_node_widths
93
94
  width = branch_button_width
94
- branch_bindings.each_value { |binding| binding.fetch(:button).set_fixed_width(width) }
95
- leaf_labels.each { |label| label.set_fixed_width(width) }
96
- entry_rows.each { |row| row.set_fixed_width(width) }
95
+ branch_bindings.each_value { |binding| binding.fetch(:button).fixed_width = width }
96
+ leaf_labels.each { |label| label.fixed_width = width }
97
+ entry_rows.each { |row| row.fixed_width = width }
97
98
  end
98
99
 
99
100
  def build_panel_layout
100
101
  QVBoxLayout.new(widget).tap do |layout|
101
102
  layout.set_contents_margins(0, 0, 0, 0)
102
- layout.set_spacing(6)
103
+ layout.spacing = 6
103
104
  end
104
105
  end
105
106
 
106
107
  def build_scroll_area
107
108
  QScrollArea.new(widget).tap do |area|
108
- area.set_object_name('entries_scroll')
109
- area.set_widget_resizable(true)
109
+ area.object_name = 'entries_scroll'
110
+ area.widget_resizable = true
110
111
  end
111
112
  end
112
113
 
@@ -7,20 +7,20 @@ module QTimetrap
7
7
  private
8
8
 
9
9
  def build_host
10
- QWidget.new(scroll_area).tap { |container| container.set_object_name('entries_host') }
10
+ QWidget.new(scroll_area).tap { |container| container.object_name = 'entries_host' }
11
11
  end
12
12
 
13
13
  def build_host_layout
14
14
  QVBoxLayout.new(host).tap do |layout|
15
15
  layout.set_contents_margins(14, 10, 14, 10)
16
- layout.set_spacing(2)
16
+ layout.spacing = 2
17
17
  end
18
18
  end
19
19
 
20
20
  def rebuild_host!
21
21
  @host = build_host
22
22
  @host_layout = build_host_layout
23
- scroll_area.set_widget(host)
23
+ scroll_area.widget = host
24
24
  end
25
25
  end
26
26
  end
@@ -15,11 +15,20 @@ module QTimetrap
15
15
  @expanded = {}
16
16
  @current_nodes = []
17
17
  @branch_bindings = {}
18
+ initialize_task_editor_state!
19
+ @active_task_container_key = nil
18
20
  @leaf_labels = []
19
21
  @entry_rows = []
20
22
  @rendering = false
21
23
  end
22
24
 
25
+ def initialize_task_editor_state!
26
+ @task_containers = {}
27
+ @task_display_inputs = {}
28
+ @task_editors = {}
29
+ @task_editor_line_edits = {}
30
+ end
31
+
23
32
  def initialize_time_filter_state!
24
33
  @time_filter_from_toggle = nil
25
34
  @time_filter_to_toggle = nil
@@ -31,8 +40,8 @@ module QTimetrap
31
40
 
32
41
  def set_time_filter_state(toggle:, input:, value:)
33
42
  enabled = !value.nil?
34
- toggle.set_checked(enabled)
35
- input.set_date_time(value) if enabled
43
+ toggle.checked = enabled
44
+ input.date_time = value if enabled
36
45
  end
37
46
 
38
47
  def syncing_time_filters?
@@ -49,11 +58,58 @@ module QTimetrap
49
58
 
50
59
  def build_time_filter_debounce_timer
51
60
  QTimer.new(parent).tap do |timer|
52
- timer.set_single_shot(true)
53
- timer.set_interval(self.class::TIME_FILTER_DEBOUNCE_MS)
61
+ timer.single_shot = true
62
+ timer.interval = self.class::TIME_FILTER_DEBOUNCE_MS
54
63
  timer.connect('timeout') { |_| emit_time_range_filter_changed }
55
64
  end
56
65
  end
66
+
67
+ def register_task_widgets(task_container, display_input, editor, line_edit)
68
+ container_key = task_container_key(task_container)
69
+ task_containers[container_key] = task_container
70
+ task_display_inputs[container_key] = display_input
71
+ task_editors[container_key] = editor
72
+ task_editor_line_edits[task_editor_key(editor)] = line_edit
73
+ end
74
+
75
+ def task_container_key(task_container)
76
+ task_container.handle.address
77
+ end
78
+
79
+ def task_editor_key(editor)
80
+ editor.handle.address
81
+ end
82
+
83
+ def task_container_matches?(left, right)
84
+ task_container_key(left) == task_container_key(right)
85
+ end
86
+
87
+ def task_container_for_key(key)
88
+ task_containers[key]
89
+ end
90
+
91
+ def active_task_container
92
+ task_container_for_key(active_task_container_key)
93
+ end
94
+
95
+ def active_task_container=(task_container)
96
+ @active_task_container_key = task_container ? task_container_key(task_container) : nil
97
+ end
98
+
99
+ def task_display_input_for(task_container)
100
+ task_display_inputs[task_container_key(task_container)]
101
+ end
102
+
103
+ def task_editor_for(task_container)
104
+ task_editors[task_container_key(task_container)]
105
+ end
106
+
107
+ def task_editor_line_edit_for(editor)
108
+ task_editor_line_edits[task_editor_key(editor)]
109
+ end
110
+
111
+ attr_reader :task_containers, :task_display_inputs, :task_editors, :task_editor_line_edits,
112
+ :active_task_container_key
57
113
  end
58
114
  end
59
115
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QTimetrap
4
+ module Entries
5
+ # Public helpers for active task editor lifecycle.
6
+ module ListTaskEditorHelpers
7
+ def close_active_task_editor
8
+ return unless active_task_container
9
+
10
+ handle_entry_task_focus_out(active_task_container)
11
+ end
12
+
13
+ def task_editor_widget?(widget)
14
+ return false unless widget
15
+
16
+ task_editor_widget_names.include?(widget.object_name)
17
+ end
18
+
19
+ private
20
+
21
+ def task_editor_widget_names
22
+ %w[
23
+ entry_node_entry_task_container
24
+ entry_node_entry_task
25
+ entry_node_entry_task_editor
26
+ entry_node_entry_task_editor_input
27
+ ]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -7,10 +7,10 @@ module QTimetrap
7
7
  private
8
8
 
9
9
  def with_widget_updates_suspended
10
- widget.set_updates_enabled(false)
10
+ widget.updates_enabled = false
11
11
  yield
12
12
  ensure
13
- widget.set_updates_enabled(true)
13
+ widget.updates_enabled = true
14
14
  widget.update
15
15
  end
16
16