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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/app/components/entries/branch_hierarchy_helpers.rb +4 -4
- data/app/components/entries/leaf_archive_helpers.rb +6 -6
- data/app/components/entries/leaf_note_helpers.rb +11 -11
- data/app/components/entries/leaf_task_display_helpers.rb +20 -0
- data/app/components/entries/leaf_task_editor_state_helpers.rb +69 -0
- data/app/components/entries/leaf_task_helpers.rb +91 -46
- data/app/components/entries/leaf_time_helpers.rb +9 -9
- data/app/components/entries/list_component.rb +8 -7
- data/app/components/entries/list_host_helpers.rb +3 -3
- data/app/components/entries/list_state_helpers.rb +60 -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 +7 -7
- data/app/components/project_sidebar/logo_helpers.rb +12 -12
- 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 +8 -8
- data/app/components/tracker_controls/layout_builder.rb +19 -11
- data/app/components/tracker_controls/layout_helpers.rb +5 -5
- data/app/styles/themes/dark/entries_list.qss +23 -3
- data/app/styles/themes/light/entries_list.qss +23 -3
- 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 +2 -2
- data/app/views/main_window_runtime_key_helpers.rb +24 -5
- 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 +3 -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 +9 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5287d989740a397f7ed84b151eb5d3d48ab8edd480bb580517cfdedc8e20bb54
|
|
4
|
+
data.tar.gz: 6234dfac9679d2b577999f83b561226d03bc70201743f8df8c4668d0e8c5fe57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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).
|
|
35
|
+
binding.fetch(:children_container).visible = visible
|
|
36
36
|
text = branch_button_text(binding.fetch(:level), binding.fetch(:label), visible)
|
|
37
|
-
binding.fetch(:button).
|
|
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.
|
|
12
|
-
button.
|
|
13
|
-
button.
|
|
14
|
-
button.
|
|
15
|
-
button.
|
|
16
|
-
button.
|
|
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.
|
|
35
|
-
row.
|
|
36
|
-
row.
|
|
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.
|
|
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.
|
|
50
|
-
prefix_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.
|
|
56
|
+
note_input.object_name = 'entry_node_entry_note'
|
|
57
57
|
note_input.text = node.fetch(:note, '')
|
|
58
|
-
note_input.
|
|
59
|
-
note_input.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
57
|
-
|
|
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
|
-
|
|
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
|
|
63
|
-
|
|
94
|
+
def entry_task_display_input(task_container)
|
|
95
|
+
task_display_input_for(task_container)
|
|
96
|
+
end
|
|
64
97
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
98
|
+
def entry_task_editor(task_container)
|
|
99
|
+
task_editor_for(task_container)
|
|
100
|
+
end
|
|
68
101
|
|
|
69
|
-
|
|
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
|
|
73
|
-
|
|
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
|
|
77
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
42
|
-
separator.
|
|
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.
|
|
48
|
+
time_input.object_name = object_name
|
|
49
49
|
time_input.text = value.to_s
|
|
50
|
-
time_input.
|
|
51
|
-
time_input.
|
|
52
|
-
time_input.
|
|
53
|
-
time_input.
|
|
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.
|
|
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).
|
|
95
|
-
leaf_labels.each { |label| label.
|
|
96
|
-
entry_rows.each { |row| row.
|
|
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.
|
|
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.
|
|
109
|
-
area.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
35
|
-
input.
|
|
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.
|
|
53
|
-
timer.
|
|
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
|