qtimetrap 0.1.1 → 0.1.3

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/README.md +3 -4
  4. data/app/components/entries/branch_hierarchy_helpers.rb +4 -4
  5. data/app/components/entries/entry_row_render_helpers.rb +196 -0
  6. data/app/components/entries/leaf_archive_helpers.rb +6 -6
  7. data/app/components/entries/leaf_note_helpers.rb +18 -12
  8. data/app/components/entries/leaf_task_display_helpers.rb +15 -0
  9. data/app/components/entries/leaf_task_editor_state_helpers.rb +68 -0
  10. data/app/components/entries/leaf_task_helpers.rb +130 -46
  11. data/app/components/entries/leaf_time_helpers.rb +9 -9
  12. data/app/components/entries/list_component.rb +302 -15
  13. data/app/components/entries/list_host_helpers.rb +6 -3
  14. data/app/components/entries/list_state_helpers.rb +77 -4
  15. data/app/components/entries/list_task_editor_helpers.rb +31 -0
  16. data/app/components/entries/render_helpers.rb +2 -2
  17. data/app/components/entries/tree_helpers.rb +9 -7
  18. data/app/components/entries/tree_toolbar_helpers.rb +12 -12
  19. data/app/components/project_sidebar/archive_toggle_helpers.rb +6 -6
  20. data/app/components/project_sidebar/component.rb +18 -7
  21. data/app/components/project_sidebar/logo_helpers.rb +14 -14
  22. data/app/components/project_sidebar/project_button_helpers.rb +7 -7
  23. data/app/components/project_sidebar/task_helpers.rb +11 -11
  24. data/app/components/qt_ui_helpers.rb +9 -9
  25. data/app/components/tracker_controls/component.rb +19 -8
  26. data/app/components/tracker_controls/layout_builder.rb +35 -15
  27. data/app/components/tracker_controls/layout_helpers.rb +7 -6
  28. data/app/services/archived_entries_store.rb +11 -6
  29. data/app/styles/themes/dark/entries_list.qss +12 -4
  30. data/app/styles/themes/light/entries_list.qss +12 -4
  31. data/app/view_models/main_view_model.rb +4 -0
  32. data/app/views/main_window.rb +2 -3
  33. data/app/views/main_window_layout_builder.rb +6 -6
  34. data/app/views/main_window_runtime.rb +14 -8
  35. data/app/views/main_window_runtime_archive_helpers.rb +7 -1
  36. data/app/views/main_window_runtime_key_helpers.rb +24 -5
  37. data/app/views/main_window_runtime_render_helpers.rb +2 -1
  38. data/app/views/main_window_splitter_toggle_bootstrap_helpers.rb +1 -1
  39. data/app/views/main_window_splitter_toggle_helpers.rb +10 -10
  40. data/app/views/main_window_splitter_toggle_hover_helpers.rb +2 -2
  41. data/app/views/main_window_ui_helpers.rb +31 -3
  42. data/app/views/window_icon_loader.rb +1 -1
  43. data/lib/qtimetrap/version.rb +1 -1
  44. data/lib/rubocop/cop/qt/prefer_ruby_style_setter.rb +50 -0
  45. metadata +11 -5
@@ -15,11 +15,22 @@ 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 = []
22
+ @theme_style_queue = []
20
23
  @rendering = false
21
24
  end
22
25
 
26
+ def initialize_task_editor_state!
27
+ @task_containers = {}
28
+ @task_display_inputs = {}
29
+ @task_editors = {}
30
+ @task_editor_line_edits = {}
31
+ @task_editor_configs = {}
32
+ end
33
+
23
34
  def initialize_time_filter_state!
24
35
  @time_filter_from_toggle = nil
25
36
  @time_filter_to_toggle = nil
@@ -31,8 +42,8 @@ module QTimetrap
31
42
 
32
43
  def set_time_filter_state(toggle:, input:, value:)
33
44
  enabled = !value.nil?
34
- toggle.set_checked(enabled)
35
- input.set_date_time(value) if enabled
45
+ toggle.checked = enabled
46
+ input.date_time = value if enabled
36
47
  end
37
48
 
38
49
  def syncing_time_filters?
@@ -49,11 +60,73 @@ module QTimetrap
49
60
 
50
61
  def build_time_filter_debounce_timer
51
62
  QTimer.new(parent).tap do |timer|
52
- timer.set_single_shot(true)
53
- timer.set_interval(self.class::TIME_FILTER_DEBOUNCE_MS)
63
+ timer.single_shot = true
64
+ timer.interval = self.class::TIME_FILTER_DEBOUNCE_MS
54
65
  timer.connect('timeout') { |_| emit_time_range_filter_changed }
55
66
  end
56
67
  end
68
+
69
+ def register_task_widgets(task_container, display_input, editor, line_edit)
70
+ container_key = task_container_key(task_container)
71
+ task_containers[container_key] = task_container
72
+ task_display_inputs[container_key] = display_input
73
+ register_task_editor_widgets(task_container, editor, line_edit) if editor
74
+ end
75
+
76
+ def register_task_editor_config(task_container, node:, task_name:)
77
+ task_editor_configs[task_container_key(task_container)] = { node: node, task_name: task_name }
78
+ end
79
+
80
+ def register_task_editor_widgets(task_container, editor, line_edit)
81
+ container_key = task_container_key(task_container)
82
+ task_editors[container_key] = editor
83
+ task_editor_line_edits[task_editor_key(editor)] = line_edit
84
+ end
85
+
86
+ def task_editor_config_for(task_container)
87
+ task_editor_configs[task_container_key(task_container)]
88
+ end
89
+
90
+ def task_container_key(task_container)
91
+ task_container.handle.address
92
+ end
93
+
94
+ def task_editor_key(editor)
95
+ editor.handle.address
96
+ end
97
+
98
+ def task_container_matches?(left, right)
99
+ task_container_key(left) == task_container_key(right)
100
+ end
101
+
102
+ def task_container_for_key(key)
103
+ task_containers[key]
104
+ end
105
+
106
+ def active_task_container
107
+ task_container_for_key(active_task_container_key)
108
+ end
109
+
110
+ def active_task_container=(task_container)
111
+ @active_task_container_key = task_container ? task_container_key(task_container) : nil
112
+ end
113
+
114
+ def task_display_input_for(task_container)
115
+ task_display_inputs[task_container_key(task_container)]
116
+ end
117
+
118
+ def task_editor_for(task_container)
119
+ task_editors[task_container_key(task_container)]
120
+ end
121
+
122
+ def task_editor_line_edit_for(editor)
123
+ return unless editor
124
+
125
+ task_editor_line_edits[task_editor_key(editor)]
126
+ end
127
+
128
+ attr_reader :task_containers, :task_display_inputs, :task_editors, :task_editor_line_edits,
129
+ :active_task_container_key, :task_editor_configs
57
130
  end
58
131
  end
59
132
  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
 
@@ -7,6 +7,8 @@ module QTimetrap
7
7
  include BranchHierarchyHelpers
8
8
  include LeafArchiveHelpers
9
9
  include LeafNoteHelpers
10
+ include LeafTaskDisplayHelpers
11
+ include LeafTaskEditorStateHelpers
10
12
  include LeafTaskHelpers
11
13
  include LeafTimeHelpers
12
14
  include NodePresentationHelpers
@@ -19,8 +21,8 @@ module QTimetrap
19
21
  end
20
22
 
21
23
  def set_initial_filter_ui_state
22
- time_filter_from_toggle.set_checked(false)
23
- time_filter_to_toggle.set_checked(false)
24
+ time_filter_from_toggle.checked = false
25
+ time_filter_to_toggle.checked = false
24
26
  end
25
27
 
26
28
  def emit_time_range_filter_changed
@@ -103,10 +105,10 @@ module QTimetrap
103
105
 
104
106
  def render_default_leaf_node(node, level, parent_widget:, layout:)
105
107
  label = QLabel.new(parent_widget)
106
- label.set_object_name(object_name_for(node))
107
- label.set_text("#{indent(level)}#{node.fetch(:label)}")
108
- label.set_fixed_width(branch_button_width)
109
- label.set_fixed_height(32)
108
+ label.object_name = object_name_for(node)
109
+ label.text = "#{indent(level)}#{node.fetch(:label)}"
110
+ label.fixed_width = branch_button_width
111
+ label.fixed_height = 32
110
112
  leaf_labels << label
111
113
  layout.add_widget(label)
112
114
  end
@@ -123,7 +125,7 @@ module QTimetrap
123
125
  def build_branch_button(node, level, expanded_state, parent_widget:)
124
126
  text = branch_button_text(level, node.fetch(:label), expanded_state)
125
127
  button = build_button(parent_widget, object_name_for(node), text, 0, 32)
126
- button.set_fixed_width(branch_button_width)
128
+ button.fixed_width = branch_button_width
127
129
  node_id = node.fetch(:id)
128
130
  button.connect('clicked') { |_| toggle_node(node_id) }
129
131
  button
@@ -8,7 +8,7 @@ module QTimetrap
8
8
 
9
9
  def build_toolbar(parent_widget:)
10
10
  toolbar = QWidget.new(parent_widget)
11
- toolbar.set_object_name('entries_toolbar')
11
+ toolbar.object_name = 'entries_toolbar'
12
12
  layout = build_toolbar_layout(toolbar)
13
13
  add_tree_toolbar_buttons(layout, toolbar)
14
14
  add_time_filter_controls(layout, toolbar)
@@ -20,7 +20,7 @@ module QTimetrap
20
20
  def build_toolbar_layout(toolbar)
21
21
  QHBoxLayout.new(toolbar).tap do |layout|
22
22
  layout.set_contents_margins(0, 0, 0, 0)
23
- layout.set_spacing(8)
23
+ layout.spacing = 8
24
24
  end
25
25
  end
26
26
 
@@ -74,22 +74,22 @@ module QTimetrap
74
74
 
75
75
  def build_filter_toggle(parent_widget, name, text)
76
76
  QCheckBox.new(parent_widget).tap do |checkbox|
77
- checkbox.set_object_name(name)
78
- checkbox.set_text(text)
79
- checkbox.set_focus_policy(Qt::NoFocus)
80
- checkbox.set_fixed_height(28)
77
+ checkbox.object_name = name
78
+ checkbox.text = text
79
+ checkbox.focus_policy = Qt::NoFocus
80
+ checkbox.fixed_height = 28
81
81
  checkbox.connect('clicked') { |_| on_filter_toggle_changed }
82
82
  end
83
83
  end
84
84
 
85
85
  def build_filter_input(parent_widget, name)
86
86
  QDateTimeEdit.new(parent_widget).tap do |input|
87
- input.set_object_name(name)
88
- input.set_focus_policy(Qt::ClickFocus)
89
- input.set_fixed_width(172)
90
- input.set_calendar_popup(true)
91
- input.set_display_format('yyyy-MM-dd HH:mm')
92
- input.set_date_time(Time.now)
87
+ input.object_name = name
88
+ input.focus_policy = Qt::ClickFocus
89
+ input.fixed_width = 172
90
+ input.calendar_popup = true
91
+ input.display_format = 'yyyy-MM-dd HH:mm'
92
+ input.date_time = Time.now
93
93
  input.connect('dateTimeChanged(QDateTime)') { |_| schedule_time_range_filter_changed }
94
94
  end
95
95
  end
@@ -8,12 +8,12 @@ module QTimetrap
8
8
 
9
9
  def build_archive_toggle_button
10
10
  QPushButton.new(widget).tap do |button|
11
- button.set_object_name('sidebar_archive_toggle')
12
- button.set_checkable(true)
13
- button.set_focus_policy(Qt::NoFocus)
14
- button.set_fixed_height(30)
15
- button.set_text("\u{1F5C3}")
16
- button.set_tool_tip('Show archived entries only')
11
+ button.object_name = 'sidebar_archive_toggle'
12
+ button.checkable = true
13
+ button.focus_policy = Qt::NoFocus
14
+ button.fixed_height = 30
15
+ button.text = "\u{1F5C3}"
16
+ button.tool_tip = 'Show archived entries only'
17
17
  button.connect('clicked') { |_| on_archive_mode_toggled&.call(button.is_checked) }
18
18
  end
19
19
  end
@@ -31,7 +31,18 @@ module QTimetrap
31
31
  selection = selection_state(selection)
32
32
  render_projects(projects: values, selection: selection)
33
33
  render_tasks(tasks: tasks, selected_project: selection.fetch(:selected_project), selected_task: selected_task)
34
- archive_toggle_button.set_checked(archive_mode)
34
+ archive_toggle_button.checked = archive_mode
35
+ end
36
+
37
+ def update_tasks(tasks:, selected_project:, selected_task:)
38
+ render_tasks(tasks: tasks, selected_project: selected_project, selected_task: selected_task)
39
+ end
40
+
41
+ def theme_style_targets(stylesheet)
42
+ widgets = [@logo_text, @heading, tasks_heading, archive_toggle_button]
43
+ widgets.concat(buttons.map { |slot| slot.fetch(:view) })
44
+ widgets.concat(task_buttons)
45
+ widgets.compact.map { |target| [target, stylesheet] }
35
46
  end
36
47
 
37
48
  private
@@ -41,7 +52,7 @@ module QTimetrap
41
52
 
42
53
  def build
43
54
  @widget = QWidget.new(parent)
44
- widget.set_object_name('sidebar_panel')
55
+ widget.object_name = 'sidebar_panel'
45
56
  layout = build_root_layout
46
57
  add_static_sidebar_sections(layout)
47
58
  @buttons_layout = build_buttons_layout
@@ -56,9 +67,9 @@ module QTimetrap
56
67
  slot[:project] = project
57
68
  view = slot[:view]
58
69
 
59
- view.set_text(project[0, 24])
60
- view.set_disabled(false)
61
- view.set_checked(selected_project_indices.include?(index))
70
+ view.text = project[0, 24]
71
+ view.disabled = false
72
+ view.checked = selected_project_indices.include?(index)
62
73
  view.show
63
74
  end
64
75
 
@@ -78,7 +89,7 @@ module QTimetrap
78
89
  def build_root_layout
79
90
  QVBoxLayout.new(widget).tap do |layout|
80
91
  layout.set_contents_margins(12, 12, 12, 12)
81
- layout.set_spacing(8)
92
+ layout.spacing = 8
82
93
  end
83
94
  end
84
95
 
@@ -117,7 +128,7 @@ module QTimetrap
117
128
  def build_buttons_layout
118
129
  QVBoxLayout.new.tap do |layout|
119
130
  layout.set_contents_margins(0, 0, 0, 0)
120
- layout.set_spacing(8)
131
+ layout.spacing = 8
121
132
  end
122
133
  end
123
134
  end
@@ -8,10 +8,10 @@ module QTimetrap
8
8
 
9
9
  def build_logo
10
10
  QWidget.new(widget).tap do |container|
11
- container.set_object_name('sidebar_logo')
11
+ container.object_name = 'sidebar_logo'
12
12
  layout = QHBoxLayout.new(container)
13
13
  layout.set_contents_margins(0, 0, 0, 0)
14
- layout.set_spacing(8)
14
+ layout.spacing = 8
15
15
  layout.add_stretch(1)
16
16
  layout.add_widget(build_logo_icon(container))
17
17
  layout.add_widget(build_logo_text(container))
@@ -21,31 +21,31 @@ module QTimetrap
21
21
 
22
22
  def build_logo_icon(parent_widget)
23
23
  QLabel.new(parent_widget).tap do |label|
24
- label.set_object_name('sidebar_logo_icon')
25
- label.set_alignment(Qt::AlignCenter)
24
+ label.object_name = 'sidebar_logo_icon'
25
+ label.alignment = Qt::AlignCenter
26
26
  label.set_fixed_size(66, 66)
27
27
  icon_path = File.join(Application.root, 'app', 'assets', 'icons', 'qtimetrap-icon-128.png')
28
- label.set_text("<img src='#{icon_path}' width='64' height='64'/>")
28
+ label.text = "<img src='#{icon_path}' width='64' height='64'/>"
29
29
  end
30
30
  end
31
31
 
32
32
  def build_logo_text(parent_widget)
33
- QLabel.new(parent_widget).tap do |label|
34
- label.set_object_name('sidebar_logo_text')
35
- label.set_alignment(Qt::AlignCenter)
36
- label.set_text('QTimetrap')
33
+ @logo_text = QLabel.new(parent_widget).tap do |label|
34
+ label.object_name = 'sidebar_logo_text'
35
+ label.alignment = Qt::AlignCenter
36
+ label.text = 'QTimetrap'
37
37
  end
38
38
  end
39
39
 
40
40
  def build_logo_spacer
41
- QWidget.new(widget).tap { |spacer| spacer.set_fixed_height(6) }
41
+ QWidget.new(widget).tap { |spacer| spacer.fixed_height = 6 }
42
42
  end
43
43
 
44
44
  def build_heading
45
- QLabel.new(widget).tap do |label|
46
- label.set_object_name('sidebar_heading')
47
- label.set_alignment(Qt::AlignCenter)
48
- label.set_text('PROJECTS')
45
+ @heading = QLabel.new(widget).tap do |label|
46
+ label.object_name = 'sidebar_heading'
47
+ label.alignment = Qt::AlignCenter
48
+ label.text = 'PROJECTS'
49
49
  end
50
50
  end
51
51
  end
@@ -29,18 +29,18 @@ module QTimetrap
29
29
  slot[:project] = project
30
30
  view = slot[:view]
31
31
 
32
- view.set_text(project[0, 24])
33
- view.set_disabled(false)
34
- view.set_checked(selected_project_indices.include?(index))
32
+ view.text = project[0, 24]
33
+ view.disabled = false
34
+ view.checked = selected_project_indices.include?(index)
35
35
  view.show
36
36
  end
37
37
 
38
38
  def build_project_button
39
39
  QPushButton.new(widget).tap do |button|
40
- button.set_object_name('project_button')
41
- button.set_checkable(true)
42
- button.set_focus_policy(Qt::NoFocus)
43
- button.set_fixed_height(30)
40
+ button.object_name = 'project_button'
41
+ button.checkable = true
42
+ button.focus_policy = Qt::NoFocus
43
+ button.fixed_height = 30
44
44
  button.connect('clicked') { |_| on_button_clicked(button) }
45
45
  end
46
46
  end
@@ -11,7 +11,7 @@ module QTimetrap
11
11
  def render_tasks(tasks:, selected_project:, selected_task:)
12
12
  values = Array(tasks)
13
13
  visible = tasks_visible?(selected_project, values)
14
- tasks_heading.set_visible(visible)
14
+ tasks_heading.visible = visible
15
15
  sync_task_buttons(visible ? values.size : 0)
16
16
  return clear_task_state unless visible
17
17
 
@@ -34,18 +34,18 @@ module QTimetrap
34
34
 
35
35
  def build_tasks_heading
36
36
  QLabel.new(widget).tap do |label|
37
- label.set_object_name('sidebar_tasks_heading')
38
- label.set_alignment(Qt::AlignCenter)
39
- label.set_text('TASKS')
37
+ label.object_name = 'sidebar_tasks_heading'
38
+ label.alignment = Qt::AlignCenter
39
+ label.text = 'TASKS'
40
40
  end
41
41
  end
42
42
 
43
43
  def build_task_button
44
44
  QPushButton.new(widget).tap do |button|
45
- button.set_object_name('task_button')
46
- button.set_checkable(true)
47
- button.set_focus_policy(Qt::NoFocus)
48
- button.set_fixed_height(28)
45
+ button.object_name = 'task_button'
46
+ button.checkable = true
47
+ button.focus_policy = Qt::NoFocus
48
+ button.fixed_height = 28
49
49
  button.connect('clicked') { |_| on_task_button_clicked(button) }
50
50
  end
51
51
  end
@@ -78,9 +78,9 @@ module QTimetrap
78
78
  view = slot.fetch(:view)
79
79
  slot[:task] = task
80
80
  text = task.to_s
81
- view.set_text(text)
82
- view.set_tool_tip(text)
83
- view.set_checked(selected_task_indices.include?(index))
81
+ view.text = text
82
+ view.tool_tip = text
83
+ view.checked = selected_task_indices.include?(index)
84
84
  view.show
85
85
  end
86
86
 
@@ -7,20 +7,20 @@ module QTimetrap
7
7
 
8
8
  def build_label(parent_widget, object_name, text, width: nil, height: nil)
9
9
  QLabel.new(parent_widget).tap do |label|
10
- label.set_object_name(object_name)
11
- label.set_text(text) if text
12
- label.set_fixed_width(width) if width
13
- label.set_fixed_height(height) if height
10
+ label.object_name = object_name
11
+ label.text = text if text
12
+ label.fixed_width = width if width
13
+ label.fixed_height = height if height
14
14
  end
15
15
  end
16
16
 
17
17
  def build_button(parent_widget, name, text, width, height)
18
18
  QPushButton.new(parent_widget).tap do |button|
19
- button.set_object_name(name)
20
- button.set_text(text)
21
- button.set_focus_policy(Qt::NoFocus)
22
- button.set_fixed_width(width) if width&.positive?
23
- button.set_fixed_height(height)
19
+ button.object_name = name
20
+ button.text = text
21
+ button.focus_policy = Qt::NoFocus
22
+ button.fixed_width = width if width&.positive?
23
+ button.fixed_height = height
24
24
  end
25
25
  end
26
26
  end
@@ -17,12 +17,12 @@ module QTimetrap
17
17
  end
18
18
 
19
19
  def update_summary(text)
20
- summary_label.set_text(text)
20
+ summary_label.text = text
21
21
  end
22
22
 
23
23
  def update_project_input(project_name)
24
24
  value = project_name.to_s.strip
25
- project_input.set_text(value)
25
+ project_input.text = value
26
26
  end
27
27
 
28
28
  def update_task_input(text)
@@ -30,14 +30,18 @@ module QTimetrap
30
30
  end
31
31
 
32
32
  def update_theme_label(theme_name)
33
- theme_button.set_text("THEME: #{theme_name.upcase}")
33
+ theme_button.text = "THEME: #{theme_name.upcase}"
34
34
  end
35
35
 
36
36
  def update_action_button(running:)
37
- start_button.set_visible(!running)
38
- stop_button.set_visible(running)
39
- task_input.set_read_only(running)
40
- project_input.set_read_only(running)
37
+ start_button.visible = !running
38
+ stop_button.visible = running
39
+ task_input.read_only = running
40
+ project_input.read_only = running
41
+ end
42
+
43
+ def theme_style_targets(stylesheet)
44
+ theme_widgets.compact.map { |target| [target, stylesheet] }
41
45
  end
42
46
 
43
47
  private
@@ -49,7 +53,7 @@ module QTimetrap
49
53
  available = [timer_label.width - TIMER_HORIZONTAL_PADDING, 24].max
50
54
  estimated = (available / (chars * TIMER_CHAR_WIDTH_FACTOR)).floor
51
55
  font_size = estimated.clamp(TIMER_FONT_MIN_PT, TIMER_FONT_MAX_PT)
52
- timer_label.set_style_sheet("font-size: #{font_size}pt;")
56
+ timer_label.style_sheet = "font-size: #{font_size}pt;"
53
57
  end
54
58
 
55
59
  def build_ui(parent:, callbacks:)
@@ -69,9 +73,16 @@ module QTimetrap
69
73
  @theme_button = ui_map.fetch(:theme_button)
70
74
  @start_button = ui_map.fetch(:start_button)
71
75
  @stop_button = ui_map.fetch(:stop_button)
76
+ @theme_widgets = [
77
+ ui_map.fetch(:topbar), ui_map.fetch(:tracker_row), ui_map.fetch(:actions_row),
78
+ ui_map.fetch(:title_label), clock_label, timer_label, task_input, project_input,
79
+ summary_label, theme_button, start_button, stop_button, ui_map.fetch(:refresh_button)
80
+ ]
72
81
  configure_timer_font_fit!
73
82
  end
74
83
 
84
+ attr_reader :theme_widgets
85
+
75
86
  def configure_timer_font_fit!
76
87
  apply_timer_font_fit(TIMER_FIT_SAMPLE)
77
88
  end
@@ -15,11 +15,13 @@ module QTimetrap
15
15
  def build
16
16
  @widget = QWidget.new(parent)
17
17
  root = build_root_layout
18
- root.add_widget(build_topbar)
18
+ @topbar = build_topbar
19
+ root.add_widget(@topbar)
19
20
  start_button, stop_button = build_tracker_row(root)
20
21
  refresh_button = build_actions_row(root)
22
+ @refresh_button = refresh_button
21
23
  connect_actions(start_button, stop_button, refresh_button)
22
- ui_payload
24
+ ui_payload.merge(theme_ui_payload)
23
25
  end
24
26
 
25
27
  private
@@ -28,28 +30,28 @@ module QTimetrap
28
30
 
29
31
  def build_topbar
30
32
  topbar = QWidget.new(widget)
31
- topbar.set_object_name('topbar')
33
+ topbar.object_name = 'topbar'
32
34
  layout = QHBoxLayout.new(topbar)
33
35
  configure_topbar_layout(layout, topbar)
34
36
  topbar
35
37
  end
36
38
 
37
39
  def build_tracker_row(root_layout)
38
- row = QWidget.new(widget)
39
- row.set_object_name('tracker_row')
40
+ row = @tracker_row = QWidget.new(widget)
41
+ row.object_name = 'tracker_row'
40
42
  layout = QHBoxLayout.new(row)
41
43
  layout.set_contents_margins(14, 12, 14, 12)
42
- layout.set_spacing(8)
44
+ layout.spacing = 8
43
45
  start_button, stop_button = add_tracker_row_widgets(layout, row)
44
46
  root_layout.add_widget(row)
45
47
  [start_button, stop_button]
46
48
  end
47
49
 
48
50
  def build_actions_row(root_layout)
49
- row = QWidget.new(widget)
51
+ row = @actions_row = QWidget.new(widget)
50
52
  layout = QHBoxLayout.new(row)
51
53
  layout.set_contents_margins(0, 0, 0, 0)
52
- layout.set_spacing(8)
54
+ layout.spacing = 8
53
55
  refresh_button = add_actions_row_widgets(layout, row)
54
56
  root_layout.add_widget(row)
55
57
  refresh_button
@@ -57,20 +59,22 @@ module QTimetrap
57
59
 
58
60
  def build_task_input(parent_widget)
59
61
  QLineEdit.new(parent_widget).tap do |input|
60
- input.set_object_name('task_input')
61
- input.set_placeholder_text('What are you working on?')
62
- input.set_focus_policy(Qt::ClickFocus)
62
+ input.object_name = 'task_input'
63
+ input.placeholder_text = 'What are you working on?'
64
+ input.focus_policy = Qt::ClickFocus
63
65
  input.text = ''
66
+ bind_input_focus_on_click(input)
64
67
  end
65
68
  end
66
69
 
67
70
  def build_project_input(parent_widget)
68
71
  QLineEdit.new(parent_widget).tap do |input|
69
- input.set_object_name('project_input')
70
- input.set_placeholder_text('your project')
71
- input.set_focus_policy(Qt::ClickFocus)
72
+ input.object_name = 'project_input'
73
+ input.placeholder_text = 'your project'
74
+ input.focus_policy = Qt::ClickFocus
72
75
  input.text = ''
73
- input.set_fixed_width(190)
76
+ input.fixed_width = 190
77
+ bind_input_focus_on_click(input)
74
78
  end
75
79
  end
76
80
 
@@ -80,6 +84,16 @@ module QTimetrap
80
84
  connect_project_input
81
85
  end
82
86
 
87
+ def theme_ui_payload
88
+ {
89
+ topbar: @topbar,
90
+ tracker_row: @tracker_row,
91
+ actions_row: @actions_row,
92
+ title_label: @title_label,
93
+ refresh_button: @refresh_button
94
+ }
95
+ end
96
+
83
97
  def connect_start_stop(start_button, stop_button)
84
98
  start_button.connect('clicked') do |_|
85
99
  callbacks.fetch(:on_start).call(@task_input.text.to_s, @project_input.text.to_s)
@@ -95,6 +109,12 @@ module QTimetrap
95
109
  def connect_project_input
96
110
  @project_input.connect('textChanged(QString)') { |text| callbacks.fetch(:on_project_change).call(text.to_s) }
97
111
  end
112
+
113
+ def bind_input_focus_on_click(input)
114
+ input.on(:mouse_button_press) do |_|
115
+ input.set_focus unless input.is_read_only
116
+ end
117
+ end
98
118
  end
99
119
  end
100
120
  end