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
@@ -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.set_spacing(14)
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.set_spacing(8)
29
- layout.add_widget(build_label(topbar, 'title_label', random_title_slogan))
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.set_alignment(Qt::AlignCenter)
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.set_alignment(Qt::AlignCenter)
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.set_alignment(Qt::AlignCenter)
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 [] unless File.exist?(path)
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 [] unless payload.is_a?(Hash)
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.sort
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
- File.write(path, { 'archived_entry_ids' => ids.uniq.sort }.to_yaml)
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
- QComboBox#entry_node_entry_task {
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#entry_node_entry_task:focus {
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#entry_node_entry_task QAbstractItemView {
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
- QComboBox#entry_node_entry_task {
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#entry_node_entry_task:focus {
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#entry_node_entry_task QAbstractItemView {
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
@@ -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.set_interval(HEARTBEAT_MS)
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.set_spacing(0)
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.set_orientation(Qt::Horizontal)
34
- splitter.set_mouse_tracking(true)
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.set_minimum_width(SIDEBAR_MIN_WIDTH)
60
- component.widget.set_maximum_width(SIDEBAR_MAX_WIDTH)
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.set_spacing(10)
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.set_text(now.strftime('%a %d %b %Y %H:%M:%S'))
26
- controls.timer_label.set_text(view_model.running_timer_line(now: now))
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
- render!
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
- render_controls(sync_sheet: false)
73
- entries.render(view_model.entry_nodes)
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
- render_controls(sync_sheet: false)
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.render(view_model.entry_nodes)
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
- render!
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
- render!(sync_sheet: false)
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 editable_line_edit?(target)
26
+ return if editable_input?(target) || entries.task_editor_widget?(target)
29
27
 
30
- focused.clear_focus
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
- editable_line_edit?(focused)
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.entry_nodes)
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.set_interval(120)
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.set_object_name('sidebar_toggle_button')
43
- button.set_text('◀')
44
- button.set_focus_policy(Qt::NoFocus)
45
- button.set_tool_tip('Collapse sidebar')
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.set_object_name('sidebar_toggle_hotspot')
60
+ zone.object_name = 'sidebar_toggle_hotspot'
61
61
  zone.set_fixed_size(SIDEBAR_TOGGLE_ZONE_W, SIDEBAR_TOGGLE_ZONE_H)
62
- zone.set_style_sheet('background: transparent;')
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.set_text('▶')
94
- button.set_tool_tip('Expand sidebar')
93
+ button.text = '▶'
94
+ button.tool_tip = 'Expand sidebar'
95
95
  else
96
96
  sidebar_widget.show
97
- button.set_text('◀')
98
- button.set_tool_tip('Collapse sidebar')
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.set_visible(show)
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.set_interval(SIDEBAR_TOGGLE_HIDE_DELAY_MS)
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.set_window_title('QTimetrap')
33
- widget.set_object_name('main_window')
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
- window.set_style_sheet(theme.application_stylesheet)
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
@@ -15,7 +15,7 @@ module QTimetrap
15
15
 
16
16
  icon = QIcon.new(candidates.first)
17
17
  add_fallbacks(icon, candidates.drop(1))
18
- window.set_window_icon(icon)
18
+ window.window_icon = icon
19
19
  end
20
20
 
21
21
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QTimetrap
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.3'
5
5
  end
@@ -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.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.4
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.4
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/releases
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: 3.6.7
210
+ rubygems_version: 4.0.10
205
211
  specification_version: 4
206
212
  summary: Desktop Timetrap UI on Qt
207
213
  test_files: []