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 `<F1>` or `<F2>` respectively, the bottom window has some errors. Maybe some timing issue.
This commit is contained in:
parent
0a086ff604
commit
3be5336ca0
23
DemoApp.cpp
23
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "Debug.hpp"
|
||||
#include <kNCurses/HorizontalTilingWindowManager.hpp>
|
||||
#include <kNCurses/Window.hpp>
|
||||
#include <ncursesw/ncurses.h>
|
||||
|
@ -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<Window *>::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);
|
||||
}
|
||||
}
|
|
@ -4,12 +4,15 @@
|
|||
|
||||
#include <kNCurses/TilingWindowManager.hpp>
|
||||
#include <kNCurses/Window.hpp>
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <algorithm>
|
||||
|
||||
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<Window *>::iterator currentVisibleWindow = visibleStack.begin();
|
||||
for(Window *currentWindow : stack)
|
||||
list<WindowStackElement>::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<WindowStackElement>::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;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "Debug.hpp"
|
||||
#include <kNCurses/VerticalTilingWindowManager.hpp>
|
||||
#include <kNCurses/Window.hpp>
|
||||
#include <ncursesw/ncurses.h>
|
||||
|
@ -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<Window *>::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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <list>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
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<Window *, windowDimension> 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<Window *> stack, visibleStack;
|
||||
std::list<WindowStackElement> 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue