Major refactor to move away from glade file
This commit is contained in:
3
src/core/widgets/__init__.py
Normal file
3
src/core/widgets/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Widgets Package
|
||||
"""
|
||||
129
src/core/widgets/category_list_widget.py
Normal file
129
src/core/widgets/category_list_widget.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
from xdg.DesktopEntry import DesktopEntry
|
||||
|
||||
# Application imports
|
||||
from libs.desktop_parsing.app_finder import find_apps
|
||||
|
||||
|
||||
|
||||
|
||||
class CategoryListWidget(Gtk.ButtonBox):
|
||||
def __init__(self):
|
||||
super(CategoryListWidget, self).__init__()
|
||||
|
||||
self.ctx = self.get_style_context()
|
||||
self.active_category: str = 'Accessories'
|
||||
|
||||
self.category_dict: {} = {
|
||||
"Accessories": [],
|
||||
"Multimedia": [],
|
||||
"Graphics": [],
|
||||
"Game": [],
|
||||
"Office": [],
|
||||
"Development": [],
|
||||
"Internet": [],
|
||||
"Settings": [],
|
||||
"System": [],
|
||||
"Wine": [],
|
||||
"Other": []
|
||||
}
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
self.fill_menu_objects()
|
||||
|
||||
self.show_all()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
self.ctx.add_class("category-list-widget")
|
||||
|
||||
def _setup_signals(self):
|
||||
event_system.subscribe("refresh-active-category", self.refresh_active_category)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
|
||||
def _load_widgets(self):
|
||||
for category in self.category_dict.keys():
|
||||
button = Gtk.Button(label = category)
|
||||
button.connect("clicked", self.set_active_category)
|
||||
button.show()
|
||||
|
||||
self.add(button)
|
||||
|
||||
def set_active_category(self, button):
|
||||
self.active_category = button.get_label()
|
||||
event_system.emit(
|
||||
"load-active-category",
|
||||
(self.category_dict[ self.active_category ],)
|
||||
)
|
||||
|
||||
def fill_menu_objects(self, apps: [] = []):
|
||||
if not apps:
|
||||
apps = find_apps()
|
||||
|
||||
for app in apps:
|
||||
fPath = app.get_filename()
|
||||
xdgObj = DesktopEntry( fPath )
|
||||
|
||||
title = xdgObj.getName()
|
||||
groups = xdgObj.getCategories()
|
||||
comment = xdgObj.getComment()
|
||||
icon = xdgObj.getIcon()
|
||||
mainExec = xdgObj.getExec()
|
||||
tryExec = xdgObj.getTryExec()
|
||||
|
||||
group = ""
|
||||
if "Accessories" in groups or "Utility" in groups:
|
||||
group = "Accessories"
|
||||
elif "Multimedia" in groups or "Video" in groups or "Audio" in groups:
|
||||
group = "Multimedia"
|
||||
elif "Development" in groups:
|
||||
group = "Development"
|
||||
elif "Game" in groups:
|
||||
group = "Game"
|
||||
elif "Internet" in groups or "Network" in groups:
|
||||
group = "Internet"
|
||||
elif "Graphics" in groups:
|
||||
group = "Graphics"
|
||||
elif "Office" in groups:
|
||||
group = "Office"
|
||||
elif "System" in groups:
|
||||
group = "System"
|
||||
elif "Settings" in groups:
|
||||
group = "Settings"
|
||||
elif "Wine" in groups:
|
||||
group = "Wine"
|
||||
else:
|
||||
group = "Other"
|
||||
|
||||
self.category_dict[group].append(
|
||||
{
|
||||
"title": title,
|
||||
"groups": groups,
|
||||
"comment": comment,
|
||||
"exec": mainExec,
|
||||
"tryExec": tryExec,
|
||||
"fileName": fPath.split("/")[-1],
|
||||
"filePath": fPath,
|
||||
"icon": icon
|
||||
}
|
||||
)
|
||||
|
||||
def refresh_active_category(self):
|
||||
event_system.emit(
|
||||
"load-active-category",
|
||||
(self.category_dict[ self.active_category ],)
|
||||
)
|
||||
|
||||
125
src/core/widgets/category_widget.py
Normal file
125
src/core/widgets/category_widget.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# Python imports
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GdkPixbuf', '2.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gio
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class CategoryWidget(Gtk.Grid):
|
||||
def __init__(self):
|
||||
super(CategoryWidget, self).__init__()
|
||||
|
||||
self.ctx = self.get_style_context()
|
||||
|
||||
self.column_count = 4
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_hexpand(True)
|
||||
self.set_vexpand(True)
|
||||
self.set_row_spacing(10)
|
||||
self.set_column_spacing(10)
|
||||
self.set_row_homogeneous(True)
|
||||
self.set_column_homogeneous(True)
|
||||
|
||||
self.set_orientation(Gtk.Orientation.HORIZONTAL)
|
||||
self.ctx.add_class("category-widget")
|
||||
|
||||
def _setup_signals(self):
|
||||
event_system.subscribe("load-active-category", self.load_active_category)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
event_system.emit("refresh-active-category")
|
||||
|
||||
def load_active_category(self, app_list: [] = []):
|
||||
self.clear_children()
|
||||
|
||||
row = 0
|
||||
col = 0
|
||||
icon_theme = Gtk.IconTheme.get_default()
|
||||
|
||||
for app in app_list:
|
||||
button = self.generate_app_button(icon_theme, app)
|
||||
self.attach(button, col, row, 1, 1)
|
||||
|
||||
col += 1
|
||||
if col == self.column_count:
|
||||
col = 0
|
||||
row += 1
|
||||
|
||||
def generate_app_button(self, icon_theme, app: {} = {}):
|
||||
if not app: return
|
||||
|
||||
title = app["title"]
|
||||
exec_str = app[
|
||||
"exec" if not app["exec"] in ("", None) else "tryExec"
|
||||
]
|
||||
|
||||
button = Gtk.Button(label = title)
|
||||
button.sig_id = button.connect("clicked", self._do_exec, exec_str)
|
||||
|
||||
icon_pth = app["icon"]
|
||||
icon = self.get_icon_from_path(icon_pth) \
|
||||
if os.path.exists(icon_pth) \
|
||||
else \
|
||||
self.get_icon_from_gio(icon_theme, icon_pth)
|
||||
|
||||
button.set_image(icon)
|
||||
button.set_always_show_image(True)
|
||||
|
||||
button.show_all()
|
||||
return button
|
||||
|
||||
def get_icon_from_path(self, path: str):
|
||||
pixbuf = GdkPixbuf.PixbufAnimation.new_from_file(path) \
|
||||
.get_static_image() \
|
||||
.scale_simple(32, 32, \
|
||||
GdkPixbuf.InterpType.BILINEAR)
|
||||
|
||||
return Gtk.Image.new_from_pixbuf(pixbuf)
|
||||
|
||||
def get_icon_from_gio(self, icon_theme, icon_name: str):
|
||||
gio_icon = Gio.Icon.new_for_string(icon_name)
|
||||
pixbuf = None
|
||||
|
||||
# Note: https://docs.gtk.org/gtk3/enum.IconSize.html
|
||||
for i in [6, 5, 3, 4, 2, 1]:
|
||||
icon_info = Gtk.IconTheme.lookup_by_gicon(icon_theme, gio_icon, i, Gtk.IconLookupFlags.FORCE_REGULAR)
|
||||
if not icon_info: continue
|
||||
|
||||
pixbuf = icon_info.load_icon().scale_simple(32, 32, 2) # 2 = BILINEAR and is best by default
|
||||
break
|
||||
|
||||
return Gtk.Image.new_from_pixbuf( pixbuf )
|
||||
|
||||
def _do_exec(self, widget, _command):
|
||||
command = _command.split("%")[0]
|
||||
subprocess.Popen(command.split(), start_new_session=True, stdout=None, stderr=None)
|
||||
|
||||
def clear_children(self, app_list: [] = []):
|
||||
children = self.get_children()
|
||||
for child in children:
|
||||
child.disconnect(child.sig_id)
|
||||
self.remove(child)
|
||||
child.run_dispose()
|
||||
|
||||
|
||||
110
src/core/widgets/clock_widget.py
Normal file
110
src/core/widgets/clock_widget.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# Python imports
|
||||
from datetime import datetime
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class CalendarWidget(Gtk.Popover):
|
||||
def __init__(self):
|
||||
super(CalendarWidget, self).__init__()
|
||||
|
||||
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):
|
||||
self.body = Gtk.Calendar()
|
||||
|
||||
self.body.show()
|
||||
self.add(self.body)
|
||||
|
||||
|
||||
class ClockWidget(Gtk.EventBox):
|
||||
def __init__(self):
|
||||
super(ClockWidget, self).__init__()
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show_all()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_size_request(180, -1)
|
||||
|
||||
ctx = self.get_style_context()
|
||||
ctx.add_class("clock-widget")
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("button_release_event", self._toggle_cal_popover)
|
||||
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
self.calendar = CalendarWidget()
|
||||
self.label = Gtk.Label()
|
||||
|
||||
self.calendar.set_relative_to(self)
|
||||
|
||||
self.label.set_justify(Gtk.Justification.CENTER)
|
||||
self.label.set_margin_top(15)
|
||||
self.label.set_margin_bottom(15)
|
||||
self.label.set_margin_left(15)
|
||||
self.label.set_margin_right(15)
|
||||
|
||||
self._update_face()
|
||||
self.add(self.label)
|
||||
|
||||
GObject.timeout_add(59000, self._update_face)
|
||||
|
||||
def _update_face(self):
|
||||
dt_now = datetime.now()
|
||||
hours_mins_sec = dt_now.strftime("%I:%M %p")
|
||||
month_day_year = dt_now.strftime("%m/%d/%Y")
|
||||
time_str = hours_mins_sec + "\n" + month_day_year
|
||||
|
||||
self.label.set_label(time_str)
|
||||
|
||||
def _toggle_cal_popover(self, widget, eve):
|
||||
if (self.calendar.get_visible() == True):
|
||||
self.calendar.popdown()
|
||||
return
|
||||
|
||||
now = datetime.now()
|
||||
timeStr = now.strftime("%m/%d/%Y")
|
||||
parts = timeStr.split("/")
|
||||
month = int(parts[0]) - 1
|
||||
day = int(parts[1])
|
||||
year = int(parts[2])
|
||||
|
||||
self.calendar.body.select_day(day)
|
||||
self.calendar.body.select_month(month, year)
|
||||
|
||||
self.calendar.popup()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3
src/core/widgets/controls/__init__.py
Normal file
3
src/core/widgets/controls/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Widgets.Controls Package
|
||||
"""
|
||||
83
src/core/widgets/controls/open_files_button.py
Normal file
83
src/core/widgets/controls/open_files_button.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# Python imports
|
||||
from contextlib import suppress
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
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)
|
||||
|
||||
with suppress(Exception):
|
||||
folder = widget.get_current_file().get_parent() if not start_dir else start_dir
|
||||
chooser.set_current_folder( folder.get_path() )
|
||||
|
||||
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
|
||||
72
src/core/widgets/controls/save_as_button.py
Normal file
72
src/core/widgets/controls/save_as_button.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# Python imports
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gio
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class SaveAsButton(Gtk.Button):
|
||||
def __init__(self):
|
||||
super(SaveAsButton, self).__init__()
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_label("Save As")
|
||||
self.set_image( Gtk.Image.new_from_icon_name("gtk-save-as", 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("released", self._save_as)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("save-as", self._save_as)
|
||||
|
||||
def _load_widgets(self):
|
||||
...
|
||||
|
||||
def _save_as(self, widget = None, eve = None, gfile = None):
|
||||
start_dir = None
|
||||
_gfile = None
|
||||
|
||||
chooser = Gtk.FileChooserDialog("Save File As...", None,
|
||||
Gtk.FileChooserAction.SAVE,
|
||||
(
|
||||
Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_SAVE_AS,
|
||||
Gtk.ResponseType.OK
|
||||
)
|
||||
)
|
||||
|
||||
# chooser.set_select_multiple(False)
|
||||
|
||||
response = chooser.run()
|
||||
if not response == Gtk.ResponseType.OK:
|
||||
chooser.destroy()
|
||||
return _gfile
|
||||
|
||||
file = chooser.get_filename()
|
||||
if not file:
|
||||
chooser.destroy()
|
||||
return _gfile
|
||||
|
||||
path = file if os.path.isabs(file) else os.path.abspath(file)
|
||||
_gfile = Gio.File.new_for_path(path)
|
||||
|
||||
chooser.destroy()
|
||||
|
||||
logger.debug(f"File To Save As: {_gfile}")
|
||||
return _gfile
|
||||
48
src/core/widgets/controls/transparency_scale.py
Normal file
48
src/core/widgets/controls/transparency_scale.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class TransparencyScale(Gtk.Scale):
|
||||
def __init__(self):
|
||||
super(TransparencyScale, self).__init__()
|
||||
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show_all()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_digits(0)
|
||||
self.set_value_pos(Gtk.PositionType.RIGHT)
|
||||
self.add_mark(50.0, Gtk.PositionType.TOP, "50%")
|
||||
self.set_hexpand(True)
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("value-changed", self._update_transparency)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
adjust = self.get_adjustment()
|
||||
adjust.set_lower(0)
|
||||
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")
|
||||
tp = int(range.get_value())
|
||||
settings.theming.transparency = tp
|
||||
event_system.emit("update-transparency")
|
||||
35
src/core/widgets/pager_widget.py
Normal file
35
src/core/widgets/pager_widget.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Wnck', '3.0')
|
||||
from gi.repository import Wnck
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class PagerWidget:
|
||||
def __init__(self):
|
||||
super(PagerWidget, self).__init__()
|
||||
|
||||
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):
|
||||
...
|
||||
|
||||
def get_widget(self):
|
||||
return Wnck.Pager.new()
|
||||
62
src/core/widgets/tab_widget.py
Normal file
62
src/core/widgets/tab_widget.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from ..widgets.category_widget import CategoryWidget
|
||||
from ..widgets.vte_widget import VteWidget
|
||||
|
||||
|
||||
|
||||
class TabWidget(Gtk.Notebook):
|
||||
def __init__(self):
|
||||
super(TabWidget, self).__init__()
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_hexpand(True)
|
||||
self.set_vexpand(True)
|
||||
|
||||
ctx = self.get_style_context()
|
||||
ctx.add_class("tab-widget")
|
||||
|
||||
def _setup_signals(self):
|
||||
...
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("focus-apps", self.focus_apps)
|
||||
event_system.subscribe("focus-terminal", self.focus_terminal)
|
||||
|
||||
def _load_widgets(self):
|
||||
scroll_view = Gtk.ScrolledWindow()
|
||||
viewport = Gtk.Viewport()
|
||||
|
||||
viewport.add(CategoryWidget())
|
||||
scroll_view.add(viewport)
|
||||
|
||||
scroll_view.show_all()
|
||||
|
||||
self.insert_page(scroll_view, Gtk.Label(label = "Apps"), 0)
|
||||
self.insert_page(VteWidget(), Gtk.Label(label = "Terminal"), 1)
|
||||
|
||||
self.set_current_page(0)
|
||||
|
||||
def focus_apps(self):
|
||||
widget = self.get_nth_page(0).get_children()[0].get_children()[0]
|
||||
self.set_current_page(0)
|
||||
widget.grab_focus()
|
||||
|
||||
def focus_terminal(self):
|
||||
widget = self.get_nth_page(1)
|
||||
self.set_current_page(1)
|
||||
widget.grab_focus()
|
||||
54
src/core/widgets/task_list_widget.py
Normal file
54
src/core/widgets/task_list_widget.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Wnck', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Wnck
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class TaskListWidget(Gtk.ScrolledWindow):
|
||||
def __init__(self):
|
||||
super(TaskListWidget, self).__init__()
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show_all()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_hexpand(False)
|
||||
self.set_size_request(180, -1)
|
||||
|
||||
def _setup_signals(self):
|
||||
...
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
viewport = Gtk.Viewport()
|
||||
task_list = Wnck.Tasklist.new()
|
||||
vbox = Gtk.Box()
|
||||
|
||||
vbox.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
|
||||
|
||||
task_list.set_scroll_enabled(False)
|
||||
task_list.set_button_relief(2) # 0 = normal relief, 2 = no relief
|
||||
task_list.set_grouping(1) # 0 = mever group, 1 auto group, 2 = always group
|
||||
|
||||
task_list.set_vexpand(True)
|
||||
task_list.set_include_all_workspaces(False)
|
||||
task_list.set_orientation(1) # 0 = horizontal, 1 = vertical
|
||||
|
||||
vbox.add(task_list)
|
||||
viewport.add(vbox)
|
||||
self.add(viewport)
|
||||
125
src/core/widgets/vte_widget.py
Normal file
125
src/core/widgets/vte_widget.py
Normal 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.debug(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: str):
|
||||
self.dont_process = True
|
||||
|
||||
cmds = [f"cd '{fpath}'\n", "clear\n"]
|
||||
for cmd in cmds:
|
||||
self.run_command(cmd)
|
||||
|
||||
def run_command(self, cmd: str):
|
||||
self.feed_child_binary(bytes(cmd, 'utf8'))
|
||||
Reference in New Issue
Block a user