diff libervia/desktop_kivy/core/file_chooser.py @ 514:d78728d7fd6a

plugin wid calls, core: implements WebRTC DataChannel file transfer: - Add a new "file" icon in call UI to send a file via WebRTC. - Handle new preflight mechanism, and WebRTC file transfer. - Native file chooser handling has been moved to new `core.file_chooser` module, and now supports "save" and "dir" modes (based on `plyer`). rel 442
author Goffi <goffi@goffi.org>
date Sat, 06 Apr 2024 13:37:27 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libervia/desktop_kivy/core/file_chooser.py	Sat Apr 06 13:37:27 2024 +0200
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+
+# Libervia Desktop-Kivy
+# Copyright (C) 2016-2024 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import asyncio
+import threading
+
+from libervia.backend.core import exceptions
+from libervia.backend.core.i18n import _
+
+from kivy import properties
+from kivy.clock import Clock
+from kivy.event import EventDispatcher
+from plyer import filechooser, storagepath
+
+
+class FileChooser(EventDispatcher):
+    callback = properties.ObjectProperty()
+    cancel_cb = properties.ObjectProperty()
+    native_filechooser = True
+    default_path = properties.StringProperty(storagepath.get_home_dir())
+    mode = properties.OptionProperty("open", options=["open", "save", "dir"])
+    title = properties.StringProperty(_("Please select a file to upload"))
+
+    def open(self):
+        """Open the file selection dialog in a separate thread"""
+        thread = threading.Thread(target=self._native_file_chooser)
+        thread.start()
+
+    @classmethod
+    async def a_open(cls, **kwargs) -> str | None:
+        """Open the file selection dialog asynchronously
+
+        @return: The path of the selected file
+            None if the dialog has been cancelled.
+        """
+        future = asyncio.Future()
+
+        def on_success(file_path):
+            if not future.done():
+                future.set_result(file_path)
+
+        def on_cancel(__):
+            if not future.done():
+                future.set_result(None)
+
+        file_chooser = cls(
+            **kwargs,
+            callback=on_success,
+            cancel_cb=on_cancel
+        )
+
+        file_chooser.open()
+
+        return await future
+
+    def _native_file_chooser(self, *args, **kwargs):
+        match self.mode:
+            case "open":
+                method = filechooser.open_file
+            case "save":
+                method = filechooser.save_file
+            case "dir":
+                method = filechooser.choose_dir
+            case _:
+                raise exceptions.InternalError("Should never be reached.")
+        files = method(
+            title=self.title, path=self.default_path, multiple=False, preview=True
+        )
+        # we want to leave the thread when calling on_files, so we use Clock
+        Clock.schedule_once(lambda *args: self.on_files(files=files), 0)
+
+    def on_files(self, files):
+        if files:
+            self.callback(files[0])
+        else:
+            self.cancel_cb(self)