Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docking/applets/applications/applet.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ class ApplicationsApplet(Applet):
id = meta.id
name = _("Applications")
icon_name = "view-app-grid"
supports_system_icon = True

def __init__(self, icon_size: int, config: Config | None = None) -> None:
super().__init__(icon_size=icon_size, config=config)
self._popup_menu: Gtk.Menu | None = None
self.present()

def create_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
def create_docking_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
return create_icon(size=size)

def on_clicked(self) -> None:
Expand Down
65 changes: 58 additions & 7 deletions docking/applets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

from __future__ import annotations

from abc import ABC, abstractmethod
from abc import ABC
from collections.abc import Callable
from importlib import resources
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -108,6 +108,11 @@

CATALOG_ICON_DIR = "icons/applets"

ICON_SOURCE_PREF_KEY = "icon_source"
ICON_SOURCE_DOCKING = "docking"
ICON_SOURCE_SYSTEM = "system"
ICON_SOURCE_VALUES = frozenset({ICON_SOURCE_DOCKING, ICON_SOURCE_SYSTEM})


def _icon_name_candidates(name: str) -> tuple[str, ...]:
names: list[str] = [name]
Expand Down Expand Up @@ -299,11 +304,11 @@ def _icon_label_outline_width(*, font_size: int) -> float:


class Applet(ABC):
"""Base class for dock plugins that render custom icons.
"""Base class for dock plugins that render Docking icons.

Each applet owns a DockItem. The applet renders custom Cairo
content to a pixbuf and assigns it to item.icon. The existing
renderer draws it like any other icon -- no renderer changes needed.
Each applet owns a DockItem. Most applets render their own pixbuf, while
simple applets can opt into a user-selected system theme icon. The existing
renderer draws both paths like any other item icon.

Lifecycle:
__init__ -> create item
Expand All @@ -315,6 +320,7 @@ class Applet(ABC):
id: str
name: str
icon_name: str
supports_system_icon = False

def __init__(self, icon_size: int, config: Config | None = None) -> None:
self._config = config
Expand Down Expand Up @@ -347,9 +353,54 @@ def save_prefs(self, prefs: dict[str, Any]) -> None:
self._config.applet_prefs[self.id] = prefs
self._config.save()

@abstractmethod
def create_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
"""Render custom content to a pixbuf at the given size."""
"""Render the active icon source at the given size."""
if self.uses_system_icon():
icon_name = self.system_icon_name()
icon = load_theme_icon(name=icon_name, size=size)
if icon is not None:
self.item.icon_name = icon_name
return icon

self.item.icon_name = self.icon_name
return self.create_docking_icon(size=size)

def create_docking_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
"""Render the built-in Docking icon when an applet opts into icon sources."""
_ = size
raise NotImplementedError(
f"{type(self).__name__} must implement create_icon() "
"or create_docking_icon()"
)

def system_icon_name(self) -> str:
"""Theme icon name used when the applet is set to System Icon."""
return self.icon_name

def icon_source(self) -> str:
"""Return the selected icon source, defaulting to the Docking icon."""
if not self.supports_system_icon:
return ICON_SOURCE_DOCKING
source = self.load_prefs().get(ICON_SOURCE_PREF_KEY)
if source == ICON_SOURCE_SYSTEM:
return ICON_SOURCE_SYSTEM
return ICON_SOURCE_DOCKING

def uses_system_icon(self) -> bool:
"""Whether this applet currently requests a theme icon."""
return self.icon_source() == ICON_SOURCE_SYSTEM

def set_icon_source(self, source: str) -> None:
"""Persist and present the selected icon source."""
if not self.supports_system_icon or source not in ICON_SOURCE_VALUES:
return
if source == self.icon_source():
return

prefs = self.load_prefs()
prefs[ICON_SOURCE_PREF_KEY] = source
self.save_prefs(prefs)
self.present()

def refresh_tooltip(self) -> None:
"""Sync tooltip/text presentation fields on self.item."""
Expand Down
3 changes: 2 additions & 1 deletion docking/applets/calculator/applet.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class CalculatorApplet(Applet):
id = meta.id
name = _("Calculator")
icon_name = "accessories-calculator"
supports_system_icon = True

def __init__(self, icon_size: int, config: Config | None = None) -> None:
self._popup: Gtk.Window | None = None
Expand All @@ -74,7 +75,7 @@ def __init__(self, icon_size: int, config: Config | None = None) -> None:
super().__init__(icon_size=icon_size, config=config)
self.present()

def create_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
def create_docking_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
return create_icon(size=size)

def refresh_tooltip(self) -> None:
Expand Down
3 changes: 2 additions & 1 deletion docking/applets/desktop/applet.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ class DesktopApplet(Applet):
id = meta.id
name = _("Desktop")
icon_name = "user-desktop"
supports_system_icon = True

def __init__(self, icon_size: int, config: Config | None = None) -> None:
super().__init__(icon_size=icon_size, config=config)
self.present()

def create_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
def create_docking_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
return create_icon(size=size)

def on_clicked(self) -> None:
Expand Down
3 changes: 2 additions & 1 deletion docking/applets/screenshot/applet.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class ScreenshotApplet(Applet):
id = meta.id
name = _("Screenshot")
icon_name = "applets-screenshooter"
supports_system_icon = True

def __init__(self, icon_size: int, config: Config | None = None) -> None:
self._tool = _detect_tool()
Expand All @@ -49,7 +50,7 @@ def __init__(self, icon_size: int, config: Config | None = None) -> None:
super().__init__(icon_size, config)
self.present()

def create_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
def create_docking_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, size, size)
cr = cairo.Context(surface)
_draw_screenshot_icon(cr=cr, size=size)
Expand Down
3 changes: 2 additions & 1 deletion docking/applets/session/applet.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ class SessionApplet(Applet):
id = meta.id
name = _("Session")
icon_name = "system-log-out"
supports_system_icon = True

def __init__(self, icon_size: int, config: Config | None = None) -> None:
super().__init__(icon_size, config)
self.present()

def create_icon(self, size: int):
def create_docking_icon(self, size: int):
return create_session_icon(size=size)

def on_clicked(self) -> None:
Expand Down
8 changes: 7 additions & 1 deletion docking/applets/trash/applet.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class TrashApplet(Applet):
id = meta.id
name = _("Trash")
icon_name = "user-trash"
supports_system_icon = True

def __init__(self, icon_size: int, config: Config | None = None) -> None:
self._desktop = detect_desktop()
Expand All @@ -44,9 +45,14 @@ def __init__(self, icon_size: int, config: Config | None = None) -> None:
super().__init__(icon_size, config)
self.present()

def create_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
def create_docking_icon(self, size: int) -> GdkPixbuf.Pixbuf | None:
return create_trash_icon(size=size, item_count=self._item_count)

def system_icon_name(self) -> str:
if self._item_count > 0:
return "user-trash-full"
return "user-trash"

def refresh_tooltip(self) -> None:
self.item.name = trash_tooltip(item_count=self._item_count)

Expand Down
64 changes: 38 additions & 26 deletions docking/locale/docking.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: docking\n"
"Report-Msgid-Bugs-To: edumucelli@gmail.com\n"
"POT-Creation-Date: 2026-05-07 01:19+0200\n"
"POT-Creation-Date: 2026-05-08 00:57+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -180,7 +180,7 @@ msgstr ""
msgid "Applications"
msgstr ""

#: docking/applets/applications/applet.py:76
#: docking/applets/applications/applet.py:77
msgid "Search applications..."
msgstr ""

Expand Down Expand Up @@ -415,7 +415,7 @@ msgid "Show Level"
msgstr ""

#: docking/applets/calculator/applet.py:64
#: docking/applets/calculator/applet.py:81
#: docking/applets/calculator/applet.py:82
msgid "Calculator"
msgstr ""

Expand Down Expand Up @@ -634,7 +634,7 @@ msgid "Set Alarm"
msgstr ""

#: docking/applets/clock/applet.py:205 docking/applets/quicknote/applet.py:73
#: docking/applets/trash/applet.py:108 docking/applets/weather/applet.py:258
#: docking/applets/trash/applet.py:114 docking/applets/weather/applet.py:258
msgid "Cancel"
msgstr ""

Expand Down Expand Up @@ -1484,7 +1484,7 @@ msgstr ""
msgid "Screenshot"
msgstr ""

#: docking/applets/screenshot/applet.py:76
#: docking/applets/screenshot/applet.py:77
#, python-brace-format
msgid "Full Screen in {delay}s"
msgstr ""
Expand Down Expand Up @@ -1521,7 +1521,7 @@ msgstr ""
msgid "Session"
msgstr ""

#: docking/applets/session/applet.py:55 docking/applets/session/state.py:28
#: docking/applets/session/applet.py:56 docking/applets/session/state.py:28
msgid "Suspend"
msgstr ""

Expand Down Expand Up @@ -1745,19 +1745,19 @@ msgstr ""
msgid "Trash"
msgstr ""

#: docking/applets/trash/applet.py:59
#: docking/applets/trash/applet.py:65
msgid "Open Trash"
msgstr ""

#: docking/applets/trash/applet.py:62 docking/applets/trash/applet.py:109
#: docking/applets/trash/applet.py:68 docking/applets/trash/applet.py:115
msgid "Empty Trash"
msgstr ""

#: docking/applets/trash/applet.py:103
#: docking/applets/trash/applet.py:109
msgid "Empty all items from Trash?"
msgstr ""

#: docking/applets/trash/applet.py:106
#: docking/applets/trash/applet.py:112
msgid "All items in the Trash will be permanently deleted."
msgstr ""

Expand Down Expand Up @@ -1986,64 +1986,76 @@ msgstr ""
msgid "%d More in Folder"
msgstr ""

#: docking/ui/menu.py:387 docking/ui/menu.py:407 docking/ui/menu.py:423
#: docking/ui/menu.py:471
#: docking/ui/menu.py:377
msgid "Icon"
msgstr ""

#: docking/ui/menu.py:379
msgid "Docking Icon"
msgstr ""

#: docking/ui/menu.py:380
msgid "System Icon"
msgstr ""

#: docking/ui/menu.py:423 docking/ui/menu.py:443 docking/ui/menu.py:459
#: docking/ui/menu.py:507
msgid "Remove from Dock"
msgstr ""

#: docking/ui/menu.py:400
#: docking/ui/menu.py:436
msgid "Open"
msgstr ""

#: docking/ui/menu.py:430
#: docking/ui/menu.py:466
msgid "Keep in Dock"
msgstr ""

#: docking/ui/menu.py:439
#: docking/ui/menu.py:475
msgid "Close All"
msgstr ""

#: docking/ui/menu.py:439
#: docking/ui/menu.py:475
msgid "Close"
msgstr ""

#: docking/ui/menu.py:456
#: docking/ui/menu.py:492
msgid "Sort By"
msgstr ""

#: docking/ui/menu.py:464
#: docking/ui/menu.py:500
msgid "Show Hidden Files"
msgstr ""

#: docking/ui/menu.py:501
#: docking/ui/menu.py:537
msgid "Add Applet"
msgstr ""

#: docking/ui/menu.py:539
#: docking/ui/menu.py:575
msgid "No Applets Available"
msgstr ""

#: docking/ui/menu.py:546
#: docking/ui/menu.py:582
msgid "Add Separator"
msgstr ""

#: docking/ui/menu.py:556 docking/ui/settings.py:167
#: docking/ui/menu.py:592 docking/ui/settings.py:167
msgid "Preferences"
msgstr ""

#: docking/ui/menu.py:561
#: docking/ui/menu.py:597
msgid "About"
msgstr ""

#: docking/ui/menu.py:566
#: docking/ui/menu.py:602
msgid "Get Support"
msgstr ""

#: docking/ui/menu.py:573
#: docking/ui/menu.py:609
msgid "Quit"
msgstr ""

#: docking/ui/menu.py:610
#: docking/ui/menu.py:646
msgid "Window"
msgstr ""

Expand Down
Loading
Loading