Add Nebula.Windows with Sway IPC backend

Introduces a new `core/windows` module (`Nebula.Windows`) with `WindowService`, window/workspace models, and a Sway IPC backend for live window tracking and control (focus, close, move/resize, snap, maximize/restore, minimize, workspace switch). The desktop shell now links this module, uses compositor focus for top-bar active app text, and adds an Open Windows section in the launcher with focus/close actions.

Also updates application metadata handling to parse `StartupWMClass` and map window `app_id`/class to friendly names and icons, and revises Sway config/docs for the new floating-first Desktop 0.2 behavior with temporary compositor-provided decorations.
This commit is contained in:
2026-08-26 18:20:56 +12:00
parent 8c07c10050
commit 2f59d3c115
29 changed files with 2177 additions and 35 deletions
+114
View File
@@ -0,0 +1,114 @@
#include "WindowModel.hpp"
#include <utility>
WindowModel::WindowModel(QObject *parent)
: QAbstractListModel(parent)
{
}
int WindowModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_windows.size();
}
int WindowModel::count() const
{
return m_windows.size();
}
QVariant WindowModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_windows.size())
return {};
const WindowInfo &window = m_windows.at(index.row());
switch (role) {
case WindowIdRole:
return QString::number(window.id);
case TitleRole:
return window.title;
case AppIdRole:
return window.appId;
case WindowClassRole:
return window.windowClass;
case WorkspaceRole:
return window.workspace;
case FocusedRole:
return window.focused;
case FloatingRole:
return window.floating;
case FullscreenRole:
return window.fullscreen;
case XRole:
return window.x;
case YRole:
return window.y;
case WidthRole:
return window.width;
case HeightRole:
return window.height;
case DisplayNameRole:
case Qt::DisplayRole:
return window.displayName.isEmpty() ? window.title : window.displayName;
case IconNameRole:
return window.iconName;
case MinimizedRole:
return window.minimized;
case MaximizedRole:
return window.maximized;
case OutputRole:
return window.output;
default:
return {};
}
}
QHash<int, QByteArray> WindowModel::roleNames() const
{
return {
{WindowIdRole, QByteArrayLiteral("windowId")},
{TitleRole, QByteArrayLiteral("title")},
{AppIdRole, QByteArrayLiteral("appId")},
{WindowClassRole, QByteArrayLiteral("windowClass")},
{WorkspaceRole, QByteArrayLiteral("workspace")},
{FocusedRole, QByteArrayLiteral("focused")},
{FloatingRole, QByteArrayLiteral("floating")},
{FullscreenRole, QByteArrayLiteral("fullscreen")},
{XRole, QByteArrayLiteral("x")},
{YRole, QByteArrayLiteral("y")},
{WidthRole, QByteArrayLiteral("width")},
{HeightRole, QByteArrayLiteral("height")},
{DisplayNameRole, QByteArrayLiteral("displayName")},
{IconNameRole, QByteArrayLiteral("iconName")},
{MinimizedRole, QByteArrayLiteral("minimized")},
{MaximizedRole, QByteArrayLiteral("maximized")},
{OutputRole, QByteArrayLiteral("output")},
};
}
void WindowModel::setWindows(QVector<WindowInfo> windows)
{
beginResetModel();
m_windows = std::move(windows);
endResetModel();
emit countChanged();
}
const WindowInfo *WindowModel::windowAt(int row) const
{
if (row < 0 || row >= m_windows.size())
return nullptr;
return &m_windows.at(row);
}
const WindowInfo *WindowModel::windowById(qint64 windowId) const
{
for (const WindowInfo &window : m_windows) {
if (window.id == windowId)
return &window;
}
return nullptr;
}