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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/README.md +3 -4
- data/app/components/entries/branch_hierarchy_helpers.rb +4 -4
- data/app/components/entries/entry_row_render_helpers.rb +196 -0
- data/app/components/entries/leaf_archive_helpers.rb +6 -6
- data/app/components/entries/leaf_note_helpers.rb +18 -12
- data/app/components/entries/leaf_task_display_helpers.rb +15 -0
- data/app/components/entries/leaf_task_editor_state_helpers.rb +68 -0
- data/app/components/entries/leaf_task_helpers.rb +130 -46
- data/app/components/entries/leaf_time_helpers.rb +9 -9
- data/app/components/entries/list_component.rb +302 -15
- data/app/components/entries/list_host_helpers.rb +6 -3
- data/app/components/entries/list_state_helpers.rb +77 -4
- data/app/components/entries/list_task_editor_helpers.rb +31 -0
- data/app/components/entries/render_helpers.rb +2 -2
- data/app/components/entries/tree_helpers.rb +9 -7
- data/app/components/entries/tree_toolbar_helpers.rb +12 -12
- data/app/components/project_sidebar/archive_toggle_helpers.rb +6 -6
- data/app/components/project_sidebar/component.rb +18 -7
- data/app/components/project_sidebar/logo_helpers.rb +14 -14
- data/app/components/project_sidebar/project_button_helpers.rb +7 -7
- data/app/components/project_sidebar/task_helpers.rb +11 -11
- data/app/components/qt_ui_helpers.rb +9 -9
- data/app/components/tracker_controls/component.rb +19 -8
- data/app/components/tracker_controls/layout_builder.rb +35 -15
- data/app/components/tracker_controls/layout_helpers.rb +7 -6
- data/app/services/archived_entries_store.rb +11 -6
- data/app/styles/themes/dark/entries_list.qss +12 -4
- data/app/styles/themes/light/entries_list.qss +12 -4
- data/app/view_models/main_view_model.rb +4 -0
- data/app/views/main_window.rb +2 -3
- data/app/views/main_window_layout_builder.rb +6 -6
- data/app/views/main_window_runtime.rb +14 -8
- data/app/views/main_window_runtime_archive_helpers.rb +7 -1
- data/app/views/main_window_runtime_key_helpers.rb +24 -5
- data/app/views/main_window_runtime_render_helpers.rb +2 -1
- data/app/views/main_window_splitter_toggle_bootstrap_helpers.rb +1 -1
- data/app/views/main_window_splitter_toggle_helpers.rb +10 -10
- data/app/views/main_window_splitter_toggle_hover_helpers.rb +2 -2
- data/app/views/main_window_ui_helpers.rb +31 -3
- data/app/views/window_icon_loader.rb +1 -1
- data/lib/qtimetrap/version.rb +1 -1
- data/lib/rubocop/cop/qt/prefer_ruby_style_setter.rb +50 -0
- 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.
|
|
35
|
-
input.
|
|
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.
|
|
53
|
-
timer.
|
|
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,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.
|
|
23
|
-
time_filter_to_toggle.
|
|
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.
|
|
107
|
-
label.
|
|
108
|
-
label.
|
|
109
|
-
label.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
78
|
-
checkbox.
|
|
79
|
-
checkbox.
|
|
80
|
-
checkbox.
|
|
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.
|
|
88
|
-
input.
|
|
89
|
-
input.
|
|
90
|
-
input.
|
|
91
|
-
input.
|
|
92
|
-
input.
|
|
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.
|
|
12
|
-
button.
|
|
13
|
-
button.
|
|
14
|
-
button.
|
|
15
|
-
button.
|
|
16
|
-
button.
|
|
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.
|
|
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.
|
|
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.
|
|
60
|
-
view.
|
|
61
|
-
view.
|
|
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.
|
|
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.
|
|
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.
|
|
11
|
+
container.object_name = 'sidebar_logo'
|
|
12
12
|
layout = QHBoxLayout.new(container)
|
|
13
13
|
layout.set_contents_margins(0, 0, 0, 0)
|
|
14
|
-
layout.
|
|
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.
|
|
25
|
-
label.
|
|
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.
|
|
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.
|
|
35
|
-
label.
|
|
36
|
-
label.
|
|
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.
|
|
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.
|
|
47
|
-
label.
|
|
48
|
-
label.
|
|
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.
|
|
33
|
-
view.
|
|
34
|
-
view.
|
|
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.
|
|
41
|
-
button.
|
|
42
|
-
button.
|
|
43
|
-
button.
|
|
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.
|
|
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.
|
|
38
|
-
label.
|
|
39
|
-
label.
|
|
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.
|
|
46
|
-
button.
|
|
47
|
-
button.
|
|
48
|
-
button.
|
|
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.
|
|
82
|
-
view.
|
|
83
|
-
view.
|
|
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.
|
|
11
|
-
label.
|
|
12
|
-
label.
|
|
13
|
-
label.
|
|
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.
|
|
20
|
-
button.
|
|
21
|
-
button.
|
|
22
|
-
button.
|
|
23
|
-
button.
|
|
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.
|
|
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.
|
|
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.
|
|
33
|
+
theme_button.text = "THEME: #{theme_name.upcase}"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def update_action_button(running:)
|
|
37
|
-
start_button.
|
|
38
|
-
stop_button.
|
|
39
|
-
task_input.
|
|
40
|
-
project_input.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
61
|
-
input.
|
|
62
|
-
input.
|
|
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.
|
|
70
|
-
input.
|
|
71
|
-
input.
|
|
72
|
+
input.object_name = 'project_input'
|
|
73
|
+
input.placeholder_text = 'your project'
|
|
74
|
+
input.focus_policy = Qt::ClickFocus
|
|
72
75
|
input.text = ''
|
|
73
|
-
input.
|
|
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
|