diff --git a/CMakeLists.txt b/CMakeLists.txt index 8727485..783b700 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,10 @@ set(PROJECT_SOURCES Keys/JumpOutputKeyListener.h Keys/PrevOutputKeyListener.h Keys/NextOutputKeyListener.h + Keys/CloseKeyListener.h + Keys/MoveToScratchpadKeyListener.h + Keys/MoveToScratchpadKeyListener.h + Keys/RestoreKeyListener.h ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) diff --git a/Keys/CloseKeyListener.h b/Keys/CloseKeyListener.h new file mode 100644 index 0000000..14be07a --- /dev/null +++ b/Keys/CloseKeyListener.h @@ -0,0 +1,58 @@ +// +// Created by grimmauld on 11.03.24. +// + +#ifndef SWAYMUX_CLOSEKEYLISTENER_H +#define SWAYMUX_CLOSEKEYLISTENER_H + + +#include "AbstractKeyListener.h" + +class CloseKeyListener : public AbstractKeyListener { +public: + explicit CloseKeyListener(MainWindow *pWindow) : window(pWindow) {} + + void handleKeyEvent(const QKeyEvent *keyEvent) const override { + auto selectedNodes = window->model->getSelectedNodes(window->ui->treeView); + + std::set containersToKill; + for (auto* container: selectedNodes) { + auto containers = container->accumulateContainers(); + for (auto* containerToMove : container->accumulateContainers()) { + containersToKill.insert(containerToMove->node.id); + } + } + + if (containersToKill.empty()) + return; + + Formatter cmd; + for (auto id: containersToKill) { + cmd << "[con_id=" << id << "] kill ; "; + } + + std::cout << cmd.str() << "\n"; + auto resp = SwayTreeModel::sway.sendIPC(swaymsg(0, cmd.str())); + std::cout << resp.msg << "\n"; + + usleep(100000); // give sway time to react. Not great but works i guess. + window->updateModel(); + } + + std::string getDescription() override { + return "Close selected windows"; + } + + std::string getKeyText() override { + return "Q / DEL"; + } + + [[nodiscard]] bool canAcceptKey(int key) const override { + return key == Qt::Key_Q || key == Qt::Key_Delete; + } +private: + MainWindow *window; +}; + + +#endif //SWAYMUX_CLOSEKEYLISTENER_H diff --git a/Keys/MoveToScratchpadKeyListener.h b/Keys/MoveToScratchpadKeyListener.h new file mode 100644 index 0000000..9fbcce4 --- /dev/null +++ b/Keys/MoveToScratchpadKeyListener.h @@ -0,0 +1,61 @@ +// +// Created by grimmauld on 11.03.24. +// + +#ifndef SWAYMUX_MOVETOSCRATCHPADKEYLISTENER_H +#define SWAYMUX_MOVETOSCRATCHPADKEYLISTENER_H + + +#include "AbstractKeyListener.h" +#include "../mainwindow.h" +#include "../sway_bindings/Formatter.h" +#include "../tree/SwayTreeModel.h" + +class MoveToScratchpadKeyListener : public AbstractKeyListener { +public: + explicit MoveToScratchpadKeyListener(MainWindow *pWindow) : window(pWindow) {} + + void handleKeyEvent(const QKeyEvent *keyEvent) const override { + auto selectedNodes = window->model->getSelectedNodes(window->ui->treeView); + + std::set containersToScratch; + for (auto* container: selectedNodes) { + auto containers = container->accumulateContainers(); + for (auto* containerToMove : container->accumulateContainers()) { + containersToScratch.insert(containerToMove->node.id); + } + } + + if (containersToScratch.empty()) + return; + + Formatter cmd; + for (auto id: containersToScratch) { + cmd << "[con_id=" << id << "] move scratchpad ; "; + } + + std::cout << cmd.str() << "\n"; + auto resp = SwayTreeModel::sway.sendIPC(swaymsg(0, cmd.str())); + std::cout << resp.msg << "\n"; + + usleep(100000); // give sway time to react. Not great but works i guess. + window->updateModel(); + } + + std::string getDescription() override { + return "Move selected windows to scratchpad"; + } + + std::string getKeyText() override { + return "-"; + } + + [[nodiscard]] bool canAcceptKey(int key) const override { + return key == Qt::Key_Minus; + } +private: + MainWindow *window; +}; + + +#endif //SWAYMUX_MOVETOSCRATCHPADKEYLISTENER_H diff --git a/Keys/RestoreKeyListener.h b/Keys/RestoreKeyListener.h new file mode 100644 index 0000000..2c2be58 --- /dev/null +++ b/Keys/RestoreKeyListener.h @@ -0,0 +1,65 @@ +// +// Created by grimmauld on 11.03.24. +// + +#ifndef SWAYMUX_RESTOREKEYLISTENER_H +#define SWAYMUX_RESTOREKEYLISTENER_H + + +#include "AbstractKeyListener.h" +#include "../mainwindow.h" +#include "../sway_bindings/Formatter.h" +#include "../tree/SwayTreeModel.h" + +class RestoreKeyListener : public AbstractKeyListener { +public: + explicit RestoreKeyListener(MainWindow *pWindow) : window(pWindow) {} + + void handleKeyEvent(const QKeyEvent *keyEvent) const override { + auto selectedNodes = window->model->getSelectedNodes(window->ui->treeView); + + std::set containersToScratch; + for (auto* container: selectedNodes) { + auto containers = container->accumulateContainers(); + for (auto* containerToMove : container->accumulateContainers()) { + containersToScratch.insert(containerToMove->node.id); + } + } + + if (containersToScratch.empty()) + return; + + Formatter cmd; + auto used_workspaces = window->model->getRoot()->accumulateWorkspaces(); + int i = 1; // workspace 1 is the first one. We don't expect our users to be programmers. + for (; used_workspaces.contains(std::to_string(i)); ++i) {} + + for (auto id: containersToScratch) { + cmd << "[con_id=" << id << "] move workspace " << i << " ; "; + } + + std::cout << cmd.str() << "\n"; + auto resp = SwayTreeModel::sway.sendIPC(swaymsg(0, cmd.str())); + std::cout << resp.msg << "\n"; + + usleep(100000); // give sway time to react. Not great but works i guess. + window->updateModel(); + } + + std::string getDescription() override { + return "Restore selected windows to a new workspace"; + } + + std::string getKeyText() override { + return "+"; + } + + [[nodiscard]] bool canAcceptKey(int key) const override { + return key == Qt::Key_Plus; + } +private: + MainWindow *window; +}; + + +#endif //SWAYMUX_RESTOREKEYLISTENER_H diff --git a/Keys/SwitchToKeybindListener.h b/Keys/SwitchToKeybindListener.h index f18749b..37a147f 100644 --- a/Keys/SwitchToKeybindListener.h +++ b/Keys/SwitchToKeybindListener.h @@ -69,7 +69,7 @@ public: return; for (auto id: containersToMove) { - cmd << "[con_id=" << id << "] focus , move container workspace " << newWorkspace << " ; "; + cmd << "[con_id=" << id << "] move container workspace " << newWorkspace << " ; "; } cmd << "workspace number " << newWorkspace; diff --git a/mainwindow.cpp b/mainwindow.cpp index 766d325..aba575b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -8,7 +8,9 @@ #include "Keys/SwitchToKeybindListener.h" #include "Keys/PrevOutputKeyListener.h" #include "Keys/NextOutputKeyListener.h" -#include "Keys/NextOutputKeyListener.h" +#include "Keys/CloseKeyListener.h" +#include "Keys/MoveToScratchpadKeyListener.h" +#include "Keys/RestoreKeyListener.h" #include #include @@ -18,19 +20,9 @@ MainWindow::MainWindow(QWidget *parent) ui->setupUi(this); model = new SwayTreeModel(ui->treeView); - // modelUpdateTimer = new QTimer(this); - // connect(modelUpdateTimer, &QTimer::timeout, this, QOverload<>::of(&MainWindow::update)); - // modelUpdateTimer->start(1000); - ui->statusbar->showMessage(QString::fromStdString("Press ? for keybind listing")); - ui->treeView->setModel(model); - ui->treeView->expandAll(); - ui->treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); - ui->treeView->selectionModel()->setCurrentIndex(model->findFocusedWindowIndex(), QItemSelectionModel::ClearAndSelect); - - for (int c = 0; c < model->columnCount(); ++c) - ui->treeView->resizeColumnToContents(c); + updateModel(); swayTreeKeyHandler = new KeyHandler(); swayTreeKeyHandler->addListener(new HelpKeyListener(ui->help_page)); @@ -39,6 +31,9 @@ MainWindow::MainWindow(QWidget *parent) swayTreeKeyHandler->addListener(new SwitchToKeybindListener(model, ui->treeView)); swayTreeKeyHandler->addListener(new PrevOutputKeyListener(model, ui->treeView)); swayTreeKeyHandler->addListener(new NextOutputKeyListener(model, ui->treeView)); + swayTreeKeyHandler->addListener(new CloseKeyListener(this)); + swayTreeKeyHandler->addListener(new MoveToScratchpadKeyListener(this)); + swayTreeKeyHandler->addListener(new RestoreKeyListener(this)); closeHelpKeyHandler = new KeyHandler(ui->tree_page); closeHelpKeyHandler->addListener(new CloseHelpKeyListener(ui->tree_page)); @@ -58,8 +53,25 @@ MainWindow::~MainWindow() { delete closeHelpKeyHandler; } -void MainWindow::keyPressEvent(QKeyEvent *event) { - if (event->key() == Qt::Key_W) { - qDebug() << "hi"; - } -} \ No newline at end of file +void MainWindow::updateModel() const { + ui->treeView->setModel(nullptr); + auto *focused = model->getRoot() == nullptr ? nullptr : model->getRoot()->findFocused(); + int id; + if (focused != nullptr) + id = focused->node.id; + + model->generateRoot(); + if (focused == nullptr) + focused = model->getRoot()->findFocused(); + else + focused = model->getRoot()->findById(id); + + if (focused != nullptr) + focused->node.focused = true; + + ui->treeView->setModel(model); + ui->treeView->expandAll(); + ui->treeView->selectionModel()->setCurrentIndex(model->findIndexByNode(focused), QItemSelectionModel::ClearAndSelect); + for (int c = 0; c < model->columnCount(); ++c) + ui->treeView->resizeColumnToContents(c); +} diff --git a/mainwindow.h b/mainwindow.h index 7e3821f..476c4ee 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -20,11 +20,12 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow() override; - void keyPressEvent(QKeyEvent *event) override; + void updateModel() const; + + SwayTreeModel* model; + Ui::MainWindow *ui; private: - Ui::MainWindow *ui; - SwayTreeModel* model; // QTimer* modelUpdateTimer{}; KeyHandler* swayTreeKeyHandler; KeyHandler* closeHelpKeyHandler; diff --git a/tree/AbstractTreeNode.h b/tree/AbstractTreeNode.h index dc70af3..3616cc0 100644 --- a/tree/AbstractTreeNode.h +++ b/tree/AbstractTreeNode.h @@ -72,13 +72,13 @@ public: return matching; } - [[nodiscard]] const T *findChildRecursive(std::function test) const { - if (test((const T *) this)) - return (const T*) this; + [[nodiscard]] T *findChildRecursive(std::function test) { + if (test((T *) this)) + return (T*) this; for (int i = 0; i < childCount(); ++i) { auto *c = child(i); - const T *r = c->findChildRecursive(test); + T *r = c->findChildRecursive(test); if (r != nullptr) return r; } diff --git a/tree/SwayTreeModel.h b/tree/SwayTreeModel.h index 4abe021..83d66f4 100644 --- a/tree/SwayTreeModel.h +++ b/tree/SwayTreeModel.h @@ -27,6 +27,10 @@ public: Q_DISABLE_COPY_MOVE(SwayTreeModel) explicit SwayTreeModel(QObject *parent = nullptr) : AbstractTreeModel(parent) { + rootItem = nullptr; + } + + void generateRoot() { auto res = sway.sendIPC(SWAY_GET_TREE); auto rep = json::parse(res.msg); rootItem = new SwayTreeNode(rep); @@ -34,11 +38,11 @@ public: ~SwayTreeModel() override = default; - explicit SwayTreeModel(SwayTreeNode * rootItem, QObject *parent = nullptr) : rootItem(rootItem), - AbstractTreeModel( - parent) {}; + explicit SwayTreeModel(SwayTreeNode *rootItem, QObject *parent = nullptr) : rootItem(rootItem), + AbstractTreeModel( + parent) {}; - [[nodiscard]] const SwayTreeNode *getRoot() const override { + [[nodiscard]] SwayTreeNode *getRoot() const override { return rootItem; }; @@ -47,14 +51,15 @@ public: return findIndexByNode(getRoot()->findFocused()); } - [[nodiscard]] std::set getSelectedNodes(const QTreeView* treeView) const; + [[nodiscard]] std::set getSelectedNodes(const QTreeView *treeView) const; - [[nodiscard]] SwayTreeNode * getSelectedNode(const QModelIndex &i) const; + [[nodiscard]] SwayTreeNode *getSelectedNode(const QModelIndex &i) const; [[nodiscard]] QModelIndex findIndexByNode(const SwayTreeNode *node) const; + private: - SwayTreeNode * rootItem; + SwayTreeNode *rootItem; }; #endif //SWAYMUX_SWAYTREEMODEL_H diff --git a/tree/swaytree.h b/tree/swaytree.h index 0c37022..b5e0851 100644 --- a/tree/swaytree.h +++ b/tree/swaytree.h @@ -106,7 +106,7 @@ struct SwayRecord { const NodeOrientation::NodeOrientation orientation; const bool urgent; const bool sticky; - const bool focused; + bool focused; [[nodiscard]] bool operator<(const SwayRecord &other) const { return this->id < other.id; @@ -130,16 +130,20 @@ private: class SwayTreeNode : public AbstractTreeNode { public: - const SwayRecord node; + SwayRecord node; explicit SwayTreeNode(const json &rep, SwayTreeNode *parent = nullptr) : node(SwayRecord(rep)), AbstractTreeNode(parent) { for (const auto &child: rep["nodes"]) { + if (child.contains("app_id") && !child["app_id"].is_null() && ((std::string) child["app_id"]) == "swaymux") + continue; auto childNode = std::make_unique(child, this); this->appendChild(childNode); } for (const auto &child: rep["floating_nodes"]) { + if (child.contains("app_id") && !child["app_id"].is_null() && ((std::string) child["app_id"]) == "swaymux") + continue; auto childNode = std::make_unique(child, this); this->appendChild(childNode); } @@ -160,7 +164,7 @@ public: [[nodiscard]] int columnCount() const override { return 1; }; - [[nodiscard]] const SwayTreeNode *findFocused() const { + [[nodiscard]] SwayTreeNode *findFocused() { std::function focused = [](const SwayTreeNode *testNode) { return testNode->node.focused; }; @@ -191,6 +195,12 @@ public: return result; } + [[nodiscard]] SwayTreeNode * findById(const int id) { + return const_cast(findChildRecursive([id](const SwayTreeNode *test) { + return test->node.id == id; + })); + } + private: [[nodiscard]] static std::function matchNodeType(NodeType::NodeType type) { return matchNodeType(std::vector{type}); @@ -204,6 +214,10 @@ private: }); }; } + + [[nodiscard]] bool operator==(const SwayTreeNode * other) const { + return this->node == other->node; + } };