Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(systray): added systray widget #155

Merged
merged 1 commit into from
Mar 17, 2025

Conversation

Video-Nomad
Copy link
Contributor

@Video-Nomad Video-Nomad commented Mar 17, 2025

A system tray widget that works similar to the original Windows systray.

Functionality:

  • Shows the system tray icons that can be pinned into a separate container and the unpinned icons can be hidden using the collapse toggle button (by default on the left of the widget).
  • Pinning can be done in two ways:
    • by holding a modifier key (alt by default) + LMB click.
    • by dragging and dropping the icon to the pinned container.
  • Icons can be repositioned by dragging and dropping.
  • Icons positions and pinned/unpinned state will be saved automatically and restored on the next YASB launch.
  • If system tray widget is added to multiple monitors - each monitor will have its separate icon position and pinned/unpinned saved state. It's NOT shared between systray widgets.

Known limitations:

  • Systray widget will not show icons for apps if they ignore "TaskbarCreated" message. Meaning that if the original developers decided to ignore this message - their systray icons will not be shown. It's rare, but there are such cases (NVIDIA App for example). This is NOT a YASB bug.
  • In rare cases systray icon might ignore click events if the original application was already running before YASB was started. Example: Epic Games Launcher. No solution for this so far.

Some technical stuff:
It works by creating a process that intercepts the messages meant for the real systray, extracting all the data it needs (icon, tooltip, callback, etc.) and forwarding those messages to the original system tray to not interrupt its functionality.
The main trick here is to create a window that has the exact same class name as the original system tray and set it as topmost to get the messages priority. It needs to reset its "topmost" state every 100ms so that Windows keeps sending messages to it.
When the window gets the WM_COPYDATA message, this message will contain all the necessary data for a fully functional systray icon: icon handle, original window handle, callback for clicks, etc.

Example config:

systray:
  type: "yasb.systray.SystrayWidget"
  options:
    class_name: "systray"
    label_collapsed: ""
    label_expanded: ""
    label_position: "left"
    icon_size: 16
    pin_click_modifier: "alt"
    show_unpinned: false
    show_battery: false
    show_volume: true

Example CSS:

.systray {
    background: transparent;
    border: None;
    margin: 0;
}

.systray .unpinned-container {
    background: darkblue;
    border-radius: 8px;
}

.systray .pinned-container {
    background: transparent;
}

.systray .pinned-container[forceshow=true] {
    background: red;
}

.systray .button {
    border-radius: 4px;
    padding: 2px 2px;
}

.systray .button:hover {
    background: #727272;
}

.systray .button[dragging=true] {
    background: orange;
    border-color: #FF8800;
}

.systray .unpinned-visibility-btn {
    border-radius: 4px;
    height: 20px;
    width: 16px;
}

.systray .unpinned-visibility-btn:checked {
    background: darkblue;
}

.systray .unpinned-visibility-btn:hover {
    border: 1px solid #AAAAAA;
    border-radius: 4px;
    border-color: #AAAAAA;
}

Special thanks:
Special thanks to Zebar team and especially @lars-berger and @HolbyFPV for their fantastic systray-util rust crate. It would have been much harder to implement the Python version without their work. And of course the Managed Shell team for the original idea of intercepting systray messages.

chore(docs): updated the docs adding systray widget details
chore(readme): updated readme adding the systray widget
fix(tray): removed self.hide() call from remove_tray_icon method
@Video-Nomad Video-Nomad force-pushed the feat/systray-widget branch from fd39b82 to a56fd8d Compare March 17, 2025 22:07
@amnweb amnweb merged commit 302cd81 into amnweb:main Mar 17, 2025
@Video-Nomad Video-Nomad deleted the feat/systray-widget branch March 17, 2025 22:19
@Video-Nomad Video-Nomad mentioned this pull request Mar 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants