diff --git a/compositor/sway/nebula-sway.conf b/compositor/sway/nebula-sway.conf index a2f5674..2152557 100644 --- a/compositor/sway/nebula-sway.conf +++ b/compositor/sway/nebula-sway.conf @@ -54,12 +54,12 @@ focus_follows_mouse yes # Layer-shell surfaces (desktop, top bar, launcher, window chrome) are not # xdg_shell/XWayland views and are not decorated by these settings. font pango:Ubuntu 10 -titlebar_padding 12 8 +titlebar_padding 14 10 titlebar_border_thickness 1 default_border normal 1 default_floating_border normal 1 hide_edge_borders none -title_format " %title" +title_format " %title" # class border backgr. text indicator child_border client.focused #3d4d62 #1a222e #e8ecf2 #7ea0c4 #3d4d62 diff --git a/core/windows/WindowService.cpp b/core/windows/WindowService.cpp index 075e428..8862ae3 100644 --- a/core/windows/WindowService.cpp +++ b/core/windows/WindowService.cpp @@ -146,6 +146,16 @@ int WindowService::focusedWindowDecoHeight() const return m_focusedWindowDecoHeight; } +int WindowService::focusedWindowTitleBarY() const +{ + return m_focusedWindowTitleBarY; +} + +int WindowService::focusedWindowTitleBarHeight() const +{ + return m_focusedWindowTitleBarHeight; +} + bool WindowService::focusWindow(const QString &windowId) { return m_backend->focusWindow(parseWindowId(windowId)); @@ -321,6 +331,8 @@ void WindowService::publishFocusedGeometry(const WindowInfo *focused) int decoY = 0; int decoWidth = 0; int decoHeight = 0; + int titleBarY = 0; + int titleBarHeight = 0; bool fullscreen = false; if (focused && !focused->minimized) { @@ -333,6 +345,17 @@ void WindowService::publishFocusedGeometry(const WindowInfo *focused) decoWidth = focused->decoWidth; decoHeight = focused->decoHeight; fullscreen = focused->fullscreen; + + titleBarHeight = focused->decoHeight; + if (titleBarHeight <= 0) + titleBarHeight = focused->contentY; + if (titleBarHeight <= 0) + titleBarHeight = 32; + + if (focused->decoHeight > 0 || focused->contentY > 0) + titleBarY = focused->y + focused->decoY; + else + titleBarY = focused->y - titleBarHeight; } if (m_focusedWindowFullscreen != fullscreen) { @@ -347,7 +370,9 @@ void WindowService::publishFocusedGeometry(const WindowInfo *focused) || m_focusedWindowDecoX != decoX || m_focusedWindowDecoY != decoY || m_focusedWindowDecoWidth != decoWidth - || m_focusedWindowDecoHeight != decoHeight) { + || m_focusedWindowDecoHeight != decoHeight + || m_focusedWindowTitleBarY != titleBarY + || m_focusedWindowTitleBarHeight != titleBarHeight) { m_focusedWindowX = x; m_focusedWindowY = y; m_focusedWindowWidth = width; @@ -356,6 +381,8 @@ void WindowService::publishFocusedGeometry(const WindowInfo *focused) m_focusedWindowDecoY = decoY; m_focusedWindowDecoWidth = decoWidth; m_focusedWindowDecoHeight = decoHeight; + m_focusedWindowTitleBarY = titleBarY; + m_focusedWindowTitleBarHeight = titleBarHeight; emit focusedWindowGeometryChanged(); } } diff --git a/core/windows/WindowService.hpp b/core/windows/WindowService.hpp index 43011fa..24729e7 100644 --- a/core/windows/WindowService.hpp +++ b/core/windows/WindowService.hpp @@ -37,6 +37,8 @@ class WindowService : public QObject Q_PROPERTY(int focusedWindowDecoY READ focusedWindowDecoY NOTIFY focusedWindowGeometryChanged) Q_PROPERTY(int focusedWindowDecoWidth READ focusedWindowDecoWidth NOTIFY focusedWindowGeometryChanged) Q_PROPERTY(int focusedWindowDecoHeight READ focusedWindowDecoHeight NOTIFY focusedWindowGeometryChanged) + Q_PROPERTY(int focusedWindowTitleBarY READ focusedWindowTitleBarY NOTIFY focusedWindowGeometryChanged) + Q_PROPERTY(int focusedWindowTitleBarHeight READ focusedWindowTitleBarHeight NOTIFY focusedWindowGeometryChanged) public: enum SnapEdge { @@ -69,6 +71,8 @@ public: int focusedWindowDecoY() const; int focusedWindowDecoWidth() const; int focusedWindowDecoHeight() const; + int focusedWindowTitleBarY() const; + int focusedWindowTitleBarHeight() const; Q_INVOKABLE bool focusWindow(const QString &windowId); Q_INVOKABLE bool closeWindow(const QString &windowId); @@ -118,5 +122,7 @@ private: int m_focusedWindowDecoY = 0; int m_focusedWindowDecoWidth = 0; int m_focusedWindowDecoHeight = 0; + int m_focusedWindowTitleBarY = 0; + int m_focusedWindowTitleBarHeight = 0; QTimer *m_geometryTimer = nullptr; }; diff --git a/core/windows/WindowTypes.hpp b/core/windows/WindowTypes.hpp index d20e06c..33e10e8 100644 --- a/core/windows/WindowTypes.hpp +++ b/core/windows/WindowTypes.hpp @@ -26,6 +26,8 @@ struct WindowInfo int decoY = 0; int decoWidth = 0; int decoHeight = 0; + int contentX = 0; + int contentY = 0; }; struct WorkspaceInfo diff --git a/core/windows/backends/sway/SwayWindowBackend.cpp b/core/windows/backends/sway/SwayWindowBackend.cpp index 298b8da..33c975f 100644 --- a/core/windows/backends/sway/SwayWindowBackend.cpp +++ b/core/windows/backends/sway/SwayWindowBackend.cpp @@ -66,6 +66,41 @@ bool nodeLooksLikeView(const QJsonObject &node) return !properties.isEmpty(); } +void applyFrameGeometry(WindowInfo &window, const QJsonObject &node, const QJsonObject &parent) +{ + QJsonObject rect = node.value(QStringLiteral("rect")).toObject(); + QJsonObject deco = node.value(QStringLiteral("deco_rect")).toObject(); + QJsonObject content = node.value(QStringLiteral("window_rect")).toObject(); + + int decoHeight = deco.value(QStringLiteral("height")).toInt(); + int contentY = content.value(QStringLiteral("y")).toInt(); + + if (decoHeight <= 0 && contentY <= 0 && !parent.isEmpty()) { + const QJsonObject parentDeco = parent.value(QStringLiteral("deco_rect")).toObject(); + const QJsonObject parentContent = parent.value(QStringLiteral("window_rect")).toObject(); + const int parentDecoHeight = parentDeco.value(QStringLiteral("height")).toInt(); + const int parentContentY = parentContent.value(QStringLiteral("y")).toInt(); + if (parentDecoHeight > 0 || parentContentY > 0) { + rect = parent.value(QStringLiteral("rect")).toObject(); + deco = parentDeco; + content = parentContent; + decoHeight = parentDecoHeight; + contentY = parentContentY; + } + } + + window.x = rect.value(QStringLiteral("x")).toInt(); + window.y = rect.value(QStringLiteral("y")).toInt(); + window.width = rect.value(QStringLiteral("width")).toInt(); + window.height = rect.value(QStringLiteral("height")).toInt(); + window.decoX = deco.value(QStringLiteral("x")).toInt(); + window.decoY = deco.value(QStringLiteral("y")).toInt(); + window.decoWidth = deco.value(QStringLiteral("width")).toInt(); + window.decoHeight = decoHeight; + window.contentX = content.value(QStringLiteral("x")).toInt(); + window.contentY = contentY; +} + } // namespace SwayWindowBackend::SwayWindowBackend(QObject *parent) @@ -247,7 +282,7 @@ bool SwayWindowBackend::updateFocusedGeometry() QVector windows; QVector workspaces; - collect(tree.object(), {}, {}, false, &windows, &workspaces); + collect(tree.object(), {}, {}, {}, false, &windows, &workspaces); for (WindowInfo &window : windows) { if (window.id != m_focusedWindowId) @@ -263,6 +298,8 @@ bool SwayWindowBackend::updateFocusedGeometry() && cached.decoY == window.decoY && cached.decoWidth == window.decoWidth && cached.decoHeight == window.decoHeight + && cached.contentX == window.contentX + && cached.contentY == window.contentY && cached.fullscreen == window.fullscreen) return false; cached.x = window.x; @@ -273,6 +310,8 @@ bool SwayWindowBackend::updateFocusedGeometry() cached.decoY = window.decoY; cached.decoWidth = window.decoWidth; cached.decoHeight = window.decoHeight; + cached.contentX = window.contentX; + cached.contentY = window.contentY; cached.fullscreen = window.fullscreen; return true; } @@ -328,7 +367,7 @@ void SwayWindowBackend::refresh() QVector windows; QVector workspaces; - collect(tree.object(), {}, {}, false, &windows, &workspaces); + collect(tree.object(), {}, {}, {}, false, &windows, &workspaces); QHash counts; qint64 focused = 0; @@ -481,6 +520,7 @@ bool SwayWindowBackend::isShellChrome(const QJsonObject &node) } void SwayWindowBackend::collect(const QJsonObject &node, + const QJsonObject &parent, const QString &workspace, const QString &output, bool onScratchpad, @@ -530,27 +570,18 @@ void SwayWindowBackend::collect(const QJsonObject &node, window.workspace = nextWorkspace; window.output = nextOutput; window.focused = node.value(QStringLiteral("focused")).toBool(); - window.floating = nodeIsFloating(node); + window.floating = nodeIsFloating(node) || nodeIsFloating(parent); window.fullscreen = node.value(QStringLiteral("fullscreen_mode")).toInt() > 0; window.minimized = nextScratchpad; - const QJsonObject rect = node.value(QStringLiteral("rect")).toObject(); - window.x = rect.value(QStringLiteral("x")).toInt(); - window.y = rect.value(QStringLiteral("y")).toInt(); - window.width = rect.value(QStringLiteral("width")).toInt(); - window.height = rect.value(QStringLiteral("height")).toInt(); - const QJsonObject deco = node.value(QStringLiteral("deco_rect")).toObject(); - window.decoX = deco.value(QStringLiteral("x")).toInt(); - window.decoY = deco.value(QStringLiteral("y")).toInt(); - window.decoWidth = deco.value(QStringLiteral("width")).toInt(); - window.decoHeight = deco.value(QStringLiteral("height")).toInt(); + applyFrameGeometry(window, node, parent); windows->append(window); } const QJsonArray nodes = node.value(QStringLiteral("nodes")).toArray(); for (const QJsonValue &child : nodes) - collect(child.toObject(), nextWorkspace, nextOutput, nextScratchpad, windows, workspaces); + collect(child.toObject(), node, nextWorkspace, nextOutput, nextScratchpad, windows, workspaces); const QJsonArray floating = node.value(QStringLiteral("floating_nodes")).toArray(); for (const QJsonValue &child : floating) - collect(child.toObject(), nextWorkspace, nextOutput, nextScratchpad, windows, workspaces); + collect(child.toObject(), node, nextWorkspace, nextOutput, nextScratchpad, windows, workspaces); } diff --git a/core/windows/backends/sway/SwayWindowBackend.hpp b/core/windows/backends/sway/SwayWindowBackend.hpp index 32ec2b7..1eed31a 100644 --- a/core/windows/backends/sway/SwayWindowBackend.hpp +++ b/core/windows/backends/sway/SwayWindowBackend.hpp @@ -44,6 +44,7 @@ private: const WindowInfo *findWindow(qint64 windowId) const; static bool isShellChrome(const QJsonObject &node); static void collect(const QJsonObject &node, + const QJsonObject &parent, const QString &workspace, const QString &output, bool onScratchpad, diff --git a/packages/nebula-ui/qml/Nebula/UI/Theme.qml b/packages/nebula-ui/qml/Nebula/UI/Theme.qml index 1239256..da82a3e 100644 --- a/packages/nebula-ui/qml/Nebula/UI/Theme.qml +++ b/packages/nebula-ui/qml/Nebula/UI/Theme.qml @@ -52,9 +52,9 @@ QtObject { readonly property int topBarHeight: 40 readonly property int controlHeight: 28 readonly property int searchHeight: 36 - readonly property int trafficLightSize: 12 + readonly property int trafficLightSize: 18 readonly property int trafficLightGap: 8 - readonly property int windowChromeInset: 10 + readonly property int windowChromeInset: 12 readonly property int fontSize: 13 readonly property int fontSizeApp: 12 diff --git a/shells/desktop/shell/qml/components/TrafficLightButton.qml b/shells/desktop/shell/qml/components/TrafficLightButton.qml index e384a42..8d55d33 100644 --- a/shells/desktop/shell/qml/components/TrafficLightButton.qml +++ b/shells/desktop/shell/qml/components/TrafficLightButton.qml @@ -13,8 +13,8 @@ Item { signal clicked() - implicitWidth: Theme.trafficLightSize + 2 - implicitHeight: Theme.trafficLightSize + 2 + implicitWidth: Theme.trafficLightSize + 6 + implicitHeight: Theme.trafficLightSize + 6 Accessible.role: Accessible.Button Accessible.name: root.label Accessible.onPressAction: root.clicked() @@ -50,17 +50,20 @@ Item { Canvas { id: glyph anchors.centerIn: parent - width: 8 - height: 8 + width: Math.round(Theme.trafficLightSize * 0.58) + height: width antialiasing: true opacity: root.showGlyph ? 1 : 0 visible: opacity > 0.01 + onWidthChanged: requestPaint() + onHeightChanged: requestPaint() onPaint: { const ctx = getContext("2d") ctx.reset() + ctx.scale(width / 8, height / 8) ctx.strokeStyle = Qt.rgba(0.18, 0.12, 0.1, 0.78) ctx.fillStyle = ctx.strokeStyle - ctx.lineWidth = 1.15 + ctx.lineWidth = 1.35 ctx.lineCap = "round" ctx.lineJoin = "round" diff --git a/shells/desktop/shell/qml/surfaces/WindowChromeSurface.qml b/shells/desktop/shell/qml/surfaces/WindowChromeSurface.qml index 0814946..d6dad62 100644 --- a/shells/desktop/shell/qml/surfaces/WindowChromeSurface.qml +++ b/shells/desktop/shell/qml/surfaces/WindowChromeSurface.qml @@ -10,15 +10,12 @@ Window { readonly property bool active: WindowService.hasFocusedWindow && !WindowService.focusedWindowFullscreen && !ShellState.launcherOpen - readonly property int chromeX: WindowService.focusedWindowX - + WindowService.focusedWindowDecoX - + Theme.windowChromeInset + readonly property int chromeX: WindowService.focusedWindowX + Theme.windowChromeInset readonly property int chromeY: { - const top = WindowService.focusedWindowY + WindowService.focusedWindowDecoY - const decoHeight = WindowService.focusedWindowDecoHeight - if (decoHeight <= 0) - return top + 6 - return top + Math.max(0, Math.round((decoHeight - height) / 2)) + const barY = WindowService.focusedWindowTitleBarY + const barH = Math.max(WindowService.focusedWindowTitleBarHeight, height) + const centered = barY + Math.round((barH - height) / 2) + return centered - Theme.topBarHeight } title: qsTr("Nebula Window Chrome")