aboutsummaryrefslogtreecommitdiffstats
path: root/tagit/windows
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2023-03-05 19:17:00 +0100
committerMatthias Baumgartner <dev@igsor.net>2023-03-05 19:17:00 +0100
commit5a325565f917c8b1d233d8e6373756c253400909 (patch)
treee6e0b475c7ab5c6a7ff4f0ea7ad1b08cecf05e68 /tagit/windows
parente1e77797454ac747b293f589d8f2e0243173a419 (diff)
parent98e567933723c59d1d97b3a85e649cfdce514676 (diff)
downloadtagit-0.23.03.tar.gz
tagit-0.23.03.tar.bz2
tagit-0.23.03.zip
Merge branch 'develop'v0.23.03
Diffstat (limited to 'tagit/windows')
-rw-r--r--tagit/windows/__init__.py10
-rw-r--r--tagit/windows/desktop.kv107
-rw-r--r--tagit/windows/desktop.py151
3 files changed, 268 insertions, 0 deletions
diff --git a/tagit/windows/__init__.py b/tagit/windows/__init__.py
new file mode 100644
index 0000000..c3ec3c0
--- /dev/null
+++ b/tagit/windows/__init__.py
@@ -0,0 +1,10 @@
+"""
+
+Part of the tagit module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# inner-module imports
+from .desktop import MainWindow
+
+## EOF ##
diff --git a/tagit/windows/desktop.kv b/tagit/windows/desktop.kv
new file mode 100644
index 0000000..d2ca0e7
--- /dev/null
+++ b/tagit/windows/desktop.kv
@@ -0,0 +1,107 @@
+#:import TileDecorationRoundedBorder tagit.tiles.decoration.TileDecorationRoundedBorder
+#:import TileDecorationBorder tagit.tiles.decoration.TileDecorationBorder
+#:import TileDecorationFilledRectangle tagit.tiles.decoration.TileDecorationFilledRectangle
+
+<HGuide@Widget>:
+
+
+<MainWindow>:
+ # main content
+ # required by most tiles and actions
+ browser: browser
+ filter: filter
+ status: status
+ # required by Menu
+ context: context
+
+ BoxLayout:
+ orientation: 'vertical'
+
+ Widget:
+ height: 5
+ size_hint: 1, None
+
+ Filter:
+ id: filter
+ root: root
+ size_hint: 1, None
+ height: 40
+
+ HGuide:
+ height: 20
+ size_hint: 1, None
+
+ Widget: # spacer
+ height: 20
+ size_hint: 1, None
+
+ BoxLayout:
+ orientation: 'horizontal'
+
+ ButtonDock: # one column of buttons on the left
+ root: root
+ orientation: 'lr-tb'
+ # one column of buttons
+ width: 1*30 + 2*10
+ name: 'sidebar_left'
+ spacing: 10
+ padding: 10
+ size_hint: None, None
+ button_height: 30
+ button_show: 'image',
+ # adjust height automatically to content
+ height: self.minimum_height
+ pos_hint: {'center_y': 0.5}
+
+ Widget: # spacer
+ width: 20 # ButtonDock already has a space of 10px
+ size_hint: None, 1
+
+ Browser: # browsing space
+ id: browser
+ root: root
+ size_hint: 1, 1
+
+ Widget: # spacer
+ width: 30
+ size_hint: None, 1
+
+ TileDock: # context info to the right
+ root: root
+ name: 'sidebar_right'
+ decoration: TileDecorationRoundedBorder
+ visible: True
+ cols: 1
+ rows: 1
+ width: 220
+ size_hint: None, 0.5
+ pos_hint: {'center_y': 0.5}
+
+ Widget: # spacer
+ height: 20
+ size_hint: 1, None
+
+ HGuide:
+ height: 20
+ size_hint: 1, None
+
+ Status:
+ id: status
+ root: root
+ size_hint: 1, None
+ height: 30
+
+ Context: # context menu
+ id: context
+ root: root
+ cancel_handler_widget: root
+ bounding_box_widget: root
+ name: 'context'
+
+ KeybindDock:
+ # key-only actions
+ root: root
+ size_hint: None, None
+ size: 0, 0
+
+## EOF ##
diff --git a/tagit/windows/desktop.py b/tagit/windows/desktop.py
new file mode 100644
index 0000000..2c087b2
--- /dev/null
+++ b/tagit/windows/desktop.py
@@ -0,0 +1,151 @@
+"""Main container of the tagit UI.
+
+Part of the tagit module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+
+"""
+# standard imports
+import logging
+import os
+import typing
+
+# kivy imports
+from kivy.clock import Clock
+from kivy.lang import Builder
+from kivy.uix.floatlayout import FloatLayout
+import kivy.properties as kp
+
+# import Image and Loader to overwrite their caches later on
+from kivy.cache import Cache
+from kivy.loader import Loader
+from kivy.resources import resource_find
+
+# tagit imports
+from tagit import actions, config, dialogues
+from tagit.widgets.browser import Browser
+from tagit.widgets.context import Context
+from tagit.widgets.dock import TileDock, ButtonDock, KeybindDock
+from tagit.widgets.filter import Filter
+from tagit.widgets.keyboard import Keyboard
+from tagit.widgets.session import Session
+from tagit.widgets.status import Status
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'KIVY_IMAGE_CACHE_SIZE',
+ 'KIVY_IMAGE_CACHE_TIMEOUT',
+ 'MainWindow',
+ )
+
+
+## code ##
+
+logger = logging.getLogger(__name__)
+
+# load kv
+Builder.load_file(os.path.join(os.path.dirname(__file__), 'desktop.kv'))
+# load styles
+Builder.load_file(resource_find('default/style.kv'))
+
+# classes
+class MainWindow(FloatLayout):
+ """A self-contained user interface for desktop usage.
+ See `tagit.apps.gui` for an example of how to invoke it.
+ """
+
+ keys = kp.ObjectProperty(None)
+
+ # unnecessary but nicely explicit
+ browser = kp.ObjectProperty(None)
+ filter = kp.ObjectProperty(None)
+ keytriggers = kp.ObjectProperty(None)
+
+ # FIXME: log actions and and replay them
+ action_log = kp.ListProperty()
+
+ def __init__ (self, cfg, stor, log, **kwargs):
+ # initialize the session
+ self._session = Session(cfg, stor, log)
+ # initialize key-only actions
+ self.keys = Keyboard()
+
+ # initialize the cache
+ cache_size = max(0, cfg('ui', 'standalone', 'browser', 'cache_size'))
+ cache_size = cache_size if cache_size > 0 else None
+ cache_timeout = max(0, cfg('ui', 'standalone', 'browser', 'cache_timeout'))
+ cache_timeout = cache_timeout if cache_timeout > 0 else None
+ Cache.register('kv.loader', limit=cache_size, timeout=cache_timeout)
+
+ # initialize the widget
+ super(MainWindow, self).__init__(**kwargs)
+
+ # bind pre-close checks
+ from kivy.core.window import Window
+ Window.bind(on_request_close=self.on_request_close)
+ Window.size = tuple(cfg('ui', 'standalone', 'window_size'))
+ if cfg('ui', 'standalone', 'maximize'):
+ Window.maximize()
+
+
+ ## properties
+
+ @property
+ def session(self):
+ return self._session
+
+ def trigger(self, action, *args, **kwargs):
+ """Trigger an action once."""
+ actions.ActionBuilder().get(action).single_shot(self, *args, **kwargs)
+
+
+ ## startup and shutdown
+
+ def on_startup(self):
+ # run script
+ for args in self.session.cfg('session', 'script'):
+ if isinstance(args, str):
+ cmd, args = args, []
+ else:
+ cmd = args.pop(0)
+ Clock.schedule_once(
+ lambda dt, cmd=cmd, args=args: self.trigger(cmd, *args),
+ self.session.cfg('session', 'script_delay'))
+
+ # FIXME: mb/port: debugging only
+ #return
+ #Clock.schedule_once(lambda dt: self.trigger('Search'), 0)
+ #Clock.schedule_once(lambda dt: self.trigger('ZoomOut'), 0)
+ #Clock.schedule_once(lambda dt: self.trigger('ZoomOut'), 0)
+ #from kivy.app import App
+ #App.get_running_app().stop()
+
+ def on_request_close(self, *args):
+ #with open('.action_history', 'a') as ofile:
+ # for itm in self.action_log:
+ # ofile.write(f'{itm}\n')
+ #App.get_running_app().stop() # FIXME: mb/port: from CloseSessionAndExit
+ return False
+
+
+## config ##
+
+config.declare(('session', 'script'), config.List(config.Any()), [],
+ __name__, 'start script', 'Actions to run after startup. Intended for testing.')
+
+config.declare(('session', 'script_delay'), config.Unsigned(), 0,
+ __name__, 'script delay', 'Start script execution delay in seconds.')
+
+config.declare(('ui', 'standalone', 'maximize'), config.Bool(), False,
+ __name__, 'Window maximization', 'Maximize the window upon startup.')
+
+config.declare(('ui', 'standalone', 'window_size'), config.List(config.Unsigned()), (1024, 768),
+ __name__, 'Wndow size', 'Set the window size upon startup.')
+
+config.declare(('ui', 'standalone', 'browser', 'cache_size'), config.Unsigned(), 1000,
+ __name__, 'Cache size', 'Number of preview images that are held in the cache. Should be high or zero if memory is not an issue. Set to a small value to preserve memory, but should be at least the most common page size. It is advised to set a value in accordance with `ui.standalone.browser.cache_items`. If zero, no limit applies.')
+
+config.declare(('ui', 'standalone', 'browser', 'cache_timeout'), config.Unsigned(), 0,
+ __name__, 'Cache timeout', 'Number of seconds until cached items are discarded. Should be high or zero if memory is not an issue. Set it to a small value to preserve memory when browsing through many images. If zero, no limit applies. Specify in seconds.')
+
+## EOF ##