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
|
@@ -19,17 +19,18 @@ module QTimetrap
|
|
|
19
19
|
def build_root_layout
|
|
20
20
|
QVBoxLayout.new(widget).tap do |layout|
|
|
21
21
|
layout.set_contents_margins(0, 8, 0, 0)
|
|
22
|
-
layout.
|
|
22
|
+
layout.spacing = 14
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def configure_topbar_layout(layout, topbar)
|
|
27
27
|
layout.set_contents_margins(16, 8, 16, 8)
|
|
28
|
-
layout.
|
|
29
|
-
|
|
28
|
+
layout.spacing = 8
|
|
29
|
+
@title_label = build_label(topbar, 'title_label', random_title_slogan)
|
|
30
|
+
layout.add_widget(@title_label)
|
|
30
31
|
layout.add_stretch(1)
|
|
31
32
|
@clock_label = build_label(topbar, 'clock_label', nil, width: 220)
|
|
32
|
-
@clock_label.
|
|
33
|
+
@clock_label.alignment = Qt::AlignCenter
|
|
33
34
|
layout.add_widget(@clock_label)
|
|
34
35
|
end
|
|
35
36
|
|
|
@@ -37,7 +38,7 @@ module QTimetrap
|
|
|
37
38
|
@task_input = build_task_input(row)
|
|
38
39
|
@project_input = build_project_input(row)
|
|
39
40
|
@timer_label = build_label(row, 'timer_label', '00:00:00', width: 120)
|
|
40
|
-
@timer_label.
|
|
41
|
+
@timer_label.alignment = Qt::AlignCenter
|
|
41
42
|
@start_button = build_button(row, 'start_button', 'START', 64, 48)
|
|
42
43
|
@stop_button = build_button(row, 'stop_button', 'STOP', 64, 48)
|
|
43
44
|
@stop_button.hide
|
|
@@ -47,7 +48,7 @@ module QTimetrap
|
|
|
47
48
|
|
|
48
49
|
def add_actions_row_widgets(layout, row)
|
|
49
50
|
@summary_label = build_label(row, 'summary_label', nil, height: 42)
|
|
50
|
-
@summary_label.
|
|
51
|
+
@summary_label.alignment = Qt::AlignCenter
|
|
51
52
|
@theme_button = build_button(row, 'theme_button', 'THEME', 112, 34)
|
|
52
53
|
refresh_button = build_button(row, 'refresh_button', 'REFRESH', 110, 34)
|
|
53
54
|
layout.add_widget(@summary_label)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'fileutils'
|
|
4
|
+
require 'set'
|
|
4
5
|
require 'yaml'
|
|
5
6
|
|
|
6
7
|
module QTimetrap
|
|
@@ -12,7 +13,7 @@ module QTimetrap
|
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def archived_ids
|
|
15
|
-
read_ids
|
|
16
|
+
read_ids.to_a.sort
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def archived?(entry_id)
|
|
@@ -47,19 +48,23 @@ module QTimetrap
|
|
|
47
48
|
attr_reader :path
|
|
48
49
|
|
|
49
50
|
def read_ids
|
|
50
|
-
return
|
|
51
|
+
return @ids_cache if @ids_cache
|
|
52
|
+
return @ids_cache = Set.new unless File.exist?(path)
|
|
51
53
|
|
|
52
54
|
payload = YAML.safe_load_file(path, permitted_classes: [], aliases: false)
|
|
53
|
-
return
|
|
55
|
+
return @ids_cache = Set.new unless payload.is_a?(Hash)
|
|
54
56
|
|
|
55
|
-
Array(payload['archived_entry_ids']).filter_map { |value| Integer(value) }.uniq
|
|
57
|
+
values = Array(payload['archived_entry_ids']).filter_map { |value| Integer(value) }.uniq
|
|
58
|
+
@ids_cache = Set.new(values)
|
|
56
59
|
rescue Psych::SyntaxError, ArgumentError, TypeError
|
|
57
|
-
|
|
60
|
+
@ids_cache = Set.new
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def write_ids(ids)
|
|
61
64
|
FileUtils.mkdir_p(File.dirname(path))
|
|
62
|
-
|
|
65
|
+
normalized_ids = ids.uniq.sort
|
|
66
|
+
File.write(path, { 'archived_entry_ids' => normalized_ids }.to_yaml)
|
|
67
|
+
@ids_cache = Set.new(normalized_ids)
|
|
63
68
|
end
|
|
64
69
|
|
|
65
70
|
def default_path
|
|
@@ -190,21 +190,29 @@ QLineEdit#entry_node_entry_note:focus {
|
|
|
190
190
|
padding-left: 6px;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
QLabel#entry_node_entry_task {
|
|
194
194
|
color: #94a3b8;
|
|
195
195
|
font-size: 12px;
|
|
196
196
|
background: transparent;
|
|
197
|
-
border: 0px;
|
|
198
197
|
padding-left: 2px;
|
|
199
198
|
}
|
|
200
199
|
|
|
201
|
-
QComboBox#
|
|
200
|
+
QComboBox#entry_node_entry_task_editor {
|
|
201
|
+
color: #94a3b8;
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
background-color: #111827;
|
|
204
|
+
border: 1px solid #334155;
|
|
205
|
+
border-radius: 6px;
|
|
206
|
+
padding-left: 2px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
QComboBox#entry_node_entry_task_editor:focus {
|
|
202
210
|
background-color: #111827;
|
|
203
211
|
border: 1px solid #334155;
|
|
204
212
|
border-radius: 6px;
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
QComboBox#
|
|
215
|
+
QComboBox#entry_node_entry_task_editor QAbstractItemView {
|
|
208
216
|
background-color: #111827;
|
|
209
217
|
color: #e2e8f0;
|
|
210
218
|
border: 1px solid #334155;
|
|
@@ -190,21 +190,29 @@ QLineEdit#entry_node_entry_note:focus {
|
|
|
190
190
|
padding-left: 6px;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
QLabel#entry_node_entry_task {
|
|
194
194
|
color: #334155;
|
|
195
195
|
font-size: 12px;
|
|
196
196
|
background: transparent;
|
|
197
|
-
border: 0px;
|
|
198
197
|
padding-left: 2px;
|
|
199
198
|
}
|
|
200
199
|
|
|
201
|
-
QComboBox#
|
|
200
|
+
QComboBox#entry_node_entry_task_editor {
|
|
201
|
+
color: #334155;
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
background-color: #ffffff;
|
|
204
|
+
border: 1px solid #d8e0ea;
|
|
205
|
+
border-radius: 6px;
|
|
206
|
+
padding-left: 2px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
QComboBox#entry_node_entry_task_editor:focus {
|
|
202
210
|
background-color: #ffffff;
|
|
203
211
|
border: 1px solid #d8e0ea;
|
|
204
212
|
border-radius: 6px;
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
QComboBox#
|
|
215
|
+
QComboBox#entry_node_entry_task_editor QAbstractItemView {
|
|
208
216
|
background-color: #ffffff;
|
|
209
217
|
color: #334155;
|
|
210
218
|
border: 1px solid #d8e0ea;
|
|
@@ -93,6 +93,10 @@ module QTimetrap
|
|
|
93
93
|
EntryNodesBuilder.new(entries: filtered_entries, selected_project: selected_project).build
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
def all_entry_nodes
|
|
97
|
+
EntryNodesBuilder.new(entries: entries, selected_project: '* ALL').build
|
|
98
|
+
end
|
|
99
|
+
|
|
96
100
|
private
|
|
97
101
|
|
|
98
102
|
attr_reader :gateway, :archived_entries_store
|
data/app/views/main_window.rb
CHANGED
|
@@ -46,7 +46,6 @@ module QTimetrap
|
|
|
46
46
|
@window = build_base_window
|
|
47
47
|
restore_window_geometry
|
|
48
48
|
set_window_icon
|
|
49
|
-
window.set_style_sheet(theme.application_stylesheet)
|
|
50
49
|
ui = MainWindowLayoutBuilder.new(
|
|
51
50
|
window: window,
|
|
52
51
|
callbacks: layout_callbacks
|
|
@@ -54,11 +53,12 @@ module QTimetrap
|
|
|
54
53
|
@sidebar = ui.fetch(:sidebar)
|
|
55
54
|
@controls = ui.fetch(:controls)
|
|
56
55
|
@entries = ui.fetch(:entries)
|
|
56
|
+
apply_theme_styles
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def connect_heartbeat
|
|
60
60
|
@heartbeat = QTimer.new(window)
|
|
61
|
-
heartbeat.
|
|
61
|
+
heartbeat.interval = HEARTBEAT_MS
|
|
62
62
|
heartbeat.connect('timeout') { |_| on_tick }
|
|
63
63
|
heartbeat.start
|
|
64
64
|
end
|
|
@@ -67,7 +67,6 @@ module QTimetrap
|
|
|
67
67
|
window.on(:key_press) { |event| on_key_press(event) }
|
|
68
68
|
register_blur_click_source(window)
|
|
69
69
|
register_blur_click_source(sidebar.widget)
|
|
70
|
-
register_blur_click_source(controls.widget)
|
|
71
70
|
register_blur_click_source(entries.widget)
|
|
72
71
|
end
|
|
73
72
|
|
|
@@ -18,7 +18,7 @@ module QTimetrap
|
|
|
18
18
|
def build
|
|
19
19
|
root = QHBoxLayout.new(window)
|
|
20
20
|
root.set_contents_margins(0, 0, 0, 0)
|
|
21
|
-
root.
|
|
21
|
+
root.spacing = 0
|
|
22
22
|
splitter, sidebar, controls, entries = build_splitter_and_content
|
|
23
23
|
root.add_widget(splitter)
|
|
24
24
|
{ sidebar: sidebar, controls: controls, entries: entries }
|
|
@@ -30,8 +30,8 @@ module QTimetrap
|
|
|
30
30
|
|
|
31
31
|
def build_splitter_and_content
|
|
32
32
|
splitter = QSplitter.new(window)
|
|
33
|
-
splitter.
|
|
34
|
-
splitter.
|
|
33
|
+
splitter.orientation = Qt::Horizontal
|
|
34
|
+
splitter.mouse_tracking = true
|
|
35
35
|
sidebar = build_sidebar(parent: splitter)
|
|
36
36
|
content, controls, entries = build_content(parent: splitter)
|
|
37
37
|
splitter.add_widget(sidebar.widget)
|
|
@@ -56,8 +56,8 @@ module QTimetrap
|
|
|
56
56
|
on_archive_mode_toggled: callbacks.fetch(:on_archive_mode_toggled)
|
|
57
57
|
).tap do |component|
|
|
58
58
|
component.widget.set_base_size(SIDEBAR_WIDTH, 0)
|
|
59
|
-
component.widget.
|
|
60
|
-
component.widget.
|
|
59
|
+
component.widget.minimum_width = SIDEBAR_MIN_WIDTH
|
|
60
|
+
component.widget.maximum_width = SIDEBAR_MAX_WIDTH
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -93,7 +93,7 @@ module QTimetrap
|
|
|
93
93
|
content = QWidget.new(parent)
|
|
94
94
|
layout = QVBoxLayout.new(content)
|
|
95
95
|
layout.set_contents_margins(14, 8, 14, 8)
|
|
96
|
-
layout.
|
|
96
|
+
layout.spacing = 10
|
|
97
97
|
[content, layout]
|
|
98
98
|
end
|
|
99
99
|
|
|
@@ -22,8 +22,8 @@ module QTimetrap
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def update_live_indicators(now)
|
|
25
|
-
controls.clock_label.
|
|
26
|
-
controls.timer_label.
|
|
25
|
+
controls.clock_label.text = now.strftime('%a %d %b %Y %H:%M:%S')
|
|
26
|
+
controls.timer_label.text = view_model.running_timer_line(now: now)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def refresh_if_needed
|
|
@@ -56,7 +56,13 @@ module QTimetrap
|
|
|
56
56
|
update_current_fields = !view_model.running_current_sheet?
|
|
57
57
|
view_model.select_projects(projects, primary_project: project, sync_current_fields: update_current_fields)
|
|
58
58
|
view_model.current_project_name = project if update_current_fields && project != '* ALL'
|
|
59
|
-
|
|
59
|
+
sidebar.update_tasks(
|
|
60
|
+
tasks: view_model.task_names_for_selected_project,
|
|
61
|
+
selected_project: view_model.selected_project,
|
|
62
|
+
selected_task: view_model.selected_tasks.first
|
|
63
|
+
)
|
|
64
|
+
controls.update_summary(view_model.summary_line)
|
|
65
|
+
entries.filter_to(view_model.entry_nodes)
|
|
60
66
|
return unless update_current_fields
|
|
61
67
|
|
|
62
68
|
controls.update_task_input(view_model.current_sheet_input)
|
|
@@ -69,8 +75,8 @@ module QTimetrap
|
|
|
69
75
|
view_model.current_task_input = task.to_s
|
|
70
76
|
controls.update_task_input(view_model.current_sheet_input)
|
|
71
77
|
end
|
|
72
|
-
|
|
73
|
-
entries.
|
|
78
|
+
controls.update_summary(view_model.summary_line)
|
|
79
|
+
entries.filter_to(view_model.entry_nodes)
|
|
74
80
|
end
|
|
75
81
|
|
|
76
82
|
def handle_project_input(project_name)
|
|
@@ -81,12 +87,12 @@ module QTimetrap
|
|
|
81
87
|
|
|
82
88
|
def handle_time_range_changed(from_at, to_at)
|
|
83
89
|
view_model.update_time_range_filter(from_at: from_at, to_at: to_at)
|
|
84
|
-
|
|
90
|
+
controls.update_summary(view_model.summary_line)
|
|
85
91
|
entries.update_time_range_inputs(
|
|
86
92
|
from_at: view_model.time_filter_from_at,
|
|
87
93
|
to_at: view_model.time_filter_to_at
|
|
88
94
|
)
|
|
89
|
-
entries.
|
|
95
|
+
entries.filter_to(view_model.entry_nodes)
|
|
90
96
|
rescue StandardError => e
|
|
91
97
|
warn("[qtimetrap] update time-range failed: #{e.class}: #{e.message}")
|
|
92
98
|
end
|
|
@@ -114,7 +120,7 @@ module QTimetrap
|
|
|
114
120
|
def switch_theme!
|
|
115
121
|
@theme = theme.with_name(next_theme_name)
|
|
116
122
|
apply_theme
|
|
117
|
-
|
|
123
|
+
controls.update_theme_label(theme.name)
|
|
118
124
|
rescue StandardError => e
|
|
119
125
|
warn("[qtimetrap] save theme failed: #{e.class}: #{e.message}")
|
|
120
126
|
end
|
|
@@ -8,7 +8,13 @@ module QTimetrap
|
|
|
8
8
|
|
|
9
9
|
def handle_archive_mode_toggled(enabled)
|
|
10
10
|
view_model.archive_mode = enabled
|
|
11
|
-
|
|
11
|
+
sidebar.update_tasks(
|
|
12
|
+
tasks: view_model.task_names_for_selected_project,
|
|
13
|
+
selected_project: view_model.selected_project,
|
|
14
|
+
selected_task: view_model.selected_tasks.first
|
|
15
|
+
)
|
|
16
|
+
controls.update_summary(view_model.summary_line)
|
|
17
|
+
entries.filter_to(view_model.entry_nodes)
|
|
12
18
|
end
|
|
13
19
|
|
|
14
20
|
def handle_entry_archived(entry_id)
|
|
@@ -22,17 +22,16 @@ module QTimetrap
|
|
|
22
22
|
|
|
23
23
|
def on_mouse_button_press(event, source_widget: window)
|
|
24
24
|
focused = window.focus_widget
|
|
25
|
-
return unless editable_line_edit?(focused)
|
|
26
|
-
|
|
27
25
|
target = click_target_widget(source_widget, event)
|
|
28
|
-
return if
|
|
26
|
+
return if editable_input?(target) || entries.task_editor_widget?(target)
|
|
29
27
|
|
|
30
|
-
focused
|
|
28
|
+
blur_editable_input(focused) if editable_input?(focused)
|
|
29
|
+
entries.close_active_task_editor
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def active_line_edit?
|
|
34
33
|
focused = window.focus_widget
|
|
35
|
-
|
|
34
|
+
editable_input?(focused)
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
def toggle_tracking_via_space
|
|
@@ -50,6 +49,26 @@ module QTimetrap
|
|
|
50
49
|
def editable_line_edit?(widget)
|
|
51
50
|
widget.is_a?(QLineEdit) && !widget.is_read_only
|
|
52
51
|
end
|
|
52
|
+
|
|
53
|
+
def editable_task_combo?(widget)
|
|
54
|
+
return false unless widget.is_a?(QComboBox)
|
|
55
|
+
|
|
56
|
+
widget.object_name == 'entry_node_entry_task_editor'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def editable_input?(widget)
|
|
60
|
+
editable_line_edit?(widget) || editable_task_combo?(widget)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def blur_editable_input(widget)
|
|
64
|
+
if editable_task_combo?(widget)
|
|
65
|
+
widget.line_edit.clear_focus
|
|
66
|
+
widget.clear_focus
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
widget.clear_focus
|
|
71
|
+
end
|
|
53
72
|
end
|
|
54
73
|
end
|
|
55
74
|
end
|
|
@@ -30,7 +30,8 @@ module QTimetrap
|
|
|
30
30
|
from_at: view_model.time_filter_from_at,
|
|
31
31
|
to_at: view_model.time_filter_to_at
|
|
32
32
|
)
|
|
33
|
-
entries.render(view_model.
|
|
33
|
+
entries.render(view_model.all_entry_nodes)
|
|
34
|
+
entries.filter_to(view_model.entry_nodes)
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def render_controls(sync_sheet:)
|
|
@@ -10,7 +10,7 @@ module QTimetrap
|
|
|
10
10
|
attempts = { count: 0 }
|
|
11
11
|
context = { splitter: splitter, sidebar_widget: sidebar_widget, button: button, zone: zone }
|
|
12
12
|
timer = QTimer.new(zone)
|
|
13
|
-
timer.
|
|
13
|
+
timer.interval = 120
|
|
14
14
|
timer.connect('timeout') do |_|
|
|
15
15
|
tick_initial_reposition(
|
|
16
16
|
attempts: attempts,
|
|
@@ -39,10 +39,10 @@ module QTimetrap
|
|
|
39
39
|
|
|
40
40
|
def build_sidebar_toggle_button(window)
|
|
41
41
|
QPushButton.new(window).tap do |button|
|
|
42
|
-
button.
|
|
43
|
-
button.
|
|
44
|
-
button.
|
|
45
|
-
button.
|
|
42
|
+
button.object_name = 'sidebar_toggle_button'
|
|
43
|
+
button.text = '◀'
|
|
44
|
+
button.focus_policy = Qt::NoFocus
|
|
45
|
+
button.tool_tip = 'Collapse sidebar'
|
|
46
46
|
button.set_fixed_size(SIDEBAR_TOGGLE_W, SIDEBAR_TOGGLE_H)
|
|
47
47
|
button.raise
|
|
48
48
|
button.hide
|
|
@@ -57,9 +57,9 @@ module QTimetrap
|
|
|
57
57
|
|
|
58
58
|
def build_sidebar_toggle_zone(window)
|
|
59
59
|
QWidget.new(window).tap do |zone|
|
|
60
|
-
zone.
|
|
60
|
+
zone.object_name = 'sidebar_toggle_hotspot'
|
|
61
61
|
zone.set_fixed_size(SIDEBAR_TOGGLE_ZONE_W, SIDEBAR_TOGGLE_ZONE_H)
|
|
62
|
-
zone.
|
|
62
|
+
zone.style_sheet = 'background: transparent;'
|
|
63
63
|
zone.raise
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -90,12 +90,12 @@ module QTimetrap
|
|
|
90
90
|
def update_sidebar_visibility(sidebar_widget, button, collapsed:)
|
|
91
91
|
if collapsed
|
|
92
92
|
sidebar_widget.hide
|
|
93
|
-
button.
|
|
94
|
-
button.
|
|
93
|
+
button.text = '▶'
|
|
94
|
+
button.tool_tip = 'Expand sidebar'
|
|
95
95
|
else
|
|
96
96
|
sidebar_widget.show
|
|
97
|
-
button.
|
|
98
|
-
button.
|
|
97
|
+
button.text = '◀'
|
|
98
|
+
button.tool_tip = 'Collapse sidebar'
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
end
|
|
@@ -43,7 +43,7 @@ module QTimetrap
|
|
|
43
43
|
|
|
44
44
|
def hide_toggle_button_unless_hovered(button:, state:)
|
|
45
45
|
show = state[:button_hovered] || state[:zone_hovered]
|
|
46
|
-
button.
|
|
46
|
+
button.visible = show
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def schedule_toggle_hide(button:, state:)
|
|
@@ -59,7 +59,7 @@ module QTimetrap
|
|
|
59
59
|
|
|
60
60
|
def build_toggle_hide_timer(button, state)
|
|
61
61
|
QTimer.new(button).tap do |timer|
|
|
62
|
-
timer.
|
|
62
|
+
timer.interval = SIDEBAR_TOGGLE_HIDE_DELAY_MS
|
|
63
63
|
timer.connect('timeout') do |_|
|
|
64
64
|
timer.stop
|
|
65
65
|
hide_toggle_button_unless_hovered(button: button, state: state)
|
|
@@ -29,17 +29,45 @@ module QTimetrap
|
|
|
29
29
|
|
|
30
30
|
def build_base_window
|
|
31
31
|
QWidget.new do |widget|
|
|
32
|
-
widget.
|
|
33
|
-
widget.
|
|
32
|
+
widget.window_title = 'QTimetrap'
|
|
33
|
+
widget.object_name = 'main_window'
|
|
34
34
|
widget.set_geometry(40, 40, self.class::WINDOW_W, self.class::WINDOW_H)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def apply_theme
|
|
39
39
|
Application.configuration.theme_name = theme.name
|
|
40
|
-
|
|
40
|
+
apply_theme_styles(update_window_background: false, incremental: true)
|
|
41
41
|
settings_store.write_theme_name(theme.name)
|
|
42
42
|
end
|
|
43
|
+
|
|
44
|
+
def apply_theme_styles(update_window_background: true, incremental: false)
|
|
45
|
+
window.style_sheet = theme.stylesheet('application') if update_window_background
|
|
46
|
+
sidebar_stylesheet = theme.stylesheet('project_sidebar')
|
|
47
|
+
controls_stylesheet = theme.stylesheet('tracker_controls')
|
|
48
|
+
entries_stylesheet = theme.stylesheet('entries_list')
|
|
49
|
+
if incremental
|
|
50
|
+
sidebar.widget.style_sheet = stylesheet_rule(sidebar_stylesheet, 'QWidget#sidebar_panel')
|
|
51
|
+
entries.apply_theme_stylesheet(
|
|
52
|
+
entries_stylesheet,
|
|
53
|
+
external_targets: sidebar.theme_style_targets(sidebar_stylesheet) +
|
|
54
|
+
controls.theme_style_targets(controls_stylesheet),
|
|
55
|
+
defer: true
|
|
56
|
+
)
|
|
57
|
+
else
|
|
58
|
+
sidebar.widget.style_sheet = sidebar_stylesheet
|
|
59
|
+
controls.widget.style_sheet = controls_stylesheet
|
|
60
|
+
entries.apply_theme_stylesheet(entries_stylesheet)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def stylesheet_rule(stylesheet, selector)
|
|
65
|
+
stylesheet.scan(/([^{}]+)\{([^{}]*)\}/m).find do |selectors, _declarations|
|
|
66
|
+
selectors.split(',').map(&:strip).include?(selector)
|
|
67
|
+
end&.then do |selectors, declarations|
|
|
68
|
+
"#{selectors.strip} {#{declarations}}"
|
|
69
|
+
end.to_s
|
|
70
|
+
end
|
|
43
71
|
end
|
|
44
72
|
end
|
|
45
73
|
end
|
data/lib/qtimetrap/version.rb
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Qt
|
|
6
|
+
# Enforces Ruby-style Qt property setters over set_* bridge methods.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# widget.set_tool_tip('Hello')
|
|
11
|
+
#
|
|
12
|
+
# # good
|
|
13
|
+
# widget.tool_tip = 'Hello'
|
|
14
|
+
class PreferRubyStyleSetter < Base
|
|
15
|
+
extend AutoCorrector
|
|
16
|
+
|
|
17
|
+
MSG = 'Prefer Ruby-style Qt setter `%<replacement>s` over `%<original>s`.'
|
|
18
|
+
def on_send(node)
|
|
19
|
+
return unless qt_setter_call?(node)
|
|
20
|
+
|
|
21
|
+
method_name = node.method_name.to_s
|
|
22
|
+
replacement = replacement_source(node)
|
|
23
|
+
add_offense(
|
|
24
|
+
node.loc.selector,
|
|
25
|
+
message: format(MSG, replacement: replacement, original: method_name)
|
|
26
|
+
) do |corrector|
|
|
27
|
+
corrector.replace(node.source_range, replacement)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def qt_setter_call?(node)
|
|
34
|
+
return false unless node.receiver
|
|
35
|
+
|
|
36
|
+
node.arguments.one? &&
|
|
37
|
+
node.method_name.to_s.start_with?('set_') &&
|
|
38
|
+
!node.setter_method?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def replacement_source(node)
|
|
42
|
+
attribute_name = node.method_name.to_s.delete_prefix('set_')
|
|
43
|
+
receiver = node.receiver.source
|
|
44
|
+
argument = node.first_argument.source
|
|
45
|
+
"#{receiver}.#{attribute_name} = #{argument}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: qtimetrap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maksim Veynberg
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
version: '0.1'
|
|
19
19
|
- - ">="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.1.
|
|
21
|
+
version: 0.1.7
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -28,7 +28,7 @@ dependencies:
|
|
|
28
28
|
version: '0.1'
|
|
29
29
|
- - ">="
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
|
-
version: 0.1.
|
|
31
|
+
version: 0.1.7
|
|
32
32
|
- !ruby/object:Gem::Dependency
|
|
33
33
|
name: zeitwerk
|
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,6 +51,7 @@ executables:
|
|
|
51
51
|
extensions: []
|
|
52
52
|
extra_rdoc_files: []
|
|
53
53
|
files:
|
|
54
|
+
- CHANGELOG.md
|
|
54
55
|
- LICENSE
|
|
55
56
|
- README.md
|
|
56
57
|
- Rakefile
|
|
@@ -59,13 +60,17 @@ files:
|
|
|
59
60
|
- app/assets/icons/qtimetrap-icon-512.png
|
|
60
61
|
- app/assets/icons/qtimetrap-icon.svg
|
|
61
62
|
- app/components/entries/branch_hierarchy_helpers.rb
|
|
63
|
+
- app/components/entries/entry_row_render_helpers.rb
|
|
62
64
|
- app/components/entries/leaf_archive_helpers.rb
|
|
63
65
|
- app/components/entries/leaf_note_helpers.rb
|
|
66
|
+
- app/components/entries/leaf_task_display_helpers.rb
|
|
67
|
+
- app/components/entries/leaf_task_editor_state_helpers.rb
|
|
64
68
|
- app/components/entries/leaf_task_helpers.rb
|
|
65
69
|
- app/components/entries/leaf_time_helpers.rb
|
|
66
70
|
- app/components/entries/list_component.rb
|
|
67
71
|
- app/components/entries/list_host_helpers.rb
|
|
68
72
|
- app/components/entries/list_state_helpers.rb
|
|
73
|
+
- app/components/entries/list_task_editor_helpers.rb
|
|
69
74
|
- app/components/entries/node_presentation_helpers.rb
|
|
70
75
|
- app/components/entries/render_helpers.rb
|
|
71
76
|
- app/components/entries/tree_helpers.rb
|
|
@@ -179,12 +184,13 @@ files:
|
|
|
179
184
|
- lib/qtimetrap/configuration.rb
|
|
180
185
|
- lib/qtimetrap/container.rb
|
|
181
186
|
- lib/qtimetrap/version.rb
|
|
187
|
+
- lib/rubocop/cop/qt/prefer_ruby_style_setter.rb
|
|
182
188
|
homepage: https://github.com/CyJimmy264/qtimetrap
|
|
183
189
|
licenses:
|
|
184
190
|
- BSD-2-Clause
|
|
185
191
|
metadata:
|
|
186
192
|
source_code_uri: https://github.com/CyJimmy264/qtimetrap
|
|
187
|
-
changelog_uri: https://github.com/CyJimmy264/qtimetrap/
|
|
193
|
+
changelog_uri: https://github.com/CyJimmy264/qtimetrap/blob/master/CHANGELOG.md
|
|
188
194
|
bug_tracker_uri: https://github.com/CyJimmy264/qtimetrap/issues
|
|
189
195
|
rubygems_mfa_required: 'true'
|
|
190
196
|
rdoc_options: []
|
|
@@ -201,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
201
207
|
- !ruby/object:Gem::Version
|
|
202
208
|
version: '0'
|
|
203
209
|
requirements: []
|
|
204
|
-
rubygems_version:
|
|
210
|
+
rubygems_version: 4.0.10
|
|
205
211
|
specification_version: 4
|
|
206
212
|
summary: Desktop Timetrap UI on Qt
|
|
207
213
|
test_files: []
|