Skip to content

Commit

Permalink
Add vertical and horizontal splits (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
falbru authored Jun 21, 2024
1 parent 2274091 commit 74bbc4d
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 54 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ qt_add_executable(kak-qt
src/keybindings.cpp
src/drawoptions.cpp
src/ipc.cpp
src/container.cpp
src/splitcontainer.cpp
src/kakounesession.cpp
src/kakouneclient.cpp
src/kakounemenu.cpp
Expand Down
13 changes: 10 additions & 3 deletions rc/kakoune-qt.kak
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ hook global SessionRenamed .*:.* %{
nop %sh{ KAKQT_SESSION_ID=$kak_client_env_KAKQT_SESSION_ID kak-qt cli rename-session $kak_session }
}

define-command -override -docstring "new [<commands>]: create a new Kakoune client" new -params .. %{ nop %sh{
KAKQT_SESSION_ID=$kak_client_env_KAKQT_SESSION_ID kak-qt cli new-client $@
define-command kakqt-split-horizontal -params .. -docstring "kakqt-split-horizontal [<commands>]: create a new Kakoune client" %{ nop %sh{
KAKQT_WINDOW_ID=$kak_client_env_KAKQT_WINDOW_ID KAKQT_SESSION_ID=$kak_client_env_KAKQT_SESSION_ID kak-qt cli split-horizontal $@
}}
complete-command -menu new command
complete-command -menu kakqt-split-horizontal command

define-command kakqt-split-vertical -params .. -docstring "kakqt-split-vertical [<commands>]: create a new Kakoune client" %{ nop %sh{
KAKQT_WINDOW_ID=$kak_client_env_KAKQT_WINDOW_ID KAKQT_SESSION_ID=$kak_client_env_KAKQT_SESSION_ID kak-qt cli split-vertical $@
}}
complete-command -menu kakqt-split-vertical command

alias global new kakqt-split-horizontal

define-command kakqt-focus -params ..1 -docstring '
kakqt-focus [<client>]: focus the given client
Expand Down
71 changes: 71 additions & 0 deletions src/container.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "container.hpp"
#include "kakounewidget.hpp"
#include <qnamespace.h>
#include <qwidget.h>

Container::Container(Qt::Orientation orientation, QWidget *parent) : QWidget(parent)
{
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);

m_splitter = new QSplitter(this);
m_splitter->setOrientation(orientation);

layout->addWidget(m_splitter);
}

void Container::addWidget(QWidget *widget)
{
connectWidget(widget);
m_splitter->addWidget(widget);
}

Qt::Orientation Container::getOrientation() const
{
return m_splitter->orientation();
}

void Container::connectWidget(QWidget *widget)
{
if (KakouneWidget *kak_widget = qobject_cast<KakouneWidget *>(widget))
connectKakouneWidget(kak_widget);

connect(widget, &QWidget::destroyed, this, [=]() {
if (m_splitter->count() == 0)
{
deleteLater();
}
else
{
m_splitter->widget(0)->setFocus();
}
});
}

void Container::connectKakouneWidget(KakouneWidget *kak_widget)
{
connect(kak_widget, &KakouneWidget::finished, this, [=]() {
kak_widget->setParent(nullptr);
kak_widget->deleteLater();
});
}

void Container::focusInEvent(QFocusEvent *event)
{
m_splitter->widget(0)->setFocus();
}

Container *Container::findParentContainer(QWidget *widget)
{
QWidget *parent_container = widget->parentWidget();
while (parent_container)
{
Container *container = qobject_cast<Container *>(parent_container);
if (container)
{
return container;
}
parent_container = parent_container->parentWidget();
}
return nullptr;
}
32 changes: 32 additions & 0 deletions src/container.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef CONTAINER_HPP
#define CONTAINER_HPP

#include "kakounewidget.hpp"
#include <qnamespace.h>
#include <qtmetamacros.h>
#include <qvariant.h>
#include <qwidget.h>

class Container : public QWidget
{
Q_OBJECT

public:
Container(Qt::Orientation orientation = Qt::Horizontal, QWidget *parent = nullptr);

void addWidget(QWidget *widget);

Qt::Orientation getOrientation() const;

static Container *findParentContainer(QWidget *widget);

protected:
void connectWidget(QWidget *widget);
void connectKakouneWidget(KakouneWidget *kak_widget);

void focusInEvent(QFocusEvent *event) override;

QSplitter *m_splitter;
};

#endif
9 changes: 5 additions & 4 deletions src/ipc.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "ipc.hpp"
#include <qjsondocument.h>
#include <qnamespace.h>

namespace KakouneIPC
{
Expand Down Expand Up @@ -57,7 +58,7 @@ IPCServer::~IPCServer()

void IPCServer::bind(MainWindow *main_window)
{
connect(this, &IPCServer::newClient, main_window, &MainWindow::newClient);
connect(this, &IPCServer::newSplit, main_window, &MainWindow::newSplit);
connect(this, &IPCServer::focusWindow, main_window, &MainWindow::focusWindow);
connect(this, &IPCServer::renameSession, main_window, &MainWindow::renameSession);
}
Expand All @@ -82,10 +83,10 @@ void IPCServer::handleConnection()
void IPCServer::handleCommand(QJsonObject request)
{
const QString method = request["method"].toString();
qDebug() << method;
if (method == "newClient")
if (method == "newSplit")
{
emit newClient(request["args"].isString() ? request["args"].toString() : "");
emit newSplit(request["client_name"].toString(), request["args"].isString() ? request["args"].toString() : "",
request["orientation"].toString() == "vertical" ? Qt::Vertical : Qt::Horizontal);
}
else if (method == "focusWindow")
{
Expand Down
2 changes: 1 addition & 1 deletion src/ipc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class IPCServer : public QObject

void bind(MainWindow *main_window);
signals:
void newClient(const QString &arguments);
void newSplit(const QString &client_name, const QString &arguments, const Qt::Orientation &orientation);
void focusWindow(const QString &client_name);
void renameSession(const QString &session_name);

Expand Down
10 changes: 8 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
#include <qcoreapplication.h>
#include <qhashfunctions.h>
#include <qjsonobject.h>
#include <qnamespace.h>
#include <qprocess.h>

int main(int argc, char *argv[])
{
Expand Down Expand Up @@ -36,9 +39,12 @@ int main(int argc, char *argv[])

const QString subcommand = positional_arguments[0];

if (subcommand == "new-client")
if (subcommand == "split-horizontal" || subcommand == "split-vertical")
{
ipc.send("newClient", {{"args", positional_arguments.mid(1).join(" ")}});
auto orientation = subcommand == "split-vertical" ? "vertical" : "horizontal";
ipc.send("newSplit", {{"args", positional_arguments.mid(1).join(" ")},
{"client_name", QProcessEnvironment::systemEnvironment().value("KAKQT_WINDOW_ID")},
{"orientation", orientation}});
}
else if (subcommand == "focus")
{
Expand Down
113 changes: 73 additions & 40 deletions src/mainwindow.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#include "mainwindow.hpp"
#include "container.hpp"
#include "kakounewidget.hpp"
#include "keybindings.hpp"
#include <climits>
#include <qnamespace.h>
#include <quuid.h>
#include <qwidget.h>

MainWindow::MainWindow(QString session_id, QWidget *parent) : QMainWindow(parent)
{
Expand All @@ -12,11 +17,13 @@ MainWindow::MainWindow(QString session_id, QWidget *parent) : QMainWindow(parent

m_session = new KakouneSession(session_id);

m_root = new QSplitter(this);
this->newClient("");
m_root = new SplitContainer(Qt::Horizontal, this);
setCentralWidget(m_root);

KakouneWidget *kak_widget = createKakouneWidget();
m_root->addWidget(kak_widget);

updateWindowTitle();
setCentralWidget(m_root);
}

MainWindow::~MainWindow()
Expand All @@ -33,58 +40,51 @@ QUuid MainWindow::getID()
return m_id;
}

void MainWindow::newClient(const QString &arguments)
void MainWindow::newSplit(const QString &client_name, const QString &arguments, const Qt::Orientation &orientation)
{
KakouneWidget *kakwidget = new KakouneWidget(m_session->getSessionId(), m_id, m_draw_options, arguments, m_root);
kakwidget->installEventFilter(new KeyBindingsFilter(this));
connect(kakwidget, &KakouneWidget::finished, m_root, [=]() {
kakwidget->setParent(nullptr);
m_windows.removeOne(kakwidget);

if (m_root->count() == 0)
{
close();
}
});

m_windows.append(kakwidget);
m_root->addWidget(kakwidget);
}
QWidget *source_widget = findKakouneWidget(client_name);
if (source_widget == nullptr)
{
qWarning() << "MainWindow::newSplit: Could not find KakouneWidget with client name: " << client_name;
return;
}

void MainWindow::focusWindow(const QString &uuid)
{
for (KakouneWidget *window : m_windows)
if (SplitContainer *parent_split = (SplitContainer *)Container::findParentContainer(source_widget))
{
if (window->getID().toString() == uuid)
{
window->setFocus();
return;
}
KakouneWidget *new_kak_widget = createKakouneWidget(arguments);
parent_split->split(source_widget, new_kak_widget, orientation);
new_kak_widget->setFocus();
}
else
{
qWarning("MainWindow::newSplit: Couldn't find parent container");
}
}

void MainWindow::focusLeft()
void MainWindow::focusWindow(const QString &client_name)
{
QWidget *focused_widget = qApp->focusWidget();
int index = m_root->indexOf((QWidget *)focused_widget->parent()); // TODO
if (index <= 0)
KakouneWidget *kak_widget = findKakouneWidget(client_name);

if (kak_widget == nullptr)
{
qWarning() << "MainWindow::focusWindow: Could not find KakouneWidget with client name: " << client_name;
return;
}

m_windows[index - 1]->setFocus();
kak_widget->setFocus();
}

void MainWindow::focusRight()
void MainWindow::focusLeft()
{
QWidget *focused_widget = qApp->focusWidget();
int index = m_root->indexOf((QWidget *)focused_widget->parent()); // TODO
if (index >= m_windows.size() - 1)
{
return;
}
// TODO
}

m_windows[index + 1]->setFocus();
void MainWindow::focusRight()
{
// TODO
// Container: have a lastFocusedWidget() method
// Iterate up to closest container that is Horizontal. Focus lastFocusedWidget
// When a Container gains focus, focus the last focused widget
}

void MainWindow::updateWindowTitle()
Expand All @@ -97,3 +97,36 @@ void MainWindow::renameSession(const QString &session_name)
m_session->setSessionId(session_name);
updateWindowTitle();
}

KakouneWidget *MainWindow::findKakouneWidget(const QString &client_name)
{
for (KakouneWidget *window : m_windows)
{
if (window->getID().toString() == client_name)
{
return window;
}
}
return nullptr;
}

KakouneWidget *MainWindow::createKakouneWidget(const QString &arguments)
{
KakouneWidget *kakwidget = new KakouneWidget(m_session->getSessionId(), m_id, m_draw_options, arguments, m_root);

kakwidget->installEventFilter(new KeyBindingsFilter(this));

connect(kakwidget, &KakouneWidget::finished, m_root, [=]() {
m_windows.removeOne(kakwidget);

if (m_windows.size() == 0)
{
close();
return;
}
});

m_windows.append(kakwidget);

return kakwidget;
}
11 changes: 7 additions & 4 deletions src/mainwindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "drawoptions.hpp"
#include "kakounesession.hpp"
#include "kakounewidget.hpp"
#include "splitcontainer.hpp"
#include <QMainWindow>
#include <QtWidgets>
#include <quuid.h>
Expand All @@ -22,8 +23,8 @@ class MainWindow : public QMainWindow
QUuid getID();

public slots:
void newClient(const QString &arguments);
void focusWindow(const QString &uuid);
void newSplit(const QString &client_name, const QString &arguments, const Qt::Orientation &orientation);
void focusWindow(const QString &client_name);
void renameSession(const QString &session_name);

protected:
Expand All @@ -32,11 +33,13 @@ class MainWindow : public QMainWindow

private:
QUuid m_id;
QSplitter *m_root;
KakouneSession *m_session;

QList<KakouneWidget *> m_windows;
KakouneWidget *createKakouneWidget(const QString &arguments = "");
KakouneWidget *findKakouneWidget(const QString &client_name);

SplitContainer *m_root;
QList<KakouneWidget *> m_windows;
DrawOptions *m_draw_options;
};

Expand Down
Loading

0 comments on commit 74bbc4d

Please sign in to comment.