GLK: FROTZ: Add support for pair windows to have more than 2 children

This is primarily for the V6 games, which have up to 8 windows
on-screen at the same time in arbitray positions
ext
This commit is contained in:
Paul Gilbert 2019-01-02 18:18:11 -08:00
parent 3ed48e3de2
commit f1d9722f3b
8 changed files with 138 additions and 79 deletions

View File

@ -22,6 +22,7 @@
#include "glk/frotz/windows.h"
#include "glk/frotz/frotz.h"
#include "glk/window_pair.h"
namespace Glk {
namespace Frotz {
@ -51,7 +52,8 @@ winid_t Window::getWindow() {
// TODO: For now I'm assuming all the extra created windows will be graphics, since Glk requires
// us to specify it at creation time. Not sure if it's true or not for all V6 games
winid_t parent = _windows->_lower;
_win = g_vm->glk_window_open(parent, winmethod_OnTop | winmethod_Fixed, 0, wintype_Graphics, 0);
_win = g_vm->glk_window_open(g_vm->glk_window_get_root(), winmethod_Arbitrary | winmethod_Fixed,
0, wintype_Graphics, 0);
}
return _win;
@ -59,6 +61,7 @@ winid_t Window::getWindow() {
void Window::setSize(const Point &newSize) {
winid_t win = getWindow();
checkRepositionLower();
win->setSize(newSize);
/* TODO
@ -73,6 +76,7 @@ void Window::setSize(const Point &newSize) {
void Window::setPosition(const Point &newPos) {
winid_t win = getWindow();
checkRepositionLower();
win->setPosition(newPos);
}
@ -113,5 +117,17 @@ void Window::setProperty(WindowProperty propType, uint16 value) {
// TODO
}
void Window::checkRepositionLower() {
if (&_windows->_lower == this) {
PairWindow *parent = dynamic_cast<PairWindow *>(_win->_parent);
if (!parent)
error("Parent was not a pair window");
// Ensure the parent pair window is flagged as having children at arbitrary positions,
// just in case it isn't already
parent->_dir = winmethod_Arbitrary;
}
}
} // End of namespace Frotz
} // End of namespace Glk

View File

@ -64,6 +64,11 @@ private:
* Set a property value
*/
void setProperty(WindowProperty propType, uint16 value);
/**
* Called when trying to reposition or resize windows. Does special handling for the lower window
*/
void checkRepositionLower();
public:
/**
* Constructor

View File

@ -258,10 +258,11 @@ winid_t GlkAPI::glk_window_get_sibling(winid_t win) {
if (!parentWin)
return nullptr;
if (parentWin->_child1 == win)
return parentWin->_child2;
else if (parentWin->_child2 == win)
return parentWin->_child1;
int index = parentWin->_children.indexOf(win);
if (index == ((int)parentWin->_children.size() - 1))
return parentWin->_children.front();
else if (index >= 0)
return parentWin->_children[index + 1];
return nullptr;
}

View File

@ -150,12 +150,12 @@ enum WinType {
};
enum WinMethod {
winmethod_Left = 0x00,
winmethod_Right = 0x01,
winmethod_Above = 0x02,
winmethod_Below = 0x03,
winmethod_OnTop = 0x04, ///< Newly introduced for ScummGlk
winmethod_DirMask = 0x0f,
winmethod_Left = 0x00,
winmethod_Right = 0x01,
winmethod_Above = 0x02,
winmethod_Below = 0x03,
winmethod_Arbitrary = 0x04, ///< Newly introduced for ScummGlk
winmethod_DirMask = 0x0f,
winmethod_Fixed = 0x10,
winmethod_Proportional = 0x20,

View File

@ -23,13 +23,20 @@
#ifndef GLK_UTILS_H
#define GLK_UTILS_H
#include "common/array.h"
#include "common/rect.h"
#include "glk/glk_types.h"
namespace Glk {
/**
* Two dimensional point
*/
typedef Common::Point Point;
/**
* Contains a square box/rect area
*/
struct Rect : public Common::Rect {
public:
static Rect fromXYWH(int x, int y, int w, int h) {
@ -41,6 +48,24 @@ public:
Rect(int16 x1, int16 y1, int16 x2, int16 y2) : Common::Rect(x1, y1, x2, y2) {}
};
/**
* Derived array class
*/
template<class T>class Array : public Common::Array<T> {
public:
/**
* Return the index in the array of a passed item
*/
int indexOf(T val) {
for (size_t idx = 0; idx < this->size(); ++idx) {
if ((*this).operator[](idx) == val)
return idx;
}
return -1;
}
};
/**
* Converts a decimal or hexadecimal string into a number
*/

View File

@ -34,13 +34,13 @@ PairWindow::PairWindow(Windows *windows, uint method, Window *key, uint size) :
_wBorder((method & winmethod_BorderMask) == winmethod_Border),
_vertical(_dir == winmethod_Left || _dir == winmethod_Right),
_backward(_dir == winmethod_Left || _dir == winmethod_Above),
_key(key), _size(size), _keyDamage(0), _child1(nullptr), _child2(nullptr) {
_key(key), _size(size), _keyDamage(0) {
_type = wintype_Pair;
}
PairWindow::~PairWindow() {
delete _child1;
delete _child2;
for (uint idx = 0; idx < _children.size(); ++idx)
delete _children[idx];
}
void PairWindow::rearrange(const Rect &box) {
@ -51,24 +51,23 @@ void PairWindow::rearrange(const Rect &box) {
_bbox = box;
if (!_backward) {
ch1 = _child1;
ch2 = _child2;
ch1 = _children[0];
ch2 = _children[1];
} else {
ch1 = _child2;
ch2 = _child1;
ch1 = _children[1];
ch2 = _children[0];
}
if (_dir == winmethod_OnTop) {
// ch2 is on top of ch1
ch1->rearrange(box1);
if (!ch2->_bbox.isEmpty() && !ch2->_bbox.contains(box)) {
// ch2 is outside new bounds, so clip it to the new dimensions
Rect subRect = ch2->_bbox;
subRect.clip(box);
ch2->rearrange(subRect);
if (_dir == winmethod_Arbitrary) {
// When a pair window is in "arbitrary" mode, each child window has it's own independant positioning,
// so thre's no need to be readjusting it
/*
for (int ctr = 0, idx = (_backward ? (int)_children.size() - 1 : 0); ctr < (int)_children.size();
++ctr, idx += (_backward ? -1 : 1)) {
Window *w = _children[idx];
w->rearrange();
}
*/
return;
}
@ -142,10 +141,12 @@ void PairWindow::rearrange(const Rect &box) {
void PairWindow::redraw() {
Window::redraw();
_child1->redraw();
_child2->redraw();
for (int ctr = 0, idx = (_backward ? (int)_children.size() - 1 : 0); ctr < (int)_children.size();
++ctr, idx += (_backward ? -1 : 1)) {
_children[idx]->redraw();
}
Window *child = !_backward ? _child1 : _child2;
Window *child = !_backward ? _children.front() : _children.back();
Rect box(child->_bbox.left, child->_yAdj ? child->_bbox.top - child->_yAdj : child->_bbox.top,
child->_bbox.right, child->_bbox.bottom);
@ -184,6 +185,7 @@ void PairWindow::getArrangement(uint *method, uint *size, Window **keyWin) {
void PairWindow::setArrangement(uint method, uint size, Window *keyWin) {
uint newDir;
bool newVertical, newBackward;
assert((method & winmethod_DirMask) != winmethod_Arbitrary && _dir != winmethod_Arbitrary);
if (_key) {
Window *wx;
@ -226,9 +228,7 @@ void PairWindow::setArrangement(uint method, uint size, Window *keyWin) {
if ((newBackward && !_backward) || (!newBackward && _backward)) {
// switch the children
Window *tmpWin = _child1;
_child1 = _child2;
_child2 = tmpWin;
SWAP(_children[0], _children[1]);
}
// set up everything else
@ -245,11 +245,12 @@ void PairWindow::setArrangement(uint method, uint size, Window *keyWin) {
}
void PairWindow::click(const Point &newPos) {
if (_child1->_bbox.contains(newPos))
_child1->click(newPos);
if (_child2->_bbox.contains(newPos))
_child2->click(newPos);
for (int ctr = 0, idx = (!_backward ? (int)_children.size() - 1 : 0); ctr < (int)_children.size();
++ctr, idx += (_backward ? -1 : 1)) {
Window *w = _children[idx];
if (w->_bbox.contains(newPos))
w->click(newPos);
}
}
} // End of namespace Glk

View File

@ -24,15 +24,17 @@
#define GLK_WINDOW_PAIR_H
#include "glk/windows.h"
#include "glk/utils.h"
namespace Glk {
/**
* Pair window
* Acts as a container of child windows. Under most cases there will be exactly two children,
* though in a special new "OnTop" mode, there can be more than two
*/
class PairWindow : public Window {
public:
Window *_child1, *_child2;
Array<Window *> _children;
// split info...
uint _dir; ///< winmethod_Left, Right, Above, Below, or OnTop

View File

@ -104,15 +104,22 @@ Window *Windows::windowOpen(Window *splitwin, uint method, uint size,
val = (method & winmethod_DirMask);
if (val != winmethod_Above && val != winmethod_Below && val != winmethod_Left
&& val != winmethod_Right && val != winmethod_OnTop) {
&& val != winmethod_Right && val != winmethod_Arbitrary) {
warning("window_open: invalid method (bad direction)");
return nullptr;
}
oldparent = splitwin->_parent;
if (oldparent && oldparent->_type != wintype_Pair) {
warning("window_open: parent window is not Pair");
return nullptr;
if (splitwin->_type == wintype_Pair) {
if ((method & winmethod_DirMask) != winmethod_Arbitrary) {
warning("window_open: Can only add windows to a Pair window in arbitrary mode");
return nullptr;
}
} else {
oldparent = splitwin->_parent;
if (oldparent && oldparent->_type != wintype_Pair) {
warning("window_open: parent window is not Pair");
return nullptr;
}
}
}
@ -125,11 +132,16 @@ Window *Windows::windowOpen(Window *splitwin, uint method, uint size,
if (!splitwin) {
_rootWin = newwin;
} else if (splitwin->_type == wintype_Pair) {
pairWin = static_cast<PairWindow *>(splitwin);
pairWin->_dir = winmethod_Arbitrary;
pairWin->_children.push_back(newwin);
newwin->_parent = pairWin;
} else {
// create pairWin, with newwin as the key
pairWin = newPairWindow(method, newwin, size);
pairWin->_child1 = splitwin;
pairWin->_child2 = newwin;
pairWin->_children.push_back(splitwin);
pairWin->_children.push_back(newwin);
splitwin->_parent = pairWin;
newwin->_parent = pairWin;
@ -138,10 +150,11 @@ Window *Windows::windowOpen(Window *splitwin, uint method, uint size,
if (oldparent) {
PairWindow *parentWin = dynamic_cast<PairWindow *>(oldparent);
assert(parentWin);
if (parentWin->_child1 == splitwin)
parentWin->_child1 = pairWin;
else
parentWin->_child2 = pairWin;
for (uint idx = 0; idx < parentWin->_children.size(); ++idx) {
if (parentWin->_children[idx] == splitwin)
parentWin->_children[idx] = pairWin;
}
} else {
_rootWin = pairWin;
}
@ -168,24 +181,22 @@ void Windows::windowClose(Window *win, StreamResult *result) {
PairWindow *pairWin = dynamic_cast<PairWindow *>(win->_parent);
PairWindow *grandparWin;
if (win == pairWin->_child1) {
sibWin = pairWin->_child2;
} else if (win == pairWin->_child2) {
sibWin = pairWin->_child1;
} else {
int index = pairWin->_children.indexOf(win);
if (index == -1) {
warning("windowClose: window tree is corrupted");
return;
}
sibWin = (index = ((int)pairWin->_children.size() - 1)) ?
pairWin->_children.front() : pairWin->_children[index + 1];
grandparWin = dynamic_cast<PairWindow *>(pairWin->_parent);
if (!grandparWin) {
_rootWin = sibWin;
sibWin->_parent = nullptr;
} else {
if (grandparWin->_child1 == pairWin)
grandparWin->_child1 = sibWin;
else
grandparWin->_child2 = sibWin;
index = grandparWin->_children.indexOf(pairWin);
grandparWin->_children[index] = sibWin;
sibWin->_parent = grandparWin;
}
@ -197,10 +208,8 @@ void Windows::windowClose(Window *win, StreamResult *result) {
win->close(true);
// This probably isn't necessary, but the child *is* gone, so just in case.
if (win == pairWin->_child1)
pairWin->_child1 = nullptr;
else if (win == pairWin->_child2)
pairWin->_child2 = nullptr;
index = pairWin->_children.indexOf(win);
pairWin->_children[index] = nullptr;
// Now we can delete the parent pair.
pairWin->close(false);
@ -462,21 +471,20 @@ Window *Windows::iterateTreeOrder(Window *win) {
PairWindow *pairWin = dynamic_cast<PairWindow *>(win);
if (pairWin) {
if (!pairWin->_backward)
return pairWin->_child1;
else
return pairWin->_child2;
return pairWin->_backward ? pairWin->_children.back() : pairWin->_children.front();
} else {
while (win->_parent) {
pairWin = dynamic_cast<PairWindow *>(win->_parent);
assert(pairWin);
int index = pairWin->_children.indexOf(win);
assert(index != -1);
if (!pairWin->_backward) {
if (win == pairWin->_child1)
return pairWin->_child2;
if (index < ((int)pairWin->_children.size() - 1))
return pairWin->_children[index + 1];
} else {
if (win == pairWin->_child2)
return pairWin->_child1;
if (index > 0)
return pairWin->_children[index - 1];
}
win = pairWin;
@ -515,10 +523,11 @@ Window::~Window() {
// Remove the window from any parent
PairWindow *parent = dynamic_cast<PairWindow *>(_parent);
if (parent && parent->_child1 == this)
parent->_child1 = nullptr;
if (parent && parent->_child2 == this)
parent->_child2 = nullptr;
if (parent) {
int index = parent->_children.indexOf(this);
if (index != -1)
parent->_children[index] = nullptr;
}
// Delete any attached window stream
_echoStream = nullptr;
@ -554,8 +563,8 @@ void Window::close(bool recurse) {
PairWindow *pairWin = dynamic_cast<PairWindow *>(this);
if (pairWin) {
pairWin->_child1->close(recurse);
pairWin->_child2->close(recurse);
for (uint idx = 0; idx < pairWin->_children.size(); ++idx)
pairWin->_children[idx]->close();
}
// Finally, delete the window