Add focused window controls to top bar

Introduces a new `WindowControls` component in the desktop top bar with minimize, maximize/restore, and close actions for the focused window. `WindowService` now exposes focused-window presence/maximized state plus focused-window convenience actions to support this UI.

Also extends Nebula UI with a reusable `NebulaToolTip`, keyboard/pressed-state support in `NebulaIconButton`, new window-control glyphs in `NebulaIcon`, and theme tokens for pressed/danger hover states, with CMake and README updates to register/document the new components.
This commit is contained in:
2026-08-26 22:26:29 +12:00
parent e96a22eb9e
commit 08e67845a5
11 changed files with 329 additions and 4 deletions
+47 -1
View File
@@ -67,6 +67,11 @@ int WindowService::windowCount() const
return m_model->count();
}
bool WindowService::hasFocusedWindow() const
{
return !m_focusedWindowId.isEmpty();
}
QString WindowService::focusedWindowId() const
{
return m_focusedWindowId;
@@ -87,6 +92,11 @@ QString WindowService::focusedApplicationName() const
return m_focusedApplicationName;
}
bool WindowService::focusedWindowMaximized() const
{
return m_focusedWindowMaximized;
}
bool WindowService::focusWindow(const QString &windowId)
{
return m_backend->focusWindow(parseWindowId(windowId));
@@ -112,6 +122,38 @@ bool WindowService::minimizeWindow(const QString &windowId)
return m_backend->minimizeWindow(parseWindowId(windowId));
}
bool WindowService::minimizeFocusedWindow()
{
if (m_focusedWindowId.isEmpty())
return false;
return minimizeWindow(m_focusedWindowId);
}
bool WindowService::toggleMaximizeFocusedWindow()
{
if (m_focusedWindowId.isEmpty())
return false;
const bool restore = m_focusedWindowMaximized;
const bool ok = restore ? restoreWindow(m_focusedWindowId)
: maximizeWindow(m_focusedWindowId);
if (!ok)
return false;
if (m_focusedWindowMaximized == restore) {
m_focusedWindowMaximized = !restore;
emit focusChanged();
}
return true;
}
bool WindowService::closeFocusedWindow()
{
if (m_focusedWindowId.isEmpty())
return false;
return closeWindow(m_focusedWindowId);
}
bool WindowService::setWindowFloating(const QString &windowId, bool floating)
{
return m_backend->setWindowFloating(parseWindowId(windowId), floating);
@@ -154,6 +196,7 @@ void WindowService::syncFromBackend()
QString title;
QString appId;
QString appName;
bool maximized = false;
if (const WindowInfo *focused = m_model->windowById(focusedId)) {
if (!focused->minimized) {
@@ -161,17 +204,20 @@ void WindowService::syncFromBackend()
title = focused->title;
appId = focused->appId.isEmpty() ? focused->windowClass : focused->appId;
appName = focused->displayName;
maximized = focused->maximized;
}
}
if (m_focusedWindowId != windowId
|| m_focusedWindowTitle != title
|| m_focusedApplicationId != appId
|| m_focusedApplicationName != appName) {
|| m_focusedApplicationName != appName
|| m_focusedWindowMaximized != maximized) {
m_focusedWindowId = windowId;
m_focusedWindowTitle = title;
m_focusedApplicationId = appId;
m_focusedApplicationName = appName;
m_focusedWindowMaximized = maximized;
emit focusChanged();
}
}