Define version 1.0, reduce unnecessary navigation actions

This commit is contained in:
LordGrimmauld 2024-03-26 18:01:53 +01:00
parent 4e6afa08fd
commit 144be992b1
14 changed files with 83 additions and 48 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.5)
project(swaymux VERSION 0.1 LANGUAGES CXX)
project(swaymux VERSION 1.0 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)

View File

@ -14,14 +14,14 @@ public:
explicit CloseKeyListener(MainWindow *pWindow) : MainWindowAwareKeyListener(pWindow) {}
void handleKeyEvent(const QKeyEvent *keyEvent) const override {
auto containersToKill = getSelectedContainerIds();
auto containersToKill = getSelectedContainers();
if (containersToKill.empty())
return;
Formatter cmd;
for (auto id: containersToKill) {
cmd << "[con_id=" << id << "] kill ; ";
for (auto node: containersToKill) {
cmd << "[con_id=" << node->node.id << "] kill ; ";
}
std::cout << cmd.str() << "\n";

View File

@ -23,7 +23,7 @@ public:
if (selectedOutput != nullptr)
cmd << "focus output " << selectedOutput->node.name << " , ";
cmd << "workspace number " << createNewWorkspaceId();
cmd << "workspace " << createNewWorkspaceId({});
auto resp = SwayTreeModel::sway.sendIPC(swaymsg(0, cmd.str()));
std::cout << resp.msg << "\n";
QApplication::quit();

View File

@ -18,28 +18,57 @@ protected:
window->updateModel();
}
[[nodiscard]] std::set<int> getSelectedContainerIds() const {
std::set<int> containerIds;
[[nodiscard]] std::set<const SwayTreeNode *> getSelectedContainers() const {
std::set<const SwayTreeNode *> containerIds;
auto selectedNodes = getModel()->getSelectedNodes(getTreeView());
for (auto *container: selectedNodes) {
auto containers = container->accumulateContainers();
for (auto *containerToMove: container->accumulateContainers()) {
containerIds.insert(containerToMove->node.id);
containerIds.insert(containerToMove);
}
}
return containerIds;
}
[[nodiscard]] int createNewWorkspaceId() const {
auto used_workspaces = getModel()->getRoot()->accumulateWorkspaces();
int newWorkspace = 1; // workspace 1 is the first one. We don't expect our users to be programmers.
for (; used_workspaces.contains(std::to_string(newWorkspace)); ++newWorkspace) {}
return newWorkspace;
[[nodiscard]] std::string createNewWorkspaceId(const std::set<const SwayTreeNode *> &allowedContainers) const {
std::set<std::string> candidates;
for (auto *con: allowedContainers) {
const auto *workspace = con->findWorkspace();
if (workspace == nullptr)
continue;
if (allowedContainers.contains(workspace)) {
candidates.insert(workspace->node.name);
continue;
}
[[nodiscard]] const SwayTreeNode* findSelectedValidOutput() const {
auto containers = workspace->accumulateContainers();
if (std::all_of(containers.begin(), containers.end(), [allowedContainers](const SwayTreeNode *container) {
return allowedContainers.contains(container);
})) {
candidates.insert(workspace->node.name);
}
}
if (!candidates.empty()) {
auto res = std::min_element(candidates.begin(), candidates.end());
return *res;
}
int newWorkspace = 0;
SwayTreeNode* workspaceNode;
do {
newWorkspace++;
workspaceNode = getModel()->getRoot()->findWorkspaceByName(std::to_string(newWorkspace));
} while (workspaceNode != nullptr && workspaceNode->childCount() != 0);
return std::to_string(newWorkspace);
}
[[nodiscard]] const SwayTreeNode *findSelectedValidOutput() const {
auto availableOutputs = SwayOutputList(SwayTreeModel::sway);
auto selectedNodes = getModel()->getSelectedNodes(getTreeView());
@ -56,11 +85,11 @@ protected:
protected:
MainWindow *window;
[[nodiscard]] SwayTreeModel* getModel() const {
[[nodiscard]] SwayTreeModel *getModel() const {
return this->window->model;
}
[[nodiscard]] QTreeView* getTreeView() const {
[[nodiscard]] QTreeView *getTreeView() const {
return window->ui->treeView;
}
};

View File

@ -16,14 +16,14 @@ public:
explicit MoveToScratchpadKeyListener(MainWindow *pWindow) : MainWindowAwareKeyListener(pWindow) {}
void handleKeyEvent(const QKeyEvent *keyEvent) const override {
std::set<int> containersToScratch = getSelectedContainerIds();
auto containersToScratch = getSelectedContainers();
if (containersToScratch.empty())
return;
Formatter cmd;
for (auto id: containersToScratch) {
cmd << "[con_id=" << id << "] move scratchpad ; ";
for (auto node: containersToScratch) {
cmd << "[con_id=" << node->node.id << "] move scratchpad ; ";
}
std::cout << cmd.str() << "\n";

View File

@ -16,16 +16,16 @@ public:
explicit RestoreKeyListener(MainWindow *pWindow) : MainWindowAwareKeyListener(pWindow) {}
void handleKeyEvent(const QKeyEvent *keyEvent) const override {
std::set<int> containersToRestore = getSelectedContainerIds();
auto containersToRestore = getSelectedContainers();
if (containersToRestore.empty())
return;
Formatter cmd;
int newWorkspaceId = createNewWorkspaceId();
auto newWorkspaceId = createNewWorkspaceId(containersToRestore);
for (auto id: containersToRestore) {
cmd << "[con_id=" << id << "] move workspace " << newWorkspaceId << " ; ";
for (auto node: containersToRestore) {
cmd << "[con_id=" << node->node.id << "] move workspace " << newWorkspaceId << " ; ";
}
std::cout << cmd.str() << "\n";

View File

@ -11,18 +11,17 @@
#include "../tree/SwayTreeModel.h"
#include "../sway_bindings/swayoutputs.h"
class SwitchToKeybindListener : public AbstractKeyListener {
class SwitchToKeybindListener : public MainWindowAwareKeyListener {
public:
explicit SwitchToKeybindListener(SwayTreeModel *swayTreeModel, QTreeView *treeView) : swayTreeModel(
swayTreeModel), treeView(treeView) {}
explicit SwitchToKeybindListener(MainWindow *pWindow) : MainWindowAwareKeyListener(pWindow) {}
void handleKeyEvent(const QKeyEvent *keyEvent) const override {
auto *root = swayTreeModel->getRoot();
auto *root = getModel()->getRoot();
if (root == nullptr)
return;
Formatter cmd;
auto selectedNodes = swayTreeModel->getSelectedNodes(treeView);
auto const selectedNodes = getModel()->getSelectedNodes(getTreeView());
if (selectedNodes.empty())
return;
@ -52,10 +51,7 @@ public:
}
}
auto used_workspaces = root->accumulateWorkspaces();
int newWorkspace = 1; // workspace 1 is the first one. We don't expect our users to be programmers.
for (; used_workspaces.contains(std::to_string(newWorkspace)); ++newWorkspace) {}
auto newWorkspace = createNewWorkspaceId(selectedNodes);
std::set<int> containersToMove;
for (auto* container: selectedNodes) {
@ -72,7 +68,7 @@ public:
cmd << "[con_id=" << id << "] move container workspace " << newWorkspace << " ; ";
}
cmd << "workspace number " << newWorkspace;
cmd << "workspace " << newWorkspace;
std::cout << cmd.str() << "\n";
auto resp = SwayTreeModel::sway.sendIPC(swaymsg(0, cmd.str()));
@ -91,10 +87,6 @@ public:
[[nodiscard]] bool canAcceptKey(int key) const override {
return key == Qt::Key_Enter || key == Qt::Key_Return;
}
private:
SwayTreeModel *swayTreeModel;
QTreeView *treeView;
};

View File

@ -3,9 +3,9 @@ Swaymux aims at making navigation in sway easier. To achieve this, swaymux provi
This main interface is primarily inspired by tmux.
Swaymux can be navigated using the keyboard, with keybindings that closely follow the tmux bindings.
This project is *not* feature-complete, far from it. So far swaymux supports listing and navigating into outputs, workspaces and containers, creating a new workspace, or creating a workspace with all currently marked containers.
This project is *not* feature-complete, far from it. So far swaymux supports listing and navigating into outputs, workspaces and containers, creating a new workspace, or creating a workspace with all currently marked containers. The user can close applications without first focusing their windows, as well as view debug information (e.g. whether a window uses native wayland or xwayland). The scratchpad is supported too, both for dumping and retrieving windows from swaymux.
Planned features and fixes include closing windows from within swaymux without specifically jumping to them, better keyboard navigation through the sway tree in the main interface, a closer look at performance tweaking, as well as maybe even iconification of containers where applicable.
Planned features and fixes include a closer look at performance tweaking, as well as maybe iconification of containers where applicable.
# Installing

View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1709968316,
"narHash": "sha256-4rZEtEDT6jcgRaqxsatBeds7x1PoEiEjb6QNGb4mNrk=",
"lastModified": 1711370797,
"narHash": "sha256-2xu0jVSjuKhN97dqc4bVtvEH52Rwh6+uyI1XCnzoUyI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0e7f98a5f30166cbed344569426850b21e4091d4",
"rev": "c726225724e681b3626acc941c6f95d2b0602087",
"type": "github"
},
"original": {

View File

@ -30,7 +30,7 @@ MainWindow::MainWindow(QWidget *parent)
swayTreeKeyHandler->addListener(new HelpKeyListener(ui->help_page));
swayTreeKeyHandler->addListener(new CloseSwaymuxKeyListener());
swayTreeKeyHandler->addListener(new CreateWorkspaceKeyListener(this));
swayTreeKeyHandler->addListener(new SwitchToKeybindListener(model, ui->treeView));
swayTreeKeyHandler->addListener(new SwitchToKeybindListener(this));
swayTreeKeyHandler->addListener(new PrevOutputKeyListener(model, ui->treeView));
swayTreeKeyHandler->addListener(new NextOutputKeyListener(model, ui->treeView));
swayTreeKeyHandler->addListener(new CloseKeyListener(this));

View File

@ -55,6 +55,10 @@ namespace NodeType {
static QString toQString(NodeType nodeType) {
return QString::fromStdString(toString(nodeType));
}
[[nodiscard]] static inline bool isContainer(NodeType type) {
return type == con || type == floating_con;
}
}
namespace NodeLayout {

View File

@ -19,8 +19,8 @@ QModelIndex SwayTreeModel::findIndexByNode(const SwayTreeNode* node) const {
return idx;
}
std::set<SwayTreeNode *> SwayTreeModel::getSelectedNodes(const QTreeView *treeView) const {
std::set<SwayTreeNode *> selectedNodes;
std::set<const SwayTreeNode *> SwayTreeModel::getSelectedNodes(const QTreeView *treeView) const {
std::set<const SwayTreeNode *> selectedNodes;
if (rootItem == nullptr)
return selectedNodes;

View File

@ -50,7 +50,7 @@ public:
return findIndexByNode(getRoot()->findFocused());
}
[[nodiscard]] std::set<SwayTreeNode *> getSelectedNodes(const QTreeView *treeView) const;
[[nodiscard]] std::set<const SwayTreeNode *> getSelectedNodes(const QTreeView *treeView) const;
[[nodiscard]] SwayTreeNode *getSelectedNode(const QModelIndex &i) const;

View File

@ -73,8 +73,12 @@ public:
return accumulateMatchingChildrenRecursive(matchNodeType({NodeType::con, NodeType::floating_con}));
}
[[nodiscard]] std::set<std::string> accumulateWorkspaces() const {
auto workspaces = accumulateMatchingChildrenRecursive(matchNodeType(NodeType::workspace));
[[nodiscard]] std::set<SwayTreeNode*> accumulateWorkspaces() const {
return accumulateMatchingChildrenRecursive(matchNodeType(NodeType::workspace));
}
[[nodiscard]] std::set<std::string> accumulateWorkspacesStr() const {
auto workspaces = accumulateWorkspaces();
std::set<std::string> result;
std::transform(workspaces.begin(), workspaces.end(), std::inserter(result, result.begin()),
[](const SwayTreeNode *workspace) { return workspace->node.name; });
@ -87,6 +91,12 @@ public:
}));
}
[[nodiscard]] SwayTreeNode * findWorkspaceByName(const std::string& name) {
return const_cast<SwayTreeNode *>(findChildRecursive([name](const SwayTreeNode *test) {
return test->node.name == name && test->node.type == NodeType::workspace;
}));
}
private:
[[nodiscard]] static std::function<bool(const SwayTreeNode *)> matchNodeType(NodeType::NodeType type) {
return matchNodeType(std::vector{type});