Compare commits

..

20 Commits

Author SHA1 Message Date
itdominator cca007db76 Added VTE widget; css changes; format cleanup 2024-11-08 22:46:07 -06:00
itdominator 33c0827ca2 Moved args info to settings and restructured calls 2024-10-20 16:03:19 -05:00
itdominator f2b33066af Added stronger typing in settings; logging loading times; css changes to transparency 2024-10-20 15:20:18 -05:00
itdominator fafc1a985f Updated __init__ files; theming css changes, other 2024-10-14 21:57:18 -05:00
itdominator e2e9dc8c1f made default hiding transparency controls easier with restructured show calls 2024-08-31 22:27:11 -05:00
itdominator 25b6b5305b update readme 2024-07-26 19:53:04 -05:00
itdominator 62debf9ece extending plugins to load pre or post app start 2024-06-29 21:24:57 -05:00
itdominator cc5966dab2 Added status icon for system trays 2024-05-06 19:11:18 -05:00
itdominator e82e4fb1eb Updated a requirement 2024-05-03 01:11:52 -05:00
itdominator 48bac7e791 Added a requirement; added a debug handler 2024-05-03 01:08:49 -05:00
itdominator b78fac0aa5 Fixed depricated exception type; fixed siguser type 2024-03-25 22:48:05 -05:00
itdominator bdb9c157f7 moved controls to dir; added open files button example; css transparency changes 2024-03-16 22:36:04 -05:00
itdominator a0f32a7c00 app_name --> APP_NAME 2024-02-29 18:50:34 -06:00
itdominator b1096055b7 Renamed settings folder; hyphenated event names 2024-02-25 16:19:14 -06:00
itdominator ea62eb280c Added builder wrapper due to plugin setup 2024-02-24 21:55:40 -06:00
itdominator cc8f62776d Fixed bindings; added bindings 2024-02-23 23:45:13 -06:00
itdominator 2389f1d414 moved db stuff to own folder 2024-02-21 22:15:25 -06:00
itdominator d81bf3815a Removed logging item 2024-02-21 20:34:42 -06:00
itdominator 1695f5bc5c Overode method to add debug logging 2024-02-16 20:43:09 -06:00
itdominator 1245951da9 Cleanup of starting file collection 2024-02-16 20:13:36 -06:00
65 changed files with 772 additions and 363 deletions

View File

@ -3,11 +3,13 @@ A template project for Python with Gtk applications.
### Requirements
* PyGObject (Gtk introspection library)
* pygobject-stubs (For actually getting pylsp or python-language-server to auto complete in LSPs. Do if GTK3 --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2)
* pyxdg (Desktop ".desktop" file parser)
* setproctitle (Define process title to search and kill more easily)
* sqlmodel (SQL databases and is powered by Pydantic and SQLAlchemy)
### Note
* pyrightconfig.json can prompt IDEs that use pyright lsp on where imports are located- look at venvPath and venv. "venvPath" is parent path of "venv" where "venv" is just the name of the folder under the parent path that is the python created venv.
* Move respetive sub folder content under user_config to the same places in Linux. Though, user/share/<app name> can go to ~/.config folder if prefered.
* In additiion, place the plugins folder in the same app folder you moved to /usr/share/<app name> or ~/.config/<app name> .
There are a "\<change_me\>" strings and files that need to be set according to your app's name located at:

View File

@ -1,2 +1,31 @@
### Note
Copy the example and rename it to your desired name. The Main class and passed in arguments are required. You don't necessarily need to use the passed in socket_id or event_system.
Copy the example and rename it to your desired name. Plugins define a ui target slot with the 'ui_target' requests data but don't have to if not directly interacted with.
Plugins must have a run method defined; though, you do not need to necessarily do anything within it. The run method implies that the passed in event system or other data is ready for the plugin to use.
### Manifest Example (All are required even if empty.)
```
class Manifest:
name: str = "Example Plugin"
author: str = "John Doe"
version: str = "0.0.1"
support: str = ""
requests: {} = {
'pass_ui_objects': ["plugin_control_list"],
'pass_events': "true",
'bind_keys': []
}
pre_launch: bool = False
```
### Requests
```
requests: {} = {
'pass_events': "true", # If empty or not present will be ignored.
"pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin.
'bind_keys': [f"{name}||send_message:<Control>f"],
f"{name}||do_save:<Control>s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.
}
```

13
pyrightconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"reportUndefinedVariable": false,
"reportUnusedVariable": false,
"reportUnusedImport": true,
"reportDuplicateImport": true,
"executionEnvironments": [
{
"root": "./src/versions/solarfm-0.0.1/solarfm"
}
],
"venvPath": ".",
"venv": ".venv"
}

View File

@ -1,4 +1,7 @@
PyGObject
pyxdg
setproctitle
sqlmodel
PyGObject==3.40.1
pygobject-stubs --no-cache-dir --config-settings=config=Gtk3,Gdk3,Soup2
setproctitle==1.2.2
pyxdg==0.27
psutil==5.8.0
pycryptodome==3.20.0
sqlmodel==0.0.19

View File

@ -11,7 +11,7 @@ from libs.event_system import EventSystem
from libs.endpoint_registry import EndpointRegistry
from libs.keybindings import Keybindings
from libs.logger import Logger
from libs.settings_manager.manager import SettingsManager
from libs.settings.manager import SettingsManager
@ -35,7 +35,7 @@ def daemon_threaded_wrapper(fn):
# NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()})
builtins.app_name = "<change_me>"
builtins.APP_NAME = "<change_me>"
builtins.keybindings = Keybindings()
builtins.event_system = EventSystem()
@ -46,9 +46,11 @@ builtins.settings_manager = SettingsManager()
settings_manager.load_settings()
builtins.settings = settings_manager.settings
builtins.logger = Logger(settings_manager.get_home_config_path(), \
_ch_log_lvl=settings.debugging.ch_log_lvl, \
_fh_log_lvl=settings.debugging.fh_log_lvl).get_logger()
builtins.logger = Logger(
settings_manager.get_home_config_path(), \
_ch_log_lvl = settings.debugging.ch_log_lvl, \
_fh_log_lvl = settings.debugging.fh_log_lvl
).get_logger()
builtins.threaded = threaded_wrapper
builtins.daemon_threaded = daemon_threaded_wrapper
@ -60,6 +62,6 @@ def custom_except_hook(exc_type, exc_value, exc_traceback):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
logger.error("Uncaught exception", exc_info = (exc_type, exc_value, exc_traceback))
sys.excepthook = custom_except_hook

View File

@ -1,3 +1,3 @@
"""
Start of package.
Src Package.
"""

View File

@ -17,8 +17,9 @@ from app import Application
def main(args, unknownargs):
setproctitle(f'{app_name}')
def main():
setproctitle(f'{APP_NAME}')
settings_manager.set_start_load_time()
if args.debug == "true":
settings_manager.set_debug(True)
@ -27,7 +28,7 @@ def main(args, unknownargs):
settings_manager.set_trace_debug(True)
settings_manager.do_dirty_start_check()
Application(args, unknownargs)
Application()
@ -36,19 +37,20 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser()
# Add long and short arguments
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
parser.add_argument("--debug", "-d", default = "false", help = "Do extra console messaging.")
parser.add_argument("--trace-debug", "-td", default = "false", help = "Disable saves, ignore IPC lock, do extra console messaging.")
parser.add_argument("--no-plugins", "-np", default = "false", help = "Do not load plugins.")
parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.")
parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
parser.add_argument("--new-tab", "-nt", default = "false", help = "Opens a 'New Tab' if a handler is set for it.")
parser.add_argument("--file", "-f", default = "default", help = "JUST SOME FILE ARG.")
# Read arguments (If any...)
args, unknownargs = parser.parse_known_args()
settings_manager.set_starting_args( args, unknownargs )
try:
faulthandler.enable() # For better debug info
main(args, unknownargs)
main()
except Exception as e:
traceback.print_exc()
quit()

View File

@ -19,27 +19,29 @@ class AppLaunchException(Exception):
class Application:
""" docstring for Application. """
def __init__(self, args, unknownargs):
def __init__(self):
super(Application, self).__init__()
if not settings_manager.is_trace_debug():
self.load_ipc(args, unknownargs)
self.load_ipc()
self.setup_debug_hook()
Window(args, unknownargs).main()
Window().main()
def load_ipc(self, args, unknownargs):
def load_ipc(self):
args, \
unknownargs = settings_manager.get_starting_args()
ipc_server = IPCServer()
self.ipc_realization_check(ipc_server)
self.ipc_realization_check(ipc_server)
if not ipc_server.is_ipc_alive:
for arg in unknownargs + [args.new_tab,]:
if os.path.isfile(arg):
message = f"FILE|{arg}"
ipc_server.send_ipc_message(message)
raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
raise AppLaunchException(f"{APP_NAME} IPC Server Exists: Have sent path(s) to it and closing...")
def ipc_realization_check(self, ipc_server):
try:
@ -56,7 +58,7 @@ class Application:
try:
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
signal.signal(
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"),
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
debug_signal_handler
)
except ValueError:

View File

@ -1,3 +1,3 @@
"""
Core Module
Core Package
"""

View File

@ -0,0 +1,33 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class BuilderWrapper(Gtk.Builder):
"""docstring for BuilderWrapper."""
def __init__(self):
super(BuilderWrapper, self).__init__()
self.objects = {}
def get_object(self, id: str, use_gtk: bool = True) -> any:
if not use_gtk:
return self.objects[id]
return super(BuilderWrapper, self).get_object(id)
def expose_object(self, id: str, object: any, use_gtk: bool = True) -> None:
if not use_gtk:
self.objects[id] = object
else:
super(BuilderWrapper, self).expose_object(id, object)
def dereference_object(self, id: str) -> None:
del self.objects[id]

View File

@ -1,3 +1,3 @@
"""
Containers Module
Containers Package
"""

View File

@ -22,7 +22,7 @@ class BaseContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show_all()
self.show()
def _setup_styling(self):
@ -33,8 +33,8 @@ class BaseContainer(Gtk.Box):
...
def _subscribe_to_events(self):
event_system.subscribe("update_transparency", self._update_transparency)
event_system.subscribe("remove_transparency", self._remove_transparency)
event_system.subscribe("update-transparency", self._update_transparency)
event_system.subscribe("remove-transparency", self._remove_transparency)
def _load_widgets(self):
self.add(HeaderContainer())

View File

@ -23,7 +23,7 @@ class BodyContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show_all()
self.show()
def _setup_styling(self):

View File

@ -21,6 +21,8 @@ class CenterContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
@ -31,7 +33,6 @@ class CenterContainer(Gtk.Box):
...
def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
...
def _load_widgets(self):
@ -40,6 +41,9 @@ class CenterContainer(Gtk.Box):
button.connect("clicked", self._hello_world)
button.show()
glade_box.show()
self.add(button)
self.add(glade_box)
self.add( WebkitUI() )

View File

@ -6,7 +6,8 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from ..widgets.transparency_scale import TransparencyScale
from ..widgets.controls.open_files_button import OpenFilesButton
from ..widgets.controls.transparency_scale import TransparencyScale
@ -32,15 +33,19 @@ class HeaderContainer(Gtk.Box):
...
def _subscribe_to_events(self):
...
event_system.subscribe("tggl-top-main-menubar", self.tggl_top_main_menubar)
def _load_widgets(self):
button = Gtk.Button(label = "Interactive Debug")
button.connect("clicked", self._interactive_debug)
self.add(TransparencyScale())
self.add( OpenFilesButton() )
self.add( TransparencyScale() )
self.add(button)
def _interactive_debug(self, widget = None, eve = None):
event_system.emit("load_interactive_debug")
event_system.emit("load-interactive-debug")
def tggl_top_main_menubar(self):
self.hide() if self.is_visible() else self.show_all()

View File

@ -18,6 +18,8 @@ class LeftContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
@ -28,7 +30,6 @@ class LeftContainer(Gtk.Box):
...
def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
...
def _load_widgets(self):

View File

@ -6,6 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from ..widgets.vte_widget import VteWidget
@ -18,6 +19,8 @@ class RightContainer(Gtk.Box):
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_orientation(Gtk.Orientation.VERTICAL)
@ -28,8 +31,8 @@ class RightContainer(Gtk.Box):
...
def _subscribe_to_events(self):
# event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
...
def _load_widgets(self):
...
vte_widget = VteWidget()
self.add( vte_widget )

View File

@ -1,3 +1,3 @@
"""
Controllers Module
Controllers Package
"""

View File

@ -1,5 +1,4 @@
# Python imports
import os
# Lib imports
import gi
@ -18,27 +17,18 @@ from .bridge_controller import BridgeController
class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
def __init__(self, args, unknownargs):
self.setup_controller_data()
def __init__(self):
self._setup_controller_data()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_controllers()
if args.no_plugins == "false":
self.plugins.launch_plugins()
for arg in unknownargs + [args.new_tab,]:
if os.path.isfile(arg):
message = f"FILE|{arg}"
event_system.emit("post_file_to_ipc", message)
if os.path.isdir(arg):
message = f"DIR|{arg}"
event_system.emit("post_file_to_ipc", message)
self._load_plugins_and_files()
logger.info(f"Made it past {self.__class__} loading...")
settings_manager.set_end_load_time()
settings_manager.log_load_time()
def _setup_styling(self):
@ -50,20 +40,30 @@ class BaseController(IPCSignalsMixin, KeyboardSignalsMixin, BaseControllerData):
self.window.connect("key-release-event", self.on_global_key_release_controller)
def _subscribe_to_events(self):
event_system.subscribe("shutting_down", lambda: print("Shutting down..."))
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
event_system.subscribe("handle_dir_from_ipc", self.handle_dir_from_ipc)
event_system.subscribe("tggl_top_main_menubar", self._tggl_top_main_menubar)
event_system.subscribe("shutting-down", lambda: print("Shutting down..."))
event_system.subscribe("handle-file-from-ipc", self.handle_file_from_ipc)
event_system.subscribe("handle-dir-from-ipc", self.handle_dir_from_ipc)
event_system.subscribe("tggl-top-main-menubar", self._tggl_top_main_menubar)
def _load_controllers(self):
BridgeController()
def _load_plugins_and_files(self):
args, unknownargs = settings_manager.get_starting_args()
if args.no_plugins == "false":
self.plugins_controller.pre_launch_plugins()
if args.no_plugins == "false":
self.plugins_controller.post_launch_plugins()
for file in settings_manager.get_starting_files():
event_system.emit("post-file-to-ipc", file)
def _tggl_top_main_menubar(self):
logger.debug("_tggl_top_main_menubar > stub...")
def setup_builder_and_container(self):
self.builder = Gtk.Builder()
self.builder.add_from_file(settings_manager.get_glade_file())
def _load_glade_file(self):
self.builder.add_from_file( settings_manager.get_glade_file() )
self.builder.expose_object("main_window", self.window)
settings_manager.set_builder(self.builder)

View File

@ -7,23 +7,44 @@ from shutil import which
# Application imports
from plugins.plugins_controller import PluginsController
from ..builder_wrapper import BuilderWrapper
class BaseControllerData:
''' BaseControllerData contains most of the state of the app at ay given time. It also has some support methods. '''
def setup_controller_data(self) -> None:
def _setup_controller_data(self) -> None:
self.window = settings_manager.get_main_window()
self.builder = None
self.builder = BuilderWrapper()
self.plugins_controller = PluginsController()
self.base_container = None
self.was_midified_key = False
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
self.setup_builder_and_container()
self.plugins = PluginsController()
self._collect_files_dirs()
self._load_glade_file()
def _collect_files_dirs(self):
args, \
unknownargs = settings_manager.get_starting_args()
files = []
for arg in unknownargs + [args.new_tab,]:
if os.path.isdir( arg.replace("file://", "") ):
files.append( f"DIR|{arg.replace('file://', '')}" )
# NOTE: If passing line number with file split against :
if os.path.isfile( arg.replace("file://", "").split(":")[0] ):
files.append( f"FILE|{arg.replace('file://', '')}" )
if len(files) > 0:
settings_manager.set_is_starting_with_file(True)
settings_manager.set_starting_files(files)
def get_base_container(self):
return self.base_container

View File

@ -20,19 +20,19 @@ class BridgeController:
...
def _subscribe_to_events(self):
event_system.subscribe("handle_bridge_event", self.handle_bridge_event)
event_system.subscribe("handle-bridge-event", self.handle_bridge_event)
def handle_bridge_event(self, event):
match event.topic:
case "save":
event_system.emit(f"handle_file_event_{event.originator}", (event,))
event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "close":
event_system.emit(f"handle_file_event_{event.originator}", (event,))
event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "load_buffer":
event_system.emit(f"handle_file_event_{event.originator}", (event,))
event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "load_file":
event_system.emit(f"handle_file_event_{event.originator}", (event,))
event_system.emit(f"handle-file-event-{event.originator}", (event,))
case "alert":
content = base64.b64decode( event.content.encode() ).decode("utf-8")
logger.info(f"\nMessage Topic: {event.topic}\nMessage Content: {content}")

View File

@ -1,3 +1,3 @@
"""
Widgets Module
Widgets Package
"""

View File

@ -0,0 +1,3 @@
"""
Widgets.Controls Package
"""

View File

@ -0,0 +1,86 @@
# Python imports
import os
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Gio
# Application imports
class OpenFilesButton(Gtk.Button):
"""docstring for OpenFilesButton."""
def __init__(self):
super(OpenFilesButton, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
self.set_label("Open File(s)...")
self.set_image( Gtk.Image.new_from_icon_name("gtk-open", 4) )
self.set_always_show_image(True)
self.set_image_position(1) # Left - 0, Right = 1
self.set_hexpand(False)
def _setup_signals(self):
self.connect("button-release-event", self._open_files)
def _subscribe_to_events(self):
event_system.subscribe("open_files", self._open_files)
def _load_widgets(self):
...
def _open_files(self, widget = None, eve = None, gfile = None):
start_dir = None
_gfiles = []
if gfile and gfile.query_exists():
start_dir = gfile.get_parent()
chooser = Gtk.FileChooserDialog("Open File(s)...", None,
Gtk.FileChooserAction.OPEN,
(
Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN,
Gtk.ResponseType.OK
)
)
chooser.set_select_multiple(True)
try:
folder = widget.get_current_file().get_parent() if not start_dir else start_dir
chooser.set_current_folder( folder.get_path() )
except Exception as e:
...
response = chooser.run()
if not response == Gtk.ResponseType.OK:
chooser.destroy()
return _gfiles
filenames = chooser.get_filenames()
if not filenames:
chooser.destroy()
return _gfiles
for file in filenames:
path = file if os.path.isabs(file) else os.path.abspath(file)
_gfiles.append( Gio.File.new_for_path(path) )
chooser.destroy()
logger.debug(_gfiles)
return _gfiles

View File

@ -37,12 +37,12 @@ class TransparencyScale(Gtk.Scale):
def _load_widgets(self):
adjust = self.get_adjustment()
adjust.set_lower(0)
adjust.set_upper(99)
adjust.set_upper(100)
adjust.set_value(settings.theming.transparency)
adjust.set_step_increment(1.0)
def _update_transparency(self, range):
event_system.emit("remove_transparency")
event_system.emit("remove-transparency")
tp = int(range.get_value())
settings.theming.transparency = tp
event_system.emit("update_transparency")
event_system.emit("update-transparency")

View File

@ -0,0 +1,125 @@
# Python imports
import os
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('Vte', '2.91')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Vte
# Application imports
from libs.dto.event import Event
class VteWidgetException(Exception):
...
class VteWidget(Vte.Terminal):
"""
https://stackoverflow.com/questions/60454326/how-to-implement-a-linux-terminal-in-a-pygtk-app-like-vscode-and-pycharm-has
"""
def __init__(self):
super(VteWidget, self).__init__()
self.cd_cmd_prefix = ("cd".encode(), "cd ".encode())
self.dont_process = False
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self._do_session_spawn()
self.show()
def _setup_styling(self):
ctx = self.get_style_context()
ctx.add_class("vte-widget")
self.set_clear_background(False)
self.set_enable_sixel(True)
self.set_cursor_shape( Vte.CursorShape.IBEAM )
def _setup_signals(self):
self.connect("commit", self._commit)
def _subscribe_to_events(self):
event_system.subscribe("update_term_path", self.update_term_path)
def _load_widgets(self):
...
def _do_session_spawn(self):
self.spawn_sync(
Vte.PtyFlags.DEFAULT,
settings_manager.get_home_path(),
["/bin/bash"],
[],
GLib.SpawnFlags.DEFAULT,
None, None,
)
# Note: '-->:' is used as a delimiter to split on to get command actual.
# !!! DO NOT REMOVE UNLESS CODE UPDATED ACCORDINGLY !!!
startup_cmds = [
"env -i /bin/bash --noprofile --norc\n",
"export TERM='xterm-256color'\n",
"export LC_ALL=C\n",
"export XDG_RUNTIME_DIR='/run/user/1000'\n",
"export DISPLAY=:0\n",
f"export XAUTHORITY='{settings_manager.get_home_path()}/.Xauthority'\n",
f"\nexport HOME='{settings_manager.get_home_path()}'\n",
"export PS1='\\h@\\u \\W -->: '\n",
"clear\n"
]
for i in startup_cmds:
self.run_command(i)
def _commit(self, terminal, text, size):
if self.dont_process:
self.dont_process = False
return
if not text.encode() == "\r".encode(): return
text, attributes = self.get_text()
lines = text.strip().splitlines()
command_ran = None
try:
command_ran = lines[-1].split("-->:")[1].strip()
except VteWidgetException as e:
logger.debud(e)
return
if not command_ran[0:3].encode() in self.cd_cmd_prefix:
return
target_path = command_ran.split( command_ran[0:3] )[1]
if target_path in (".", "./"): return
if not target_path:
target_path = settings_manager.get_home_path()
event = Event("pty_path_updated", "", target_path)
event_system.emit("handle_bridge_event", (event,))
def update_term_path(self, fpath):
self.dont_process = True
cmds = [f"cd '{fpath}'\n", "clear\n"]
for i in cmds:
self.run_command(i)
def run_command(self, cmd):
self.feed_child_binary(bytes(cmd, 'utf8'))

View File

@ -1,3 +1,3 @@
"""
WebKit2 UI Module
WebKit2 UI Package
"""

View File

@ -9,17 +9,14 @@ from gi.repository import Gdk
from gi.repository import WebKit2
# Application imports
from libs.data_types import Event
from libs.settings.other.webkit_ui_settings import WebkitUISettings
from libs.dto.event import Event
class WebkitUI(WebKit2.WebView):
def __init__(self):
super(WebkitUI, self).__init__()
# self.get_context().set_sandbox_enabled(False)
self._load_settings()
self._setup_styling()
self._subscribe_to_events()
self._load_view()
@ -27,10 +24,6 @@ class WebkitUI(WebKit2.WebView):
self.show_all()
if settings_manager.is_debug():
inspector = self.get_inspector()
inspector.show()
def _setup_styling(self):
self.set_vexpand(True)
@ -38,7 +31,7 @@ class WebkitUI(WebKit2.WebView):
self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) )
def _subscribe_to_events(self):
event_system.subscribe(f"ui_message", self.ui_message)
event_system.subscribe(f"ui-message", self.ui_message)
def _load_settings(self):
self.set_settings( WebkitUISettings() )
@ -54,7 +47,6 @@ class WebkitUI(WebKit2.WebView):
def _setup_content_manager(self):
content_manager = self.get_user_content_manager()
content_manager.connect("script-message-received", self._process_js_message)
content_manager.register_script_message_handler("backend")
@ -64,45 +56,14 @@ class WebkitUI(WebKit2.WebView):
try:
event = Event( **json.loads(message) )
event_system.emit("handle_bridge_event", (event,))
event_system.emit("handle-bridge-event", (event,))
except Exception as e:
logger.info(e)
def ui_message(self, mtype, message):
def ui_message(self, message, mtype):
command = f"displayMessage('{message}', '{mtype}', '3')"
self.run_javascript(command, None, None)
class WebkitUISettings(WebKit2.Settings):
def __init__(self):
super(WebkitUISettings, self).__init__()
self._set_default_settings()
# Note: Highly insecure setup but most "app" like setup I could think of.
# Audit heavily any scripts/links ran/clicked under this setup!
def _set_default_settings(self):
# self.set_enable_xss_auditor(True)
# self.set_enable_hyperlink_auditing(True)
self.set_enable_xss_auditor(False)
self.set_enable_hyperlink_auditing(False)
self.set_enable_dns_prefetching(False)
self.set_allow_file_access_from_file_urls(True)
self.set_allow_universal_access_from_file_urls(True)
# self.set_enable_java(True)
self.set_enable_page_cache(False)
self.set_enable_offline_web_application_cache(False)
self.set_enable_html5_local_storage(False)
self.set_enable_html5_database(False)
self.set_print_backgrounds(False)
self.set_enable_tabs_to_links(False)
self.set_enable_fullscreen(True)
self.set_enable_developer_extras(True)
self.set_enable_webrtc(True)
self.set_enable_webaudio(True)
self.set_enable_accelerated_2d_canvas(True)
self.set_user_agent(f"Mozilla/5.0 {app_name}")
def run_javascript(self, script, cancellable, callback):
logger.debug(script)
super().run_javascript(script, cancellable, callback)

View File

@ -1,64 +0,0 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('WebKit2', '4.0')
from gi.repository import Gdk
from gi.repository import WebKit2
# Application imports
from libs.settings_manager.other.webkit_ui_settings import WebkitUISettings
class WebkitUI(WebKit2.WebView):
def __init__(self):
super(WebkitUI, self).__init__()
self._setup_styling()
self._subscribe_to_events()
self._load_view()
self._setup_content_manager()
self.show_all()
def _setup_styling(self):
self.set_vexpand(True)
self.set_hexpand(True)
self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) )
def _subscribe_to_events(self):
event_system.subscribe(f"ui_message", self.ui_message)
def _load_settings(self):
self.set_settings( WebkitUISettings() )
def _load_view(self):
path = settings_manager.get_context_path()
data = None
with open(f"{path}/index.html", "r") as f:
data = f.read()
self.load_html(content = data, base_uri = f"file://{path}/")
def _setup_content_manager(self):
content_manager = self.get_user_content_manager()
content_manager.connect("script-message-received", self._process_js_message)
content_manager.register_script_message_handler("backend")
def _process_js_message(self, user_content_manager, js_result):
js_value = js_result.get_js_value()
message = js_value.to_string()
try:
event = Event( **json.loads(message) )
event_system.emit("handle_bridge_event", (event,))
except Exception as e:
logger.info(e)
def ui_message(self, message, mtype):
command = f"displayMessage('{message}', '{mtype}', '3')"
self.run_javascript(command, None, None)

View File

@ -11,6 +11,7 @@ from gi.repository import Gdk
from gi.repository import GLib
# Application imports
from libs.status_icon import StatusIcon
from core.controllers.base_controller import BaseController
@ -23,16 +24,17 @@ class ControllerStartExceptiom(Exception):
class Window(Gtk.ApplicationWindow):
""" docstring for Window. """
def __init__(self, args, unknownargs):
def __init__(self):
super(Window, self).__init__()
settings_manager.set_main_window(self)
self._status_icon = None
self._controller = None
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets(args, unknownargs)
self._load_widgets()
self._set_window_data()
self._set_size_constraints()
@ -41,7 +43,7 @@ class Window(Gtk.ApplicationWindow):
def _setup_styling(self):
self.set_title(f"{app_name}")
self.set_title(f"{APP_NAME}")
self.set_icon_from_file( settings_manager.get_window_icon() )
self.set_gravity(5) # 5 = CENTER
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
@ -58,14 +60,15 @@ class Window(Gtk.ApplicationWindow):
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down)
def _subscribe_to_events(self):
event_system.subscribe("tear_down", self._tear_down)
event_system.subscribe("load_interactive_debug", self._load_interactive_debug)
event_system.subscribe("tear-down", self._tear_down)
event_system.subscribe("load-interactive-debug", self._load_interactive_debug)
def _load_widgets(self, args, unknownargs):
def _load_widgets(self):
if settings_manager.is_debug():
self.set_interactive_debugging(True)
self._controller = BaseController(args, unknownargs)
self._controller = BaseController()
self._status_icon = StatusIcon()
if not self._controller:
raise ControllerStartException("BaseController exited and doesn't exist...")
@ -90,7 +93,7 @@ class Window(Gtk.ApplicationWindow):
if visual and screen.is_composited() and settings.config.make_transparent == 0:
self.set_visual(visual)
self.set_app_paintable(True)
self.connect("draw", self._area_draw)
# self.connect("draw", self._area_draw)
# bind css file
cssProvider = Gtk.CssProvider()
@ -107,17 +110,17 @@ class Window(Gtk.ApplicationWindow):
def _on_focus_in_event(self, widget, event):
event_system.emit("pause_dnd_signals")
event_system.emit("pause-dnd-signals")
def _on_focus_out_event(self, widget, event):
event_system.emit("listen_dnd_signals")
event_system.emit("listen-dnd-signals")
def _load_interactive_debug(self):
self.set_interactive_debugging(True)
def _tear_down(self, widget = None, eve = None):
event_system.emit("shutting_down")
event_system.emit("shutting-down")
size = self.get_size()
pos = self.get_position()

View File

@ -1,3 +1,3 @@
"""
Utils module
Libs Package
"""

View File

@ -1,5 +0,0 @@
"""
Dasta Class module
"""
from .event import Event

6
src/libs/db/__init__.py Normal file
View File

@ -0,0 +1,6 @@
"""
DB Package
"""
from .models import User
from .db import DB

View File

@ -18,7 +18,7 @@ def debug_signal_handler(signal, frame):
rpdb2.start_embedded_debugger("foobar", True, True)
rpdb2.setbreak(depth=1)
return
except StandardError:
except Exception:
...
try:
@ -26,7 +26,7 @@ def debug_signal_handler(signal, frame):
logger.debug("\n\nStarting embedded rconsole debugger...\n\n")
rconsole.spawn_server()
return
except StandardError as ex:
except Exception as ex:
...
try:
@ -34,7 +34,15 @@ def debug_signal_handler(signal, frame):
logger.debug("\n\nStarting PuDB debugger...\n\n")
set_trace(paused = True)
return
except StandardError as ex:
except Exception as ex:
...
try:
import ipdb
logger.debug("\n\nStarting IPDB debugger...\n\n")
ipdb.set_trace()
return
except Exception as ex:
...
try:
@ -42,11 +50,11 @@ def debug_signal_handler(signal, frame):
logger.debug("\n\nStarting embedded PDB debugger...\n\n")
pdb.Pdb(skip=['gi.*']).set_trace()
return
except StandardError as ex:
except Exception as ex:
...
try:
import code
code.interact()
except StandardError as ex:
except Exception as ex:
logger.debug(f"{ex}, returning to normal program flow...")

5
src/libs/dto/__init__.py Normal file
View File

@ -0,0 +1,5 @@
"""
Dasta Class Package
"""
from .event import Event

View File

@ -13,17 +13,17 @@ from .singleton import Singleton
class IPCServer(Singleton):
""" Create a listener so that other {app_name} instances send requests back to existing instance. """
""" Create a listener so that other {APP_NAME} instances send requests back to existing instance. """
def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "socket"):
self.is_ipc_alive = False
self._ipc_port = 4848
self._ipc_address = ipc_address
self._conn_type = conn_type
self._ipc_authkey = b'' + bytes(f'{app_name}-ipc', 'utf-8')
self._ipc_authkey = b'' + bytes(f'{APP_NAME}-ipc', 'utf-8')
self._ipc_timeout = 15.0
if conn_type == "socket":
self._ipc_address = f'/tmp/{app_name}-ipc.sock'
self._ipc_address = f'/tmp/{APP_NAME}-ipc.sock'
elif conn_type == "full_network":
self._ipc_address = '0.0.0.0'
elif conn_type == "full_network_unsecured":
@ -35,7 +35,7 @@ class IPCServer(Singleton):
self._subscribe_to_events()
def _subscribe_to_events(self):
event_system.subscribe("post_file_to_ipc", self.send_ipc_message)
event_system.subscribe("post-file-to-ipc", self.send_ipc_message)
def create_ipc_listener(self) -> None:
@ -56,7 +56,7 @@ class IPCServer(Singleton):
@daemon_threaded
def _run_ipc_loop(self, listener) -> None:
# NOTE: Not thread safe if using with Gtk. Need to import GLib and use idle_add
while True:
while self.is_ipc_alive:
try:
conn = listener.accept()
start_time = time.perf_counter()
@ -67,19 +67,22 @@ class IPCServer(Singleton):
listener.close()
def _handle_ipc_message(self, conn, start_time) -> None:
while True:
while self.is_ipc_alive:
msg = conn.recv()
logger.debug(msg)
if "FILE|" in msg:
file = msg.split("FILE|")[1].strip()
if file:
event_system.emit("handle_file_from_ipc", file)
event_system.emit("handle-file-from-ipc", file)
conn.close()
break
if "DIR|" in msg:
file = msg.split("DIR|")[1].strip()
if file:
event_system.emit("handle_dir_from_ipc", file)
event_system.emit("handle-dir-from-ipc", file)
conn.close()
break

View File

@ -1,3 +1,3 @@
"""
Utils/Mixins module
Libs.Mixins Package
"""

View File

@ -67,4 +67,4 @@ class DnDMixin:
files.append(gfile)
event_system.emit('set_pre_drop_dnd', (files,))
event_system.emit('set-pre-drop-dnd', (files,))

View File

@ -1,6 +1,8 @@
# Python imports
# Lib imports
import gi
from gi.repository import GLib
# Application imports
@ -8,13 +10,22 @@
class IPCSignalsMixin:
""" IPCSignalsMixin handle messages from another starting solarfm process. """
""" IPCSignalsMixin handle messages from another starting {APP_NAME} process. """
def print_to_console(self, message=None):
def print_to_console(self, message = None):
logger.debug(message)
def handle_file_from_ipc(self, path: str) -> None:
logger.debug(f"File From IPC: {path}")
def handle_file_from_ipc(self, fpath: str) -> None:
logger.debug(f"File From IPC: {fpath}")
GLib.idle_add(
self.broadcast_message, "handle-file", (fpath,)
)
def handle_dir_from_ipc(self, path: str) -> None:
logger.debug(f"Dir From IPC: {path}")
def handle_dir_from_ipc(self, fpath: str) -> None:
logger.debug(f"Dir From IPC: {fpath}")
GLib.idle_add(
self.broadcast_message, "handle-folder", (fpath,)
)
def broadcast_message(self, message_type: str = "none", data: () = ()) -> None:
event_system.emit(message_type, data)

View File

@ -68,11 +68,11 @@ class KeyboardSignalsMixin:
if mapping:
self.handle_mapped_key_event(mapping)
else:
self.handle_as_key_event_scope(mapping)
self.handle_as_key_event_scope(keyname)
def handle_mapped_key_event(self, mapping):
try:
self.handle_as_controller_scope()
self.handle_as_controller_scope(mapping)
except Exception:
self.handle_as_plugin_scope(mapping)
@ -86,13 +86,11 @@ class KeyboardSignalsMixin:
sender = ""
eve_type = mapping
self.handle_as_key_event_system(sender, eve_type)
def handle_as_key_event_scope(self, mapping):
logger.debug(f"on_global_key_release_controller > key > {keyname}")
self.handle_key_event_system(sender, eve_type)
def handle_as_key_event_scope(self, keyname):
if self.ctrl_down and not keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
self.handle_key_event_system(None, mapping)
self.handle_key_event_system(None, keyname)
def handle_key_event_system(self, sender, eve_type):
event_system.emit(eve_type)

View File

@ -0,0 +1,4 @@
"""
Settings Package
"""
from .manager import SettingsManager

View File

@ -1,5 +1,6 @@
# Python imports
import inspect
import time
import json
import zipfile
@ -22,35 +23,35 @@ class MissingConfigError(Exception):
class SettingsManager(StartCheckMixin, Singleton):
def __init__(self):
self._SCRIPT_PTH = path.dirname(path.realpath(__file__))
self._USER_HOME = path.expanduser('~')
self._HOME_CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
self._USR_PATH = f"/usr/share/{app_name.lower()}"
self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json"
self._SCRIPT_PTH: str = path.dirname(path.realpath(__file__))
self._USER_HOME: str = path.expanduser('~')
self._HOME_CONFIG_PATH: str = f"{self._USER_HOME}/.config/{APP_NAME.lower()}"
self._USR_PATH: str = f"/usr/share/{APP_NAME.lower()}"
self._USR_CONFIG_FILE: str = f"{self._USR_PATH}/settings.json"
self._CONTEXT_PATH = f"{self._HOME_CONFIG_PATH}/context_path"
self._PLUGINS_PATH = f"{self._HOME_CONFIG_PATH}/plugins"
self._DEFAULT_ICONS = f"{self._HOME_CONFIG_PATH}/icons"
self._CONFIG_FILE = f"{self._HOME_CONFIG_PATH}/settings.json"
self._GLADE_FILE = f"{self._HOME_CONFIG_PATH}/Main_Window.glade"
self._CSS_FILE = f"{self._HOME_CONFIG_PATH}/stylesheet.css"
self._KEY_BINDINGS_FILE = f"{self._HOME_CONFIG_PATH}/key-bindings.json"
self._PID_FILE = f"{self._HOME_CONFIG_PATH}/{app_name.lower()}.pid"
self._UI_WIDEGTS_PATH = f"{self._HOME_CONFIG_PATH}/ui_widgets"
self._CONTEXT_MENU = f"{self._HOME_CONFIG_PATH}/contexct_menu.json"
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
self._CONTEXT_PATH: str = f"{self._HOME_CONFIG_PATH}/context_path"
self._PLUGINS_PATH: str = f"{self._HOME_CONFIG_PATH}/plugins"
self._DEFAULT_ICONS: str = f"{self._HOME_CONFIG_PATH}/icons"
self._CONFIG_FILE: str = f"{self._HOME_CONFIG_PATH}/settings.json"
self._GLADE_FILE: str = f"{self._HOME_CONFIG_PATH}/Main_Window.glade"
self._CSS_FILE: str = f"{self._HOME_CONFIG_PATH}/stylesheet.css"
self._KEY_BINDINGS_FILE: str = f"{self._HOME_CONFIG_PATH}/key-bindings.json"
self._PID_FILE: str = f"{self._HOME_CONFIG_PATH}/{APP_NAME.lower()}.pid"
self._UI_WIDEGTS_PATH: str = f"{self._HOME_CONFIG_PATH}/ui_widgets"
self._CONTEXT_MENU: str = f"{self._HOME_CONFIG_PATH}/contexct_menu.json"
self._WINDOW_ICON: str = f"{self._DEFAULT_ICONS}/{APP_NAME.lower()}.png"
# self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json"
# self._PLUGINS_PATH = f"plugins"
# self._CONFIG_FILE = f"settings.json"
# self._GLADE_FILE = f"Main_Window.glade"
# self._CSS_FILE = f"stylesheet.css"
# self._KEY_BINDINGS_FILE = f"key-bindings.json"
# self._PID_FILE = f"{app_name.lower()}.pid"
# self._WINDOW_ICON = f"{app_name.lower()}.png"
# self._UI_WIDEGTS_PATH = f"ui_widgets"
# self._CONTEXT_MENU = f"contexct_menu.json"
# self._DEFAULT_ICONS = f"icons"
# self._USR_CONFIG_FILE: str = f"{self._USR_PATH}/settings.json"
# self._PLUGINS_PATH: str = f"plugins"
# self._CONFIG_FILE: str = f"settings.json"
# self._GLADE_FILE: str = f"Main_Window.glade"
# self._CSS_FILE: str = f"stylesheet.css"
# self._KEY_BINDINGS_FILE: str = f"key-bindings.json"
# self._PID_FILE: str = f"{APP_NAME.lower()}.pid"
# self._WINDOW_ICON: str = f"{APP_NAME.lower()}.png"
# self._UI_WIDEGTS_PATH: str = f"ui_widgets"
# self._CONTEXT_MENU: str = f"contexct_menu.json"
# self._DEFAULT_ICONS: str = f"icons"
# with zipfile.ZipFile("files.zip", mode="r", allowZip64=True) as zf:
@ -79,7 +80,7 @@ class SettingsManager(StartCheckMixin, Singleton):
if not path.exists(self._CSS_FILE):
raise MissingConfigError("Unable to find the application Stylesheet file.")
if not path.exists(self._WINDOW_ICON):
self._WINDOW_ICON = f"{self._USR_PATH}/icons/{app_name.lower()}.png"
self._WINDOW_ICON = f"{self._USR_PATH}/icons/{APP_NAME.lower()}.png"
if not path.exists(self._WINDOW_ICON):
raise MissingConfigError("Unable to find the application icon.")
if not path.exists(self._UI_WIDEGTS_PATH):
@ -105,20 +106,22 @@ class SettingsManager(StartCheckMixin, Singleton):
self.settings: Settings = None
self._main_window = None
self._builder = None
self.PAINT_BG_COLOR = (0, 0, 0, 0.0)
self.PAINT_BG_COLOR: tuple = (0, 0, 0, 0.0)
self._trace_debug = False
self._debug = False
self._dirty_start = False
self._trace_debug: bool = False
self._debug: bool = False
self._dirty_start: bool = False
self._passed_in_file: bool = False
self._starting_files: list = []
def register_signals_to_builder(self, classes=None):
def register_signals_to_builder(self, classes = None):
handlers = {}
for c in classes:
methods = None
try:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
methods = inspect.getmembers(c, predicate = inspect.ismethod)
handlers.update(methods)
except Exception as e:
...
@ -128,7 +131,6 @@ class SettingsManager(StartCheckMixin, Singleton):
def set_main_window(self, window): self._main_window = window
def set_builder(self, builder) -> any: self._builder = builder
def get_monitor_data(self) -> list:
screen = self._main_window.get_screen()
monitors = []
@ -152,29 +154,45 @@ class SettingsManager(StartCheckMixin, Singleton):
def get_home_config_path(self) -> str: return self._HOME_CONFIG_PATH
def get_window_icon(self) -> str: return self._WINDOW_ICON
def get_home_path(self) -> str: return self._USER_HOME
def get_starting_files(self) -> list: return self._starting_files
def get_starting_args(self):
return self.args, self.unknownargs
def set_main_window_x(self, x: int = 0): self.settings.config.main_window_x = x
def set_main_window_y(self, y: int = 0): self.settings.config.main_window_y = y
def set_main_window_width(self, width: int = 800): self.settings.config.main_window_width = width
def set_main_window_height(self, height: int = 600): self.settings.config.main_window_height = height
def set_main_window_min_width(self, width: int = 720): self.settings.config.main_window_min_width = width
def set_main_window_min_height(self, height: int = 480): self.settings.config.main_window_min_height = height
def set_starting_files(self, files: list): self._starting_files = files
def set_start_load_time(self): self._start_load_time = time.perf_counter()
def set_end_load_time(self): self._end_load_time = time.perf_counter()
def set_starting_args(self, args, unknownargs):
self.args = args
self.unknownargs = unknownargs
def set_trace_debug(self, trace_debug: bool):
self._trace_debug = trace_debug
def set_debug(self, debug: bool):
self._debug = debug
def set_is_starting_with_file(self, is_passed_in_file: bool = False):
self._passed_in_file = is_passed_in_file
def is_trace_debug(self) -> str: return self._trace_debug
def is_debug(self) -> str: return self._debug
def is_starting_with_file(self) -> bool: return self._passed_in_file
def call_method(self, target_class = None, _method_name = None, data = None):
def log_load_time(self): logger.info( f"Load Time: {self._end_load_time - self._start_load_time}" )
def call_method(self, target_class: any = None, _method_name: str = "", data: any = None):
method_name = str(_method_name)
method = getattr(target_class, method_name, lambda data: f"No valid key passed...\nkey={method_name}\nargs={data}")
return method(data) if data else method()
def set_main_window_x(self, x = 0): self.settings.config.main_window_x = x
def set_main_window_y(self, y = 0): self.settings.config.main_window_y = y
def set_main_window_width(self, width = 800): self.settings.config.main_window_width = width
def set_main_window_height(self, height = 600): self.settings.config.main_window_height = height
def set_main_window_min_width(self, width = 720): self.settings.config.main_window_min_width = width
def set_main_window_min_height(self, height = 480): self.settings.config.main_window_min_height = height
def set_trace_debug(self, trace_debug):
self._trace_debug = trace_debug
def set_debug(self, debug):
self._debug = debug
def load_settings(self):
if not path.exists(self._CONFIG_FILE):
self.settings = Settings()

View File

@ -1,5 +1,5 @@
"""
Options module
Settings.Options Package
"""
from .settings import Settings
from .config import Config

View File

@ -0,0 +1,3 @@
"""
Settings.Other Package
"""

View File

@ -39,4 +39,4 @@ class WebkitUISettings(WebKit2.Settings):
self.set_enable_webaudio(True)
self.set_enable_accelerated_2d_canvas(True)
self.set_user_agent(f"{app_name}")
self.set_user_agent(f"{APP_NAME}")

View File

@ -41,7 +41,7 @@ class StartCheckMixin:
try:
os.kill(pid, 0)
except OSError:
print(f"{app_name} PID file exists but PID is irrelevant; starting dirty...")
print(f"{APP_NAME} PID file exists but PID is irrelevant; starting dirty...")
self._dirty_start = True
return False
@ -53,7 +53,7 @@ class StartCheckMixin:
self._print_pid(pid)
def _print_pid(self, pid):
print(f"{app_name} PID: {pid}")
print(f"{APP_NAME} PID: {pid}")
def _clean_pid(self):
os.unlink(self._PID_FILE)

View File

@ -1,4 +0,0 @@
"""
Settings module
"""
from .manager import SettingsManager

View File

@ -1,3 +0,0 @@
"""
Settings Other module
"""

67
src/libs/status_icon.py Normal file
View File

@ -0,0 +1,67 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import AppIndicator3
# Application imports
class StatusIcon():
""" StatusIcon for Application to go to Status Tray. """
def __init__(self):
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
def _setup_styling(self):
...
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
status_menu = Gtk.Menu()
icon_theme = Gtk.IconTheme.get_default()
check_menu_item = Gtk.CheckMenuItem.new_with_label("Update icon")
quit_menu_item = Gtk.MenuItem.new_with_label("Quit")
# Create StatusNotifierItem
self.indicator = AppIndicator3.Indicator.new(
f"{APP_NAME}-statusicon",
"gtk-info",
AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
check_menu_item.connect("activate", self.check_menu_item_cb)
quit_menu_item.connect("activate", self.quit_menu_item_cb)
icon_theme.connect('changed', self.icon_theme_changed_cb)
self.indicator.set_menu(status_menu)
status_menu.append(check_menu_item)
status_menu.append(quit_menu_item)
status_menu.show_all()
def update_icon(self, icon_name):
self.indicator.set_icon(icon_name)
def check_menu_item_cb(self, widget, data = None):
icon_name = "parole" if widget.get_active() else "gtk-info"
self.update_icon(icon_name)
def icon_theme_changed_cb(self, theme):
self.update_icon("gtk-info")
def quit_menu_item_cb(self, widget, data = None):
event_system.emit("tear-down")

View File

@ -22,6 +22,7 @@ class Plugin:
support: str = None
requests:{} = None
reference: type = None
pre_launch: bool = False
class ManifestProcessor:
@ -46,23 +47,29 @@ class ManifestProcessor:
plugin.support = self._manifest["support"]
plugin.requests = self._manifest["requests"]
if "pre_launch" in self._manifest.keys():
plugin.pre_launch = True if self._manifest["pre_launch"] == "true" else False
return plugin
def get_loading_data(self):
loading_data = {}
requests = self._plugin.requests
keys = requests.keys()
if "pass_events" in keys:
if "pass_events" in requests:
if requests["pass_events"] in ["true"]:
loading_data["pass_events"] = True
if "bind_keys" in keys:
if isinstance(requests["bind_keys"], list):
loading_data["bind_keys"] = requests["bind_keys"]
if "pass_ui_objects" in keys:
if "pass_ui_objects" in requests:
if isinstance(requests["pass_ui_objects"], list):
loading_data["pass_ui_objects"] = [ self._builder.get_object(obj) for obj in requests["pass_ui_objects"] ]
if "bind_keys" in requests:
if isinstance(requests["bind_keys"], list):
loading_data["bind_keys"] = requests["bind_keys"]
return self._plugin, loading_data
def is_pre_launch(self):
return self._plugin.pre_launch

View File

@ -10,6 +10,7 @@ from os.path import isdir
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gio
# Application imports
@ -35,11 +36,23 @@ class PluginsController:
self._plugins_dir_watcher = None
self._plugin_collection = []
self._plugin_manifests = {}
self._load_manifests()
def launch_plugins(self) -> None:
def _load_manifests(self):
logger.info(f"Loading manifests...")
for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]:
manifest = ManifestProcessor(path, self._builder)
self._plugin_manifests[path] = {
"path": path,
"folder": folder,
"manifest": manifest
}
self._set_plugins_watcher()
self.load_plugins()
def _set_plugins_watcher(self) -> None:
self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \
@ -52,21 +65,47 @@ class PluginsController:
Gio.FileMonitorEvent.MOVED_OUT]:
self.reload_plugins(file)
def load_plugins(self, file: str = None) -> None:
logger.info(f"Loading plugins...")
def pre_launch_plugins(self) -> None:
logger.info(f"Loading pre-launch plugins...")
plugin_manifests: {} = {}
for key in self._plugin_manifests:
target_manifest = self._plugin_manifests[key]["manifest"]
if target_manifest.is_pre_launch():
plugin_manifests[key] = self._plugin_manifests[key]
self._load_plugins(plugin_manifests, is_pre_launch = True)
def post_launch_plugins(self) -> None:
logger.info(f"Loading post-launch plugins...")
plugin_manifests: {} = {}
for key in self._plugin_manifests:
target_manifest = self._plugin_manifests[key]["manifest"]
if not target_manifest.is_pre_launch():
plugin_manifests[key] = self._plugin_manifests[key]
self._load_plugins(plugin_manifests)
def _load_plugins(self, plugin_manifests: {} = {}, is_pre_launch: bool = False) -> None:
parent_path = os.getcwd()
for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]:
for key in plugin_manifests:
target_manifest = plugin_manifests[key]
path, folder, manifest = target_manifest["path"], target_manifest["folder"], target_manifest["manifest"]
try:
target = join(path, "plugin.py")
manifest = ManifestProcessor(path, self._builder)
if not os.path.exists(target):
raise InvalidPluginException("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
plugin, loading_data = manifest.get_loading_data()
module = self.load_plugin_module(path, folder, target)
if is_pre_launch:
self.execute_plugin(module, plugin, loading_data)
else:
GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data))
except Exception as e:
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
logger.debug("Trace: ", traceback.print_exc())

View File

@ -43,6 +43,6 @@
/* Other message text colors */
.errorTxt { color: rgb(170, 18, 18); }
.warningTxt { color: rgb(255, 168, 0); }
.successTxt { color: rgb(136, 204, 39); }
.error-txt { color: rgb(170, 18, 18); }
.warning-txt { color: rgb(255, 168, 0); }
.success=txt { color: rgb(136, 204, 39); }

View File

@ -1,6 +1,7 @@
html, body {
display: block;
background-color: #32383e00;
// background-color: #32383e00;
background-color: rgba(39, 43, 52, 0.64);
color: #ffffff;
text-wrap: wrap;
}

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,23 +1,24 @@
{
"keybindings": {
"help" : "F1",
"rename_files" : ["F2", "<Control>e"],
"open_terminal" : "F4",
"refresh_tab" : ["F5", "<Control>r"],
"delete_files" : "Delete",
"tggl_top_main_menubar" : "Alt_L",
"trash_files" : "<Shift><Control>t",
"tear_down" : "<Control>q",
"go_up" : "<Control>Up",
"go_home" : "<Control>slash",
"grab_focus_path_entry" : "<Control>l",
"open_files" : "<Control>o",
"show_hide_hidden_files" : "<Control>h",
"keyboard_create_tab" : "<Control>t",
"keyboard_close_tab" : "<Control>w",
"keyboard_copy_files" : "<Control>c",
"keyboard_cut_files" : "<Control>x",
"paste_files" : "<Control>v",
"show_new_file_menu" : "<Control>n"
"rename-files" : ["F2", "<Control>e"],
"open-terminal" : "F4",
"refresh-tab" : ["F5", "<Control>r"],
"delete-files" : "Delete",
"tggl-top-main-menubar" : "Alt_L",
"tggl-top-main-menubar" : "<Control>0",
"trash-files" : "<Shift><Control>t",
"tear-down" : "<Control>q",
"go-up" : "<Control>Up",
"go-home" : "<Control>slash",
"grab-focus-path-entry" : "<Control>l",
"open-files" : "<Control>o",
"show-hide-hidden-files" : "<Control>h",
"keyboard-create-tab" : "<Control>t",
"keyboard-close-tab" : "<Control>w",
"keyboard-copy-files" : "<Control>c",
"keyboard-cut-files" : "<Control>x",
"paste-files" : "<Control>v",
"show-new-file-menu" : "<Control>n"
}
}

View File

@ -1,3 +1,18 @@
/* ---- Make most desired things base transparent ---- */
/* ---- Make most desired things base transparent ---- */
popover,
popover > box
.main-window,
.base-container,
.body-container,
.center-container,
.header-container,
.left-containerm,
.right-container {
background: rgba(0, 0, 0, 0.0);
color: rgba(255, 255, 255, 1);
}
.base-container {
margin: 10px;
}
@ -128,3 +143,4 @@ XfdesktopIconView.view:active {
.mw_transparency_97 { background: rgba(39, 43, 52, 0.97); }
.mw_transparency_98 { background: rgba(39, 43, 52, 0.98); }
.mw_transparency_99 { background: rgba(39, 43, 52, 0.99); }
.mw_transparency_100 { background: rgba(39, 43, 52, 1.00); }