deprecation notice to plugin compatibility shims
- When qml-common became a thing, we left qs.Widgets shims in place for
plugin compatibility
- It's annoying to need to maintain them when qml-common has any change,
add a warning so we can safely deprecate them in the future
- One shot rename the common stuff to D to make it shorter and nicer to
write
* feat(dock): push neighboring icons apart on magnification
* fix(dock): cap magnification expansion and disable it in taskbar mode
* fix(dock): scale pushed icon offsets to the capped magnification expansion
* fix(dock): avoid binding loop when reading expansion ratio
- caps lock, power profile, idle inhibitor and charging pulse in the island
- microphone mute and volume as a level transient
- privacy slot while mic, camera or screen share is in use
- duo battery style: arc around the network glyph with signal dots
* feat(dock): add proximity icon magnification with customizable curves
* fix(dock): restrict proximity magnification to dock and add translator context
* fix: keep tray order stable when tooltipTitle changes dynamically
Revert the earlier simplistic key approach: keep getTrayItemKey()
unchanged (id::tooltipTitle) so duplicate-id disambiguation (#1542)
is preserved and persisted id::tooltipTitle entries keep matching.
Instead make persisted-key lookups tolerant of dynamic tooltipTitle:
resolveOrderIndex() in sortByPreferredOrder() does exact match first,
falling back to prefix matching an id::* entry only when the id has a
single instance. findSavedTrayKey()/isTrayIdHidden() replace direct
SessionData.isHiddenTrayId() calls and showTrayId() now receives the
actual saved key.
Prefix fallback is gated by trayItemCountForId() to single-instance
ids only, so multiple items sharing an id always match exactly and
hiding/ordering one never affects the others.
* fix: bail on ambiguous saved tray keys in fallback
* tray: use occurrence index as stable key, add one-time session migration
* tray: pin stable key per item instance so it never flips when siblings come and go
* tray: assign unique per-id sequential keys to avoid collision after sibling exit
* tray: reuse free key slots so a restarted singleton keeps its plain id
* feat(bar): show edit window rule in focused app popout when rule exists
Check existing DMS-managed window rules when the focused window popout
opens. If a matching rule exists for the focused window, show
'Edit window rule' and open the rule in edit mode via showEdit().
Otherwise, keep showing 'Add window rule' for new rule creation.
* fix(bar): address PR review feedback for focused window rules
- Clear matchingRule up front in checkMatchingRule() to prevent showing previous window's rule while async query is in flight
- Reuse existing catalog term 'Edit Window Rule' to leverage existing translations
them towards theme
controlCenter: make a real grid, add numerous more tile varieties, add
icon size option
surfaces: fix elevation levels in general, and shift them down
settings: restyles and re-org
sliders: scale down universally
- This adds native persistent slots per compositor such as Hyprland, Mango & Sway
- Not all compositors record preloading workspaces so those options return as minimum placeholders
Hyprland's workspace refactor (hyprwm/Hyprland#16140) broke
Hyprland.focusedWorkspace.monitor as a way to find the focused screen
(quickshell-mirror/quickshell#1119). Quickshell already exposes
Hyprland.focusedMonitor directly, so read the monitor from there
* idle inhibitor: add duration presets and timed expiry
Right-click the bar widget or expand the Control Center pill to keep the
system awake for a set duration rather than only indefinitely. The deadline
is persisted so it survives a shell restart, and is re-checked on resume
because timers stop across suspend.
Adds `dms ipc call inhibit enableFor <minutes>`, and `inhibit status` now
reports the remaining time.
* idle inhibitor: add translator context to new duration strings
* refactor(systemtray): convert overflow menu and context menu to DankPopout
* fix(systemtray): correct window-local anchor position mapping and enable contentHandlesKeys for tray menu keyboard navigation
* fix(systemtray): force active focus on tray menu content
* fix(systemtray): address inline PR review feedback
- Force active focus on open for key navigation
- Unregister menu only if matching active instance
- Map anchor trigger position relative to screen origin
* fix(systemtray): gate onMenuOpenChanged on useOverflowPopup
- Prevent registering overflowPopout in inline expansion mode
* fix(systemtray): correct context menu positioning in overflow popout
- Handle screen global coordinate mapping for both connected and standalone popout modes
* fix(systemtray): handle per-screen backend and vertical bar anchor Y offset
- Use overflowPopout.useConnectedBackend for per-screen discriminator
- Add vertical anchor Y centering offset in showForTrayItem
* fix(systemtray): center overflow popout on vertical bar caret
- Add vertical orientation anchor Y offset in _openOverflowAt
* fix(systemtray): clear submenu entryStack on tray menu open
- Expose entryStack alias on trayMenuContentRoot
- Clear entryStack on trayMenuPopout onOpened to avoid submenu state leakage
* updated dank to support aqueous
* further updates
* aqueous support work
* updated to support workspace options better in aqueous
* added blur to dms shell for aqueous
* added keybinding update
* remove unneeded blur
* clean up per claud review
* swapped to ipc
* aqueous: move IPC integration to QML, delete test fixtures (since we
dont have a QML test structure even setup), delete in-repo docs
* fix staticcheck
* aqueous: consolidate more aqueous specific stuff
* add aqueous logo
---------
Co-authored-by: Zoey <[email protected]>
* fix(bar): keep widget content on whole pixels
Bar widgets centre their content inside a box of
widgetThickness - horizontalPadding * 2, and Qt rounds the offset of a
centred anchor to whole pixels. When the content and that box have
different parity the exact offset falls on a half pixel, Qt rounds it
away from zero and the content renders half a pixel above the centre.
Widgets drawn from rectangles, such as the workspace switcher, compute
their own snapped height and stay exactly centred, which is why they
look right next to everything else in #3074.
With the stock configuration this is the normal case: the bar is 40 px,
the box is 14 px and barIconSize returns round((40 / 48) * 18) = 15.
Two sizes now round to whole pairs of pixels. The icon size, so that it
matches the parity of the box, and widgetThickness, because the box
inherits its parity from there (the padding is doubled, so it never
changes it). Going through every innerPadding from 0 to 16, nine of the
seventeen configurations currently put the icon on a half pixel and none
of them do afterwards. The 48 px default bar is untouched, the sizes
that change do so by one pixel.
Measured in a nested Hyprland at scale 1, ink centroid of the widget
content relative to the centre of its pill, before and after:
launcher -0.50 +0.00
weather -0.81 -0.52
clipboard -1.49 -0.93
notifications +0.03 +0.46
battery -0.63 -0.49
bluetooth -0.53 -0.03
Every icon moves down by half a pixel. What remains matches the ink
asymmetry of the individual glyphs, measured separately at an exact
position, and is not a placement error.
Two things this does not cover. On a fractional or HiDPI scale
horizontalPadding can land on a half pixel, which puts the box back
between the grid lines, and a text only widget such as the clock has a
line box of 15 px at the default font size, which cannot sit exactly on
the centre of a 28 px pill no matter how it is rounded.
Closes#3074
* fix(bar): give bar text a line box that can be centred
A widget without an icon is centred as one line box, and the same parity
rule applies to it as to the icon in the commit before this one. At
fontSizeSmall the line box of the bar font is 15 px tall, so on a 28 px
pill it cannot sit on the centre no matter which way the offset is
rounded, and the clock reads about half a pixel high next to everything
else. One step up the box is 16 px and it lands on the grid.
Only the branch the standard bar uses changes. The 0.9 branch below a
scale of 0.75 already comes out at 14 px, and the fontSizeMedium branch
above 1.25 is left alone because there the next even box is two steps
away, which would be a bigger change than this is worth.
Same session and same measurement as the previous commit, ink centroid
of the widget content relative to the centre of its pill:
clock and date -0.58 +0.05
battery -0.63 -0.05
weather -0.82 -0.28
The bar font goes from 12 px to 13 px, which is the visible part of this
and the reason it is a separate commit. It can be dropped without
touching the icon fix.
* bar: snap widget thickness to even device pixels and pick text sizes by line box parity
* bar: snap bar thickness to even so the pill centres on whole pixels
* bar: share the bar thickness formula so dock, osd and popouts stay in step
* dock: read the bar clearance at the compositor screen scale
---------
Co-authored-by: bbedward <[email protected]>
* fix(bar): prevent focused app widget from blanking and live-sync popout on focus switch
* fix(bar): use per-output is_active flag for Niri workspace resolution
* fix(bar): gate Niri lastFocusedWindowId fallback on active popout visibility
The battery popout already surfaces the UPower energy rate, but the bar
widget could only show percentage and remaining time, so telling how fast
the battery is charging or draining meant opening the popout (or running a
third-party plugin that polls /sys/class/power_supply).
Add two independent, opt-in toggles to the battery widget - "Show Charge
Rate" and "Show Discharge Rate" - that render the signed rate next to the
existing text, e.g. "+28W" while charging and "77% (2h 11m) -19W" while
draining. Both default to off, and follow the established pattern: a global
default in SettingsData plus a per-widget-instance override.
The value comes from BatteryService.changeRate, so there is no extra process
or polling. It hides itself when the rate is below 0.05W (idle, fully
charged, or sitting at a charge limit) so it costs no bar space unless it
has something to say. Precision adapts to keep the pill narrow: one decimal
below 10W, whole watts above; vertical bars get a whole-watt line.
CompositorService detects the compositor asynchronously: compositorInitTimer
waits 100ms before calling detectCompositor(), which shells out to identify
the Wayland socket owner and only sets the compositor flags in its callback.
KeyboardLayoutName gates its initial updateLayout() on CompositorService.isHyprland
in Component.onCompleted, which always runs before that lands, so the call never
happens and the widget renders with an empty label until the first activelayout
event, i.e. until the user switches layout by hand.
Re-run updateLayout() when the detected compositor changes. updateLayout()
already branches on the compositor internally, so one handler covers every
backend.
Co-authored-by: shalevc1098 <[email protected]>
* feat: add cellular support
* fix(settings): resolve cellular tab index collision
* fix: address review feedback for cellular support
Bump the network API version, drop the deleted term freeze file,
regenerate the settings search index, and reuse existing translation
terms where they already cover the new cellular UI.
* fix: do not assign readonly userPreference when switching network type
Assigning SettingsData.networkPreference through the readonly alias
threw on click, so the Cellular segment in Control Center did nothing.
* fix: make Cellular selectable and the modem row clickable
The Control Center type switcher was bound only to preference, so
Cellular snapped back to Ethernet. The settings modem row highlighted
on hover but had no click handler. Show the modem as a connect target
when no APN profiles exist.
* fix: create a GSM profile when connecting cellular with none saved
Clicking Mobile broadband only activated an existing profile, so a
modem with no APN did nothing useful. Fall back to AddAndActivate
like ethernet connect.
* fix: address second round of cellular review feedback
- Guard cellularDevices map with devMutex via snapshot/by-iface/set/remove
helpers, mirroring the existing ethernet and wifi accessors
- Regenerate mocks with mockery v3 instead of hand-editing mock_Backend.go
- Reuse refreshCellularState() in SetCellularEnabled, DisconnectCellular,
DisconnectCellularDevice, ActivateCellularConnection and device removal
- Wrap retryErr rather than the first error when the auto-config retry fails
- Use Theme.surfaceTextSecondary/primaryHover/primaryHoverLight in the
cellular-off state instead of hand-rolled Qt.rgba values
- Drop the duplicate "No devices found" label so it can no longer render
twice with no modem and no saved config
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* fix: address remaining cellular review comments
- Toast "Failed to toggle cellular" instead of always "enable"
- Remove unused enableCellularRadio() wrappers
- Store Control Center network type as a value, not a list index
* fix: pass nmcli connection selector as separate argument
`nmcli con mod uuid:<UUID>` is not valid syntax -- nmcli expects the
selector keyword as its own argument, so every priority update failed
with "no such connection profile". Because practically every NM profile
has a UUID, this silently disabled autoconnect-priority and route-metric
updates for ethernet and WiFi as well as cellular.
Also fix the Cellular settings status text, which said "Connecting..."
for a flag that only tracks radio enable/disable, and add the seven
cellular terms that were missing from the translation catalogs.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* apply review comments
* fix lints
* update debug doc
* fix style inconsistencies
---------
Co-authored-by: Yechiel Worenklein <[email protected]>
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
Co-authored-by: bbedward <[email protected]>