From 3be5336ca0ad2454a83bd1bacdabc17187ca3334 Mon Sep 17 00:00:00 2001 From: Christian Burger Date: Wed, 18 May 2022 21:15:13 +0200 Subject: [PATCH] refactoring tiling window manager Laying out the windows in the window manager frame, is a very similar process regardless of doing it horizontally or vertically. So the template design pattern was applied and the general functionality was pulled up; only specific functionalities were put into the corresponding classes. kNCursesDemoApp: Separated the screen into three windows vertically. The top and bottom window contain another two windows each, lay out horizontally. The top left window is fixed to width 1 and the top right takes the rest of the space. Bottom two windows take 50 % of the space each. Remarks: There are still some rendering bugs, when hiding and showing windows. E. g. when hiding the top or middle window with `` or `` respectively, the bottom window has some errors. Maybe some timing issue. --- DemoApp.cpp | 23 ++++- DemoApp.hpp | 6 +- HorizontalTilingWindowManager.cpp | 47 ++++----- TilingWindowManager.cpp | 97 +++++++++++++++---- VerticalTilingWindowManager.cpp | 54 ++++------- .../HorizontalTilingWindowManager.hpp | 8 +- include/kNCurses/TilingWindowManager.hpp | 35 +++++-- .../kNCurses/VerticalTilingWindowManager.hpp | 8 +- 8 files changed, 182 insertions(+), 96 deletions(-) diff --git a/DemoApp.cpp b/DemoApp.cpp index 5752dad..1fe0f30 100644 --- a/DemoApp.cpp +++ b/DemoApp.cpp @@ -37,19 +37,32 @@ namespace krikkel::NCurses = new VerticalTilingWindowManager(Root_Window, &ncursesMutex); topWindowManager = new HorizontalTilingWindowManager(new Window(), &ncursesMutex); + bottomWindowManager + = new HorizontalTilingWindowManager(new Window(), &ncursesMutex); windowManager->addWindow(topWindowManager); ptyWindow = new PtyWindow(&ncursesMutex, 0, 0, 0, 0); windowManager->addWindow(ptyWindow); - dummyWindowBottom = new Window(windowManager); + windowManager->addWindow(bottomWindowManager); windowManager->updateLayout(); windowManager->refresh(); - dummyWindowTopLeft = new Window(topWindowManager); + dummyWindowTopLeft = new Window(); + topWindowManager->addWindow(dummyWindowTopLeft, 1); + dummyWindowTopLeft->addstr("t\no\np\n \nl\ne\nf\nt\n w\ni\nn\nd\no\nw"); dummyWindowTopRight = new Window(topWindowManager); + dummyWindowTopRight->addstr("top right window"); topWindowManager->updateLayout(); topWindowManager->refresh(); + + dummyWindowBottomLeft = new Window(bottomWindowManager); + dummyWindowBottomLeft->addstr("bottom left window"); + dummyWindowBottomRight = new Window(bottomWindowManager); + dummyWindowBottomRight->addstr("bottom right window"); + + bottomWindowManager->updateLayout(); + bottomWindowManager->refresh(); } void DemoApp::forkShell() @@ -110,10 +123,10 @@ namespace krikkel::NCurses windowManager->refresh(); break; case KEY_F(3): - if(dummyWindowBottom->isHidden()) - windowManager->showWindow(dummyWindowBottom); + if(bottomWindowManager->isHidden()) + windowManager->showWindow(bottomWindowManager); else - windowManager->hideWindow(dummyWindowBottom); + windowManager->hideWindow(bottomWindowManager); windowManager->updateLayout(); windowManager->refresh(); break; diff --git a/DemoApp.hpp b/DemoApp.hpp index 1d2470a..fa3951d 100644 --- a/DemoApp.hpp +++ b/DemoApp.hpp @@ -23,9 +23,9 @@ namespace krikkel::NCurses private: VerticalTilingWindowManager *windowManager; - HorizontalTilingWindowManager *topWindowManager; - Window *dummyWindowBottom, *dummyWindowTopLeft, - *dummyWindowTopRight; + HorizontalTilingWindowManager *topWindowManager, *bottomWindowManager; + Window *dummyWindowTopLeft, *dummyWindowTopRight + , *dummyWindowBottomLeft, *dummyWindowBottomRight; PtyWindow *ptyWindow; std::recursive_mutex ncursesMutex; diff --git a/HorizontalTilingWindowManager.cpp b/HorizontalTilingWindowManager.cpp index d429f61..c244a7c 100644 --- a/HorizontalTilingWindowManager.cpp +++ b/HorizontalTilingWindowManager.cpp @@ -2,6 +2,7 @@ * @author Christian Burger (christian@krikkel.de) */ +#include "Debug.hpp" #include #include #include @@ -9,45 +10,31 @@ namespace krikkel::NCurses { - using std::list; using std::recursive_mutex; - using std::scoped_lock; HorizontalTilingWindowManager::HorizontalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex) : TilingWindowManager(rootWindow, ncursesMutex) {} - void HorizontalTilingWindowManager::updateLayout() + void HorizontalTilingWindowManager::resizeAndMoveWindow(Window *window, windowDimension dimension, windowPosition position) { - scoped_lock lock(*ncursesMutex); + __debug_log("(" + std::to_string(position + begx()) + ", " + std::to_string(0 + begy()) + ", " + std::to_string(dimension) + ", " + std::to_string(height()) + ")"); + window->resize(height(), dimension); + window->mvwin(0 + begy(), position + begx()); + } - size_t stackSize = visibleStack.size(); - if(stackSize == 0) - { - clear(); - return; - } + TilingWindowManager::windowDimension HorizontalTilingWindowManager::getAvailableSpace() + { + return width(); + } - int availableWidth = width(); - int availableHeight = height(); - int windowWidth = availableWidth / stackSize - 1; + void HorizontalTilingWindowManager::windowBorder() + { + vline(height()); + } - if((windowWidth + 1) * stackSize < availableWidth) - ++windowWidth; - - uint16_t x = 0; - list::iterator it = visibleStack.begin(); - for(size_t index = 0 - ; index < visibleStack.size() - 1 - ; ++index) - { - resizeWindow(*it++, availableHeight, windowWidth, 0, x); - x += windowWidth; - { - move(0, x++); - vline(availableHeight); - } - } - resizeWindow(*it, availableHeight, availableWidth - x, 0, x); + void HorizontalTilingWindowManager::moveCursor(TilingWindowManager::windowPosition position) + { + move(0, position); } } \ No newline at end of file diff --git a/TilingWindowManager.cpp b/TilingWindowManager.cpp index e4a7e56..bfc4d2c 100644 --- a/TilingWindowManager.cpp +++ b/TilingWindowManager.cpp @@ -4,12 +4,15 @@ #include #include +#include "Debug.hpp" + #include #include namespace krikkel::NCurses { using std::list; + using std::pair; using std::recursive_mutex; using std::scoped_lock; @@ -17,15 +20,23 @@ namespace krikkel::NCurses : Window(*rootWindow), ncursesMutex(ncursesMutex) {} - void TilingWindowManager::addWindow(Window *window) + void TilingWindowManager::addWindow(Window *window, windowDimension size) { - stack.push_back(window); - visibleStack.push_back(window); + WindowStackElement stackElement(window, size); + stack.push_back(stackElement); + visibleStack.push_back(stackElement); } void TilingWindowManager::hideWindow(Window *window) { - visibleStack.remove(window); + if(window->hidden) + return; + + visibleStack.remove_if( + [window](WindowStackElement element) + { + return element.first == window; + }); window->hidden = true; } @@ -34,18 +45,18 @@ namespace krikkel::NCurses if(!window->hidden) return; - list::iterator currentVisibleWindow = visibleStack.begin(); - for(Window *currentWindow : stack) + list::iterator currentVisibleWindowElement = visibleStack.begin(); + for(WindowStackElement currentWindowElement : stack) { - if(currentWindow == window) + if(currentWindowElement.first == window) { - visibleStack.insert(currentVisibleWindow, window); + visibleStack.insert(currentVisibleWindowElement, currentWindowElement); window->hidden = false; break; } - if(currentWindow != (*currentVisibleWindow)) + if(currentWindowElement != (*currentVisibleWindowElement)) continue; - ++currentVisibleWindow; + ++currentVisibleWindowElement; } } @@ -62,21 +73,73 @@ namespace krikkel::NCurses int result = Window::refresh(); - for(Window *window : visibleStack) + for(WindowStackElement stackElement : visibleStack) // @todo there are return values; compound them? - window->refresh(); + stackElement.first->refresh(); return result; } - SingleUserInput TilingWindowManager::readSingleUserInput() + void TilingWindowManager::updateLayout() { - return Window::readSingleUserInput(); + scoped_lock lock(*ncursesMutex); + + size_t stackSize = visibleStack.size(); + if(stackSize == 0) + { + clear(); + return; + } + + int unitSize = getRelativeUnitSizeForVisibleWindows(getAvailableSpace()) - 1; + windowPosition position = 0; + auto lastButNotLeast = prev(visibleStack.end()); + for(auto stackElementIterator = visibleStack.begin() + ; stackElementIterator != lastButNotLeast + ; ++stackElementIterator) + { + WindowStackElement stackElement = *stackElementIterator; + Window *window = stackElement.first; + if(!window->isHidden()) + { + int windowShift; + int windowSize = stackElement.second; + if(windowSize < 0) + windowShift = unitSize * (-windowSize); + else + windowShift = windowSize; + + resizeAndMoveWindow(window, windowShift, position); + position += windowShift; + __debug_log("drawing line at " + std::to_string(position)); + moveCursor(position++); + windowBorder(); + } + } + resizeAndMoveWindow((*lastButNotLeast).first, getAvailableSpace() - position, position); } - void TilingWindowManager::resizeWindow(Window *window, uint16_t height, uint16_t width, uint16_t y, uint16_t x) + + TilingWindowManager::windowDimension TilingWindowManager::getRelativeUnitSizeForVisibleWindows(windowDimension spaceAvailable) { - window->resize(height, width); - window->mvwin(y, x); + uint16_t numberOfRelativeUnits = 0; + windowDimension absoluteSum = 0; + list::iterator windowIterator = stack.begin(); + + for(WindowStackElement stackElement : visibleStack) + { + windowDimension size = stackElement.second; + if(size < 0) + numberOfRelativeUnits -= size; + else + absoluteSum += size; + } + + windowDimension relativeUnitSize = (spaceAvailable - absoluteSum) / numberOfRelativeUnits; + windowDimension remainder = (spaceAvailable - absoluteSum - relativeUnitSize * numberOfRelativeUnits); + if(remainder > 0 && remainder * 2 < numberOfRelativeUnits) + ++relativeUnitSize; + + return relativeUnitSize; } } \ No newline at end of file diff --git a/VerticalTilingWindowManager.cpp b/VerticalTilingWindowManager.cpp index d186140..dbe95c0 100644 --- a/VerticalTilingWindowManager.cpp +++ b/VerticalTilingWindowManager.cpp @@ -2,6 +2,7 @@ * @author Christian Burger (christian@krikkel.de) */ +#include "Debug.hpp" #include #include #include @@ -9,45 +10,32 @@ namespace krikkel::NCurses { - using std::list; using std::recursive_mutex; - using std::scoped_lock; VerticalTilingWindowManager::VerticalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex) : TilingWindowManager(rootWindow, ncursesMutex) {} - void VerticalTilingWindowManager::updateLayout() + void VerticalTilingWindowManager::resizeAndMoveWindow(Window *window, windowDimension dimension, windowPosition position) { - scoped_lock lock(*ncursesMutex); - - size_t stackSize = visibleStack.size(); - if(stackSize == 0) - { - clear(); - return; - } - - int availableWidth = width(); - int availableHeight = height(); - int windowHeight = availableHeight / stackSize - 1; - - if((windowHeight + 1) * stackSize < availableHeight) - ++windowHeight; - - uint16_t y = 0; - list::iterator it = visibleStack.begin(); - for(size_t index = 0 - ; index < visibleStack.size() - 1 - ; ++index) - { - resizeWindow(*it++, windowHeight, availableWidth, y, 0); - y += windowHeight; - { - move(y++, 0); - hline(availableWidth); - } - } - resizeWindow(*it, availableHeight - y, availableWidth, y, 0); + __debug_log("(" + std::to_string(0 + begx()) + ", " + std::to_string(position + begy()) + ", " + std::to_string(width()) + ", " + std::to_string(dimension) + ")"); + window->resize(dimension, width()); + window->mvwin(position + begy(), 0 + begx()); } + + TilingWindowManager::windowDimension VerticalTilingWindowManager::getAvailableSpace() + { + return height(); + } + + void VerticalTilingWindowManager::windowBorder() + { + hline(width()); + } + + void VerticalTilingWindowManager::moveCursor(TilingWindowManager::windowPosition position) + { + move(position, 0); + } + } \ No newline at end of file diff --git a/include/kNCurses/HorizontalTilingWindowManager.hpp b/include/kNCurses/HorizontalTilingWindowManager.hpp index 739b0bb..f5771ea 100644 --- a/include/kNCurses/HorizontalTilingWindowManager.hpp +++ b/include/kNCurses/HorizontalTilingWindowManager.hpp @@ -17,7 +17,13 @@ namespace krikkel::NCurses { public: HorizontalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex); - void updateLayout() override; + + void resizeAndMoveWindow(Window *window + , windowDimension dimension + , windowPosition position) override; + windowDimension getAvailableSpace() override; + void windowBorder() override; + void moveCursor(windowPosition position) override; }; } diff --git a/include/kNCurses/TilingWindowManager.hpp b/include/kNCurses/TilingWindowManager.hpp index 94be308..d79e4c4 100644 --- a/include/kNCurses/TilingWindowManager.hpp +++ b/include/kNCurses/TilingWindowManager.hpp @@ -1,5 +1,17 @@ /** - * @brief Window manager + * @brief Window manager for tiling (template pattern, abstract parent) + * + * A window manager contains windows of a given size, lays them out and can hide + * or show them. There is no concrete window manager for tiling horizontally and + * vertically at the same time. Instead nesting the window managers is adviced, + * since the window manager classes are derived from windows in the end. + * + * This is an abstract class used by the concrete classes + * `VerticalTilingWindowManager` and `HorizontalTilingWindowManager`. Both + * specify the virtual and protected, declared but undefined methods used by the + * public interface to facilitate the function depending on the orientation + * (horizontally or vertically). + * * @author Christian Burger (christian@krikkel.de) */ @@ -9,6 +21,7 @@ #include "Window.hpp" #include #include +#include namespace krikkel::NCurses { @@ -17,21 +30,31 @@ namespace krikkel::NCurses class TilingWindowManager : public Window { public: + /// @todo figure out more appropiate names … + typedef int16_t windowDimension; + typedef uint16_t windowPosition; + typedef std::pair WindowStackElement; + TilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex); - void addWindow(Window *window); + void addWindow(Window *window, windowDimension size = -1); int resize(int rows, int cols) override; int refresh() override; - virtual void updateLayout() = 0; + void updateLayout(); void hideWindow(Window *window); void showWindow(Window *window); - SingleUserInput readSingleUserInput(); protected: std::recursive_mutex *ncursesMutex; - std::list stack, visibleStack; + std::list stack, visibleStack; - void resizeWindow(Window *window, uint16_t height, uint16_t width, uint16_t y, uint16_t x); + windowDimension getRelativeUnitSizeForVisibleWindows(windowDimension spaceAvailable); + virtual void resizeAndMoveWindow(Window *window + , windowDimension dimension + , windowPosition position) = 0; + virtual void windowBorder() = 0; + virtual windowDimension getAvailableSpace() = 0; + virtual void moveCursor(windowPosition position) = 0; }; } diff --git a/include/kNCurses/VerticalTilingWindowManager.hpp b/include/kNCurses/VerticalTilingWindowManager.hpp index cb47abb..c7c1143 100644 --- a/include/kNCurses/VerticalTilingWindowManager.hpp +++ b/include/kNCurses/VerticalTilingWindowManager.hpp @@ -17,7 +17,13 @@ namespace krikkel::NCurses { public: VerticalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex); - void updateLayout() override; + + void resizeAndMoveWindow(Window *window + , windowDimension dimension + , windowPosition position) override; + windowDimension getAvailableSpace() override; + void windowBorder() override; + void moveCursor(windowPosition position) override; }; }