qtimetrap 0.1.1
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 +7 -0
- data/LICENSE +25 -0
- data/README.md +92 -0
- data/Rakefile +105 -0
- data/app/assets/icons/qtimetrap-icon-128.png +0 -0
- data/app/assets/icons/qtimetrap-icon-256.png +0 -0
- data/app/assets/icons/qtimetrap-icon-512.png +0 -0
- data/app/assets/icons/qtimetrap-icon.svg +123 -0
- data/app/components/entries/branch_hierarchy_helpers.rb +41 -0
- data/app/components/entries/leaf_archive_helpers.rb +23 -0
- data/app/components/entries/leaf_note_helpers.rb +118 -0
- data/app/components/entries/leaf_task_helpers.rb +81 -0
- data/app/components/entries/leaf_time_helpers.rb +105 -0
- data/app/components/entries/list_component.rb +118 -0
- data/app/components/entries/list_host_helpers.rb +27 -0
- data/app/components/entries/list_state_helpers.rb +59 -0
- data/app/components/entries/node_presentation_helpers.rb +29 -0
- data/app/components/entries/render_helpers.rb +25 -0
- data/app/components/entries/tree_helpers.rb +133 -0
- data/app/components/entries/tree_toolbar_helpers.rb +98 -0
- data/app/components/project_sidebar/archive_toggle_helpers.rb +22 -0
- data/app/components/project_sidebar/component.rb +125 -0
- data/app/components/project_sidebar/logo_helpers.rb +53 -0
- data/app/components/project_sidebar/project_button_helpers.rb +72 -0
- data/app/components/project_sidebar/project_selection_helpers.rb +94 -0
- data/app/components/project_sidebar/task_helpers.rb +94 -0
- data/app/components/project_sidebar/task_selection_helpers.rb +72 -0
- data/app/components/qt_ui_helpers.rb +27 -0
- data/app/components/tracker_controls/component.rb +80 -0
- data/app/components/tracker_controls/layout_builder.rb +100 -0
- data/app/components/tracker_controls/layout_helpers.rb +88 -0
- data/app/models/null_settings_store.rb +12 -0
- data/app/models/time_entry.rb +59 -0
- data/app/services/archived_entries_store.rb +71 -0
- data/app/services/formatters.rb +29 -0
- data/app/services/settings_store.rb +83 -0
- data/app/services/timetrap_gateway.rb +89 -0
- data/app/services/timetrap_gateway_logger.rb +54 -0
- data/app/services/timetrap_gateway_query_helpers.rb +102 -0
- data/app/services/timetrap_gateway_start_helpers.rb +28 -0
- data/app/services/timetrap_gateway_update_note_helpers.rb +48 -0
- data/app/services/timetrap_gateway_update_task_helpers.rb +47 -0
- data/app/services/timetrap_gateway_update_time_helpers.rb +50 -0
- data/app/styles/theme.rb +61 -0
- data/app/styles/themes/dark/application.qss +8 -0
- data/app/styles/themes/dark/entries_list.qss +235 -0
- data/app/styles/themes/dark/project_sidebar.qss +99 -0
- data/app/styles/themes/dark/snippets/app_background.qss +2 -0
- data/app/styles/themes/dark/snippets/button_ghost.qss +5 -0
- data/app/styles/themes/dark/snippets/button_start.qss +5 -0
- data/app/styles/themes/dark/snippets/button_stop.qss +5 -0
- data/app/styles/themes/dark/snippets/entries_host.qss +2 -0
- data/app/styles/themes/dark/snippets/entries_scroll.qss +16 -0
- data/app/styles/themes/dark/snippets/entry_row_day.qss +6 -0
- data/app/styles/themes/dark/snippets/entry_row_detail.qss +5 -0
- data/app/styles/themes/dark/snippets/entry_row_project.qss +6 -0
- data/app/styles/themes/dark/snippets/project_button.qss +5 -0
- data/app/styles/themes/dark/snippets/project_button_active.qss +5 -0
- data/app/styles/themes/dark/snippets/project_sidebar_heading.qss +2 -0
- data/app/styles/themes/dark/snippets/project_sidebar_logo.qss +3 -0
- data/app/styles/themes/dark/snippets/project_sidebar_panel.qss +2 -0
- data/app/styles/themes/dark/snippets/tracker_clock.qss +2 -0
- data/app/styles/themes/dark/snippets/tracker_input.qss +5 -0
- data/app/styles/themes/dark/snippets/tracker_project_label.qss +5 -0
- data/app/styles/themes/dark/snippets/tracker_row.qss +3 -0
- data/app/styles/themes/dark/snippets/tracker_summary.qss +3 -0
- data/app/styles/themes/dark/snippets/tracker_timer.qss +3 -0
- data/app/styles/themes/dark/snippets/tracker_title.qss +3 -0
- data/app/styles/themes/dark/snippets/tracker_topbar.qss +2 -0
- data/app/styles/themes/dark/tracker_controls.qss +84 -0
- data/app/styles/themes/light/application.qss +8 -0
- data/app/styles/themes/light/entries_list.qss +235 -0
- data/app/styles/themes/light/project_sidebar.qss +99 -0
- data/app/styles/themes/light/snippets/app_background.qss +2 -0
- data/app/styles/themes/light/snippets/button_ghost.qss +5 -0
- data/app/styles/themes/light/snippets/button_start.qss +5 -0
- data/app/styles/themes/light/snippets/button_stop.qss +5 -0
- data/app/styles/themes/light/snippets/entries_host.qss +2 -0
- data/app/styles/themes/light/snippets/entries_scroll.qss +16 -0
- data/app/styles/themes/light/snippets/entry_row_day.qss +6 -0
- data/app/styles/themes/light/snippets/entry_row_detail.qss +5 -0
- data/app/styles/themes/light/snippets/entry_row_project.qss +6 -0
- data/app/styles/themes/light/snippets/project_button.qss +5 -0
- data/app/styles/themes/light/snippets/project_button_active.qss +5 -0
- data/app/styles/themes/light/snippets/project_sidebar_heading.qss +2 -0
- data/app/styles/themes/light/snippets/project_sidebar_logo.qss +3 -0
- data/app/styles/themes/light/snippets/project_sidebar_panel.qss +2 -0
- data/app/styles/themes/light/snippets/tracker_clock.qss +2 -0
- data/app/styles/themes/light/snippets/tracker_input.qss +5 -0
- data/app/styles/themes/light/snippets/tracker_project_label.qss +5 -0
- data/app/styles/themes/light/snippets/tracker_row.qss +3 -0
- data/app/styles/themes/light/snippets/tracker_summary.qss +3 -0
- data/app/styles/themes/light/snippets/tracker_timer.qss +3 -0
- data/app/styles/themes/light/snippets/tracker_title.qss +3 -0
- data/app/styles/themes/light/snippets/tracker_topbar.qss +2 -0
- data/app/styles/themes/light/tracker_controls.qss +84 -0
- data/app/view_models/entry_nodes_builder.rb +109 -0
- data/app/view_models/main_view_model.rb +135 -0
- data/app/view_models/main_view_model_archive_mode_helpers.rb +102 -0
- data/app/view_models/main_view_model_entry_note_helpers.rb +30 -0
- data/app/view_models/main_view_model_entry_task_helpers.rb +39 -0
- data/app/view_models/main_view_model_entry_time_helpers.rb +45 -0
- data/app/view_models/main_view_model_sheet_helpers.rb +74 -0
- data/app/view_models/main_view_model_task_filter_helpers.rb +58 -0
- data/app/view_models/main_view_model_time_range_filter_helpers.rb +23 -0
- data/app/views/main_window.rb +126 -0
- data/app/views/main_window_layout_builder.rb +114 -0
- data/app/views/main_window_runtime.rb +123 -0
- data/app/views/main_window_runtime_archive_helpers.rb +26 -0
- data/app/views/main_window_runtime_entry_task_helpers.rb +21 -0
- data/app/views/main_window_runtime_entry_time_helpers.rb +17 -0
- data/app/views/main_window_runtime_key_helpers.rb +55 -0
- data/app/views/main_window_runtime_render_helpers.rb +50 -0
- data/app/views/main_window_splitter_toggle_bootstrap_helpers.rb +34 -0
- data/app/views/main_window_splitter_toggle_helpers.rb +103 -0
- data/app/views/main_window_splitter_toggle_hover_helpers.rb +71 -0
- data/app/views/main_window_splitter_toggle_layout_helpers.rb +24 -0
- data/app/views/main_window_ui_helpers.rb +45 -0
- data/app/views/window_icon_loader.rb +37 -0
- data/bin/qtimetrap +7 -0
- data/config/application.rb +87 -0
- data/config/environments/development.rb +5 -0
- data/config/environments/production.rb +5 -0
- data/config/initializers/theme.rb +5 -0
- data/config/initializers/timetrap.rb +5 -0
- data/lib/qtimetrap/configuration.rb +15 -0
- data/lib/qtimetrap/container.rb +57 -0
- data/lib/qtimetrap/version.rb +5 -0
- data/lib/qtimetrap.rb +25 -0
- metadata +207 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
module Views
|
|
5
|
+
# Hover and delayed hide behavior for splitter toggle button.
|
|
6
|
+
module MainWindowSplitterToggleHoverHelpers
|
|
7
|
+
SIDEBAR_TOGGLE_HIDE_DELAY_MS = 40
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def bind_toggle_zone_events(zone:, button:, state:)
|
|
12
|
+
zone.on(:enter) { |_| on_toggle_zone_enter(button: button, state: state) }
|
|
13
|
+
zone.on(:leave) { |_| on_toggle_zone_leave(button: button, state: state) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def bind_toggle_button_events(button:, state:)
|
|
17
|
+
button.on(:enter) { |_| on_toggle_button_enter(button: button, state: state) }
|
|
18
|
+
button.on(:leave) { |_| on_toggle_button_leave(button: button, state: state) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def on_toggle_zone_enter(button:, state:)
|
|
22
|
+
state[:zone_hovered] = true
|
|
23
|
+
cancel_toggle_hide(state)
|
|
24
|
+
button.show
|
|
25
|
+
button.raise
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def on_toggle_zone_leave(button:, state:)
|
|
29
|
+
state[:zone_hovered] = false
|
|
30
|
+
schedule_toggle_hide(button: button, state: state)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def on_toggle_button_enter(button:, state:)
|
|
34
|
+
state[:button_hovered] = true
|
|
35
|
+
cancel_toggle_hide(state)
|
|
36
|
+
button.raise
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def on_toggle_button_leave(button:, state:)
|
|
40
|
+
state[:button_hovered] = false
|
|
41
|
+
schedule_toggle_hide(button: button, state: state)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def hide_toggle_button_unless_hovered(button:, state:)
|
|
45
|
+
show = state[:button_hovered] || state[:zone_hovered]
|
|
46
|
+
button.set_visible(show)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def schedule_toggle_hide(button:, state:)
|
|
50
|
+
timer = state[:hide_timer] ||= build_toggle_hide_timer(button, state)
|
|
51
|
+
timer.stop
|
|
52
|
+
timer.start
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def cancel_toggle_hide(state)
|
|
56
|
+
timer = state[:hide_timer]
|
|
57
|
+
timer&.stop
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_toggle_hide_timer(button, state)
|
|
61
|
+
QTimer.new(button).tap do |timer|
|
|
62
|
+
timer.set_interval(SIDEBAR_TOGGLE_HIDE_DELAY_MS)
|
|
63
|
+
timer.connect('timeout') do |_|
|
|
64
|
+
timer.stop
|
|
65
|
+
hide_toggle_button_unless_hovered(button: button, state: state)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
module Views
|
|
5
|
+
# Coordinate helpers for splitter toggle affordance.
|
|
6
|
+
module MainWindowSplitterToggleLayoutHelpers
|
|
7
|
+
SIDEBAR_TOGGLE_W = 20
|
|
8
|
+
SIDEBAR_TOGGLE_H = 56
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def toggle_x(splitter:, sidebar_widget:)
|
|
13
|
+
sidebar_width = sidebar_widget.is_visible ? sidebar_widget.width : 0
|
|
14
|
+
splitter.x + [sidebar_width - (SIDEBAR_TOGGLE_W / 2), 0].max
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def toggle_y(splitter_height:)
|
|
18
|
+
y_center = ((splitter_height - SIDEBAR_TOGGLE_H) / 2)
|
|
19
|
+
max_y = [splitter_height - SIDEBAR_TOGGLE_H - 8, 8].max
|
|
20
|
+
y_center.clamp(8, max_y)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
module Views
|
|
5
|
+
# UI helper methods extracted from MainWindow runtime logic.
|
|
6
|
+
module MainWindowUiHelpers
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def next_theme_name
|
|
10
|
+
themes = self.class::THEMES
|
|
11
|
+
current = themes.index(theme.name) || 0
|
|
12
|
+
themes[(current + 1) % themes.length]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def set_window_icon
|
|
16
|
+
WindowIconLoader.new(window: window, root: Application.root).apply
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
warn("[qtimetrap] icon load failed: #{e.class}: #{e.message}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def extract_event_value(event, key)
|
|
22
|
+
return unless event.respond_to?(:[])
|
|
23
|
+
|
|
24
|
+
value = event[key]
|
|
25
|
+
return value.to_i if value
|
|
26
|
+
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def build_base_window
|
|
31
|
+
QWidget.new do |widget|
|
|
32
|
+
widget.set_window_title('QTimetrap')
|
|
33
|
+
widget.set_object_name('main_window')
|
|
34
|
+
widget.set_geometry(40, 40, self.class::WINDOW_W, self.class::WINDOW_H)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def apply_theme
|
|
39
|
+
Application.configuration.theme_name = theme.name
|
|
40
|
+
window.set_style_sheet(theme.application_stylesheet)
|
|
41
|
+
settings_store.write_theme_name(theme.name)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
module Views
|
|
5
|
+
# Loads the app icon with SVG primary source and PNG fallback.
|
|
6
|
+
class WindowIconLoader
|
|
7
|
+
def initialize(window:, root:)
|
|
8
|
+
@window = window
|
|
9
|
+
@root = root
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def apply
|
|
13
|
+
candidates = icon_candidates
|
|
14
|
+
return if candidates.empty?
|
|
15
|
+
|
|
16
|
+
icon = QIcon.new(candidates.first)
|
|
17
|
+
add_fallbacks(icon, candidates.drop(1))
|
|
18
|
+
window.set_window_icon(icon)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :window, :root
|
|
24
|
+
|
|
25
|
+
def icon_candidates
|
|
26
|
+
icons_dir = File.join(root, 'app', 'assets', 'icons')
|
|
27
|
+
svg_path = File.join(icons_dir, 'qtimetrap-icon.svg')
|
|
28
|
+
png_fallback = File.join(icons_dir, 'qtimetrap-icon-256.png')
|
|
29
|
+
[svg_path, png_fallback].select { |path| File.exist?(path) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add_fallbacks(icon, paths)
|
|
33
|
+
paths.each { |path| icon.add_file(path) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/bin/qtimetrap
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
# Bootstraps app runtime, configuration, loader, and Qt application object.
|
|
5
|
+
module Application
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def boot!
|
|
9
|
+
setup_qt
|
|
10
|
+
loader.setup
|
|
11
|
+
load_environment!
|
|
12
|
+
load_initializers!
|
|
13
|
+
load_persisted_configuration!
|
|
14
|
+
qt_app
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def root
|
|
18
|
+
@root ||= File.expand_path('..', __dir__)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def environment
|
|
22
|
+
ENV.fetch('QTIMETRAP_ENV', 'development')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def configuration
|
|
26
|
+
@configuration ||= Configuration.new(environment: environment)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def configure
|
|
30
|
+
yield(configuration)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def container
|
|
34
|
+
@container ||= Container.new(config: configuration)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def loader
|
|
38
|
+
@loader ||= Zeitwerk::Loader.new.tap do |autoload|
|
|
39
|
+
autoload.push_dir(File.join(root, 'app'), namespace: QTimetrap)
|
|
40
|
+
autoload.collapse(File.join(root, 'app', 'components'))
|
|
41
|
+
autoload.enable_reloading if configuration.enable_reloading
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def setup_qt
|
|
46
|
+
return if @qt_app
|
|
47
|
+
|
|
48
|
+
@qt_app = QApplication.new(0, [])
|
|
49
|
+
apply_qt_identity!
|
|
50
|
+
rescue StandardError
|
|
51
|
+
@qt_app ||= nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def qt_app
|
|
55
|
+
@qt_app
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def load_environment!
|
|
59
|
+
path = File.join(root, 'config', 'environments', "#{environment}.rb")
|
|
60
|
+
require path if File.exist?(path)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def load_initializers!
|
|
64
|
+
Dir[File.join(root, 'config', 'initializers', '*.rb')].each do |file|
|
|
65
|
+
require file
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def load_persisted_configuration!
|
|
70
|
+
return if env_theme_name?
|
|
71
|
+
|
|
72
|
+
persisted_theme = Services::SettingsStore.new.read_theme_name
|
|
73
|
+
configuration.theme_name = persisted_theme if persisted_theme
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def env_theme_name?
|
|
77
|
+
ENV.fetch('QTIMETRAP_THEME', '').strip != ''
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def apply_qt_identity!
|
|
81
|
+
QApplication.set_application_name('qtimetrap')
|
|
82
|
+
QApplication.set_desktop_file_name('qtimetrap')
|
|
83
|
+
QApplication.set_application_display_name('QTimetrap')
|
|
84
|
+
QApplication.set_organization_name('mveynberg')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
# Mutable runtime configuration used during application boot.
|
|
5
|
+
class Configuration
|
|
6
|
+
attr_accessor :environment, :theme_name, :timetrap_bin, :enable_reloading
|
|
7
|
+
|
|
8
|
+
def initialize(environment: 'development')
|
|
9
|
+
@environment = environment
|
|
10
|
+
@theme_name = 'light'
|
|
11
|
+
@timetrap_bin = 't'
|
|
12
|
+
@enable_reloading = environment == 'development'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QTimetrap
|
|
4
|
+
# Tiny DI container that registers app services and memoized singletons.
|
|
5
|
+
class Container
|
|
6
|
+
def initialize(config:)
|
|
7
|
+
@config = config
|
|
8
|
+
@providers = {}
|
|
9
|
+
@memoized = {}
|
|
10
|
+
register_defaults
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def register(key, &provider)
|
|
14
|
+
providers[key.to_sym] = provider
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fetch(key)
|
|
18
|
+
name = key.to_sym
|
|
19
|
+
return memoized[name] if memoized.key?(name)
|
|
20
|
+
|
|
21
|
+
provider = providers.fetch(name)
|
|
22
|
+
memoized[name] = provider.call
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :config, :providers, :memoized
|
|
28
|
+
|
|
29
|
+
def register_defaults
|
|
30
|
+
register_core
|
|
31
|
+
register_ui
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def register_core
|
|
35
|
+
register(:theme) { Styles::Theme.new(name: config.theme_name, root: Application.root) }
|
|
36
|
+
register(:settings_store) { Services::SettingsStore.new }
|
|
37
|
+
register(:archived_entries_store) { Services::ArchivedEntriesStore.new }
|
|
38
|
+
register(:timetrap_gateway) { Services::TimetrapGateway.new(bin: config.timetrap_bin) }
|
|
39
|
+
register(:main_view_model) do
|
|
40
|
+
ViewModels::MainViewModel.new(
|
|
41
|
+
gateway: fetch(:timetrap_gateway),
|
|
42
|
+
archived_entries_store: fetch(:archived_entries_store)
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def register_ui
|
|
48
|
+
register(:main_window) do
|
|
49
|
+
Views::MainWindow.new(
|
|
50
|
+
view_model: fetch(:main_view_model),
|
|
51
|
+
theme: fetch(:theme),
|
|
52
|
+
settings_store: fetch(:settings_store)
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/qtimetrap.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'qt'
|
|
4
|
+
require 'zeitwerk'
|
|
5
|
+
|
|
6
|
+
require_relative 'qtimetrap/version'
|
|
7
|
+
require_relative 'qtimetrap/configuration'
|
|
8
|
+
require_relative 'qtimetrap/container'
|
|
9
|
+
require_relative '../config/application'
|
|
10
|
+
|
|
11
|
+
module QTimetrap
|
|
12
|
+
# CLI entrypoint for launching the Qt desktop application.
|
|
13
|
+
class CLI
|
|
14
|
+
def self.start(_argv = [])
|
|
15
|
+
app = QTimetrap::Application.boot!
|
|
16
|
+
main_window = QTimetrap::Application.container.fetch(:main_window)
|
|
17
|
+
previous_int = Signal.trap('INT') { main_window.request_shutdown }
|
|
18
|
+
main_window.show
|
|
19
|
+
app.exec
|
|
20
|
+
ensure
|
|
21
|
+
Signal.trap('INT', previous_int) if defined?(previous_int) && previous_int
|
|
22
|
+
app&.dispose
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: qtimetrap
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Maksim Veynberg
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: qt
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.1'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 0.1.4
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '0.1'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 0.1.4
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: zeitwerk
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '2.6'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '2.6'
|
|
46
|
+
description: MVVM Ruby desktop app for Timetrap built with Qt and Zeitwerk.
|
|
47
|
+
email:
|
|
48
|
+
- mv@cj264.ru
|
|
49
|
+
executables:
|
|
50
|
+
- qtimetrap
|
|
51
|
+
extensions: []
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- Rakefile
|
|
57
|
+
- app/assets/icons/qtimetrap-icon-128.png
|
|
58
|
+
- app/assets/icons/qtimetrap-icon-256.png
|
|
59
|
+
- app/assets/icons/qtimetrap-icon-512.png
|
|
60
|
+
- app/assets/icons/qtimetrap-icon.svg
|
|
61
|
+
- app/components/entries/branch_hierarchy_helpers.rb
|
|
62
|
+
- app/components/entries/leaf_archive_helpers.rb
|
|
63
|
+
- app/components/entries/leaf_note_helpers.rb
|
|
64
|
+
- app/components/entries/leaf_task_helpers.rb
|
|
65
|
+
- app/components/entries/leaf_time_helpers.rb
|
|
66
|
+
- app/components/entries/list_component.rb
|
|
67
|
+
- app/components/entries/list_host_helpers.rb
|
|
68
|
+
- app/components/entries/list_state_helpers.rb
|
|
69
|
+
- app/components/entries/node_presentation_helpers.rb
|
|
70
|
+
- app/components/entries/render_helpers.rb
|
|
71
|
+
- app/components/entries/tree_helpers.rb
|
|
72
|
+
- app/components/entries/tree_toolbar_helpers.rb
|
|
73
|
+
- app/components/project_sidebar/archive_toggle_helpers.rb
|
|
74
|
+
- app/components/project_sidebar/component.rb
|
|
75
|
+
- app/components/project_sidebar/logo_helpers.rb
|
|
76
|
+
- app/components/project_sidebar/project_button_helpers.rb
|
|
77
|
+
- app/components/project_sidebar/project_selection_helpers.rb
|
|
78
|
+
- app/components/project_sidebar/task_helpers.rb
|
|
79
|
+
- app/components/project_sidebar/task_selection_helpers.rb
|
|
80
|
+
- app/components/qt_ui_helpers.rb
|
|
81
|
+
- app/components/tracker_controls/component.rb
|
|
82
|
+
- app/components/tracker_controls/layout_builder.rb
|
|
83
|
+
- app/components/tracker_controls/layout_helpers.rb
|
|
84
|
+
- app/models/null_settings_store.rb
|
|
85
|
+
- app/models/time_entry.rb
|
|
86
|
+
- app/services/archived_entries_store.rb
|
|
87
|
+
- app/services/formatters.rb
|
|
88
|
+
- app/services/settings_store.rb
|
|
89
|
+
- app/services/timetrap_gateway.rb
|
|
90
|
+
- app/services/timetrap_gateway_logger.rb
|
|
91
|
+
- app/services/timetrap_gateway_query_helpers.rb
|
|
92
|
+
- app/services/timetrap_gateway_start_helpers.rb
|
|
93
|
+
- app/services/timetrap_gateway_update_note_helpers.rb
|
|
94
|
+
- app/services/timetrap_gateway_update_task_helpers.rb
|
|
95
|
+
- app/services/timetrap_gateway_update_time_helpers.rb
|
|
96
|
+
- app/styles/theme.rb
|
|
97
|
+
- app/styles/themes/dark/application.qss
|
|
98
|
+
- app/styles/themes/dark/entries_list.qss
|
|
99
|
+
- app/styles/themes/dark/project_sidebar.qss
|
|
100
|
+
- app/styles/themes/dark/snippets/app_background.qss
|
|
101
|
+
- app/styles/themes/dark/snippets/button_ghost.qss
|
|
102
|
+
- app/styles/themes/dark/snippets/button_start.qss
|
|
103
|
+
- app/styles/themes/dark/snippets/button_stop.qss
|
|
104
|
+
- app/styles/themes/dark/snippets/entries_host.qss
|
|
105
|
+
- app/styles/themes/dark/snippets/entries_scroll.qss
|
|
106
|
+
- app/styles/themes/dark/snippets/entry_row_day.qss
|
|
107
|
+
- app/styles/themes/dark/snippets/entry_row_detail.qss
|
|
108
|
+
- app/styles/themes/dark/snippets/entry_row_project.qss
|
|
109
|
+
- app/styles/themes/dark/snippets/project_button.qss
|
|
110
|
+
- app/styles/themes/dark/snippets/project_button_active.qss
|
|
111
|
+
- app/styles/themes/dark/snippets/project_sidebar_heading.qss
|
|
112
|
+
- app/styles/themes/dark/snippets/project_sidebar_logo.qss
|
|
113
|
+
- app/styles/themes/dark/snippets/project_sidebar_panel.qss
|
|
114
|
+
- app/styles/themes/dark/snippets/tracker_clock.qss
|
|
115
|
+
- app/styles/themes/dark/snippets/tracker_input.qss
|
|
116
|
+
- app/styles/themes/dark/snippets/tracker_project_label.qss
|
|
117
|
+
- app/styles/themes/dark/snippets/tracker_row.qss
|
|
118
|
+
- app/styles/themes/dark/snippets/tracker_summary.qss
|
|
119
|
+
- app/styles/themes/dark/snippets/tracker_timer.qss
|
|
120
|
+
- app/styles/themes/dark/snippets/tracker_title.qss
|
|
121
|
+
- app/styles/themes/dark/snippets/tracker_topbar.qss
|
|
122
|
+
- app/styles/themes/dark/tracker_controls.qss
|
|
123
|
+
- app/styles/themes/light/application.qss
|
|
124
|
+
- app/styles/themes/light/entries_list.qss
|
|
125
|
+
- app/styles/themes/light/project_sidebar.qss
|
|
126
|
+
- app/styles/themes/light/snippets/app_background.qss
|
|
127
|
+
- app/styles/themes/light/snippets/button_ghost.qss
|
|
128
|
+
- app/styles/themes/light/snippets/button_start.qss
|
|
129
|
+
- app/styles/themes/light/snippets/button_stop.qss
|
|
130
|
+
- app/styles/themes/light/snippets/entries_host.qss
|
|
131
|
+
- app/styles/themes/light/snippets/entries_scroll.qss
|
|
132
|
+
- app/styles/themes/light/snippets/entry_row_day.qss
|
|
133
|
+
- app/styles/themes/light/snippets/entry_row_detail.qss
|
|
134
|
+
- app/styles/themes/light/snippets/entry_row_project.qss
|
|
135
|
+
- app/styles/themes/light/snippets/project_button.qss
|
|
136
|
+
- app/styles/themes/light/snippets/project_button_active.qss
|
|
137
|
+
- app/styles/themes/light/snippets/project_sidebar_heading.qss
|
|
138
|
+
- app/styles/themes/light/snippets/project_sidebar_logo.qss
|
|
139
|
+
- app/styles/themes/light/snippets/project_sidebar_panel.qss
|
|
140
|
+
- app/styles/themes/light/snippets/tracker_clock.qss
|
|
141
|
+
- app/styles/themes/light/snippets/tracker_input.qss
|
|
142
|
+
- app/styles/themes/light/snippets/tracker_project_label.qss
|
|
143
|
+
- app/styles/themes/light/snippets/tracker_row.qss
|
|
144
|
+
- app/styles/themes/light/snippets/tracker_summary.qss
|
|
145
|
+
- app/styles/themes/light/snippets/tracker_timer.qss
|
|
146
|
+
- app/styles/themes/light/snippets/tracker_title.qss
|
|
147
|
+
- app/styles/themes/light/snippets/tracker_topbar.qss
|
|
148
|
+
- app/styles/themes/light/tracker_controls.qss
|
|
149
|
+
- app/view_models/entry_nodes_builder.rb
|
|
150
|
+
- app/view_models/main_view_model.rb
|
|
151
|
+
- app/view_models/main_view_model_archive_mode_helpers.rb
|
|
152
|
+
- app/view_models/main_view_model_entry_note_helpers.rb
|
|
153
|
+
- app/view_models/main_view_model_entry_task_helpers.rb
|
|
154
|
+
- app/view_models/main_view_model_entry_time_helpers.rb
|
|
155
|
+
- app/view_models/main_view_model_sheet_helpers.rb
|
|
156
|
+
- app/view_models/main_view_model_task_filter_helpers.rb
|
|
157
|
+
- app/view_models/main_view_model_time_range_filter_helpers.rb
|
|
158
|
+
- app/views/main_window.rb
|
|
159
|
+
- app/views/main_window_layout_builder.rb
|
|
160
|
+
- app/views/main_window_runtime.rb
|
|
161
|
+
- app/views/main_window_runtime_archive_helpers.rb
|
|
162
|
+
- app/views/main_window_runtime_entry_task_helpers.rb
|
|
163
|
+
- app/views/main_window_runtime_entry_time_helpers.rb
|
|
164
|
+
- app/views/main_window_runtime_key_helpers.rb
|
|
165
|
+
- app/views/main_window_runtime_render_helpers.rb
|
|
166
|
+
- app/views/main_window_splitter_toggle_bootstrap_helpers.rb
|
|
167
|
+
- app/views/main_window_splitter_toggle_helpers.rb
|
|
168
|
+
- app/views/main_window_splitter_toggle_hover_helpers.rb
|
|
169
|
+
- app/views/main_window_splitter_toggle_layout_helpers.rb
|
|
170
|
+
- app/views/main_window_ui_helpers.rb
|
|
171
|
+
- app/views/window_icon_loader.rb
|
|
172
|
+
- bin/qtimetrap
|
|
173
|
+
- config/application.rb
|
|
174
|
+
- config/environments/development.rb
|
|
175
|
+
- config/environments/production.rb
|
|
176
|
+
- config/initializers/theme.rb
|
|
177
|
+
- config/initializers/timetrap.rb
|
|
178
|
+
- lib/qtimetrap.rb
|
|
179
|
+
- lib/qtimetrap/configuration.rb
|
|
180
|
+
- lib/qtimetrap/container.rb
|
|
181
|
+
- lib/qtimetrap/version.rb
|
|
182
|
+
homepage: https://github.com/CyJimmy264/qtimetrap
|
|
183
|
+
licenses:
|
|
184
|
+
- BSD-2-Clause
|
|
185
|
+
metadata:
|
|
186
|
+
source_code_uri: https://github.com/CyJimmy264/qtimetrap
|
|
187
|
+
changelog_uri: https://github.com/CyJimmy264/qtimetrap/releases
|
|
188
|
+
bug_tracker_uri: https://github.com/CyJimmy264/qtimetrap/issues
|
|
189
|
+
rubygems_mfa_required: 'true'
|
|
190
|
+
rdoc_options: []
|
|
191
|
+
require_paths:
|
|
192
|
+
- lib
|
|
193
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
|
+
requirements:
|
|
195
|
+
- - ">="
|
|
196
|
+
- !ruby/object:Gem::Version
|
|
197
|
+
version: '3.2'
|
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
|
+
requirements:
|
|
200
|
+
- - ">="
|
|
201
|
+
- !ruby/object:Gem::Version
|
|
202
|
+
version: '0'
|
|
203
|
+
requirements: []
|
|
204
|
+
rubygems_version: 3.6.7
|
|
205
|
+
specification_version: 4
|
|
206
|
+
summary: Desktop Timetrap UI on Qt
|
|
207
|
+
test_files: []
|