From f1d9722f3bd58f7883f3e76c19d4a0133da73489 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Wed, 2 Jan 2019 18:18:11 -0800 Subject: 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 --- engines/glk/frotz/windows.cpp | 18 ++++++++- engines/glk/frotz/windows.h | 5 +++ engines/glk/glk_api.cpp | 9 +++-- engines/glk/glk_types.h | 12 +++--- engines/glk/utils.h | 25 +++++++++++++ engines/glk/window_pair.cpp | 57 +++++++++++++++-------------- engines/glk/window_pair.h | 6 ++- engines/glk/windows.cpp | 85 ++++++++++++++++++++++++------------------- 8 files changed, 138 insertions(+), 79 deletions(-) (limited to 'engines/glk') diff --git a/engines/glk/frotz/windows.cpp b/engines/glk/frotz/windows.cpp index 1863e68032..ffee6392c7 100644 --- a/engines/glk/frotz/windows.cpp +++ b/engines/glk/frotz/windows.cpp @@ -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(_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 diff --git a/engines/glk/frotz/windows.h b/engines/glk/frotz/windows.h index ba277d57dc..43358665b2 100644 --- a/engines/glk/frotz/windows.h +++ b/engines/glk/frotz/windows.h @@ -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 diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp index e2da31706a..6e0f33ea56 100644 --- a/engines/glk/glk_api.cpp +++ b/engines/glk/glk_api.cpp @@ -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; } diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h index 2e23aec018..4a29b15abf 100644 --- a/engines/glk/glk_types.h +++ b/engines/glk/glk_types.h @@ -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, diff --git a/engines/glk/utils.h b/engines/glk/utils.h index 3655f57c33..2532f270a5 100644 --- a/engines/glk/utils.h +++ b/engines/glk/utils.h @@ -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 + */ +templateclass Array : public Common::Array { +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 */ diff --git a/engines/glk/window_pair.cpp b/engines/glk/window_pair.cpp index c90f389eac..7ddd57b23c 100644 --- a/engines/glk/window_pair.cpp +++ b/engines/glk/window_pair.cpp @@ -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 diff --git a/engines/glk/window_pair.h b/engines/glk/window_pair.h index 83cbcc884a..10d92648d0 100644 --- a/engines/glk/window_pair.h +++ b/engines/glk/window_pair.h @@ -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 _children; // split info... uint _dir; ///< winmethod_Left, Right, Above, Below, or OnTop diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp index de8fa52385..d75ba1f6e5 100644 --- a/engines/glk/windows.cpp +++ b/engines/glk/windows.cpp @@ -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(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(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(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(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(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(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(_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(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 -- cgit v1.2.3