From 0306c24f3cbfcb84fbea03bc7592370f634b8dbe Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 22 Sep 2014 02:07:13 +0100 Subject: [PATCH] libcommon|Menu: Cleanup --- doomsday/plugins/common/include/menu/page.h | 63 ++- doomsday/plugins/common/src/hu_menu.cpp | 105 ++--- doomsday/plugins/common/src/m_ctrl.cpp | 9 +- doomsday/plugins/common/src/menu/page.cpp | 492 ++++++++++---------- 4 files changed, 319 insertions(+), 350 deletions(-) diff --git a/doomsday/plugins/common/include/menu/page.h b/doomsday/plugins/common/include/menu/page.h index 059441d012..4536930f68 100644 --- a/doomsday/plugins/common/include/menu/page.h +++ b/doomsday/plugins/common/include/menu/page.h @@ -74,7 +74,7 @@ class Page * @param drawer * @param cmdResponder */ - Page(de::String name, Point2Raw const &origin = Point2Raw(), int flags = 0, + Page(de::String name, de::Vector2i const &origin = de::Vector2i(), int flags = 0, OnDrawCallback drawer = 0, CommandResponder cmdResponder = 0); @@ -85,6 +85,18 @@ class Page */ de::String name() const; + void setTitle(de::String const &newTitle); + de::String title() const; + + void setOrigin(de::Vector2i const &newOrigin); + de::Vector2i origin() const; + + void setX(int x); + void setY(int y); + + void setPreviousPage(Page *newPreviousPage); + Page *previousPage() const; + /** * Adds a widget object as a child widget of the Page and sets up the Widget -> Page * relationship. The object must be an instance of a class derived from Widget. @@ -110,25 +122,6 @@ class Page */ Widget &addWidget(Widget *widget); - /** - * Provides access to the list of child widgets of the Page, for efficient traversal. - */ - Widgets const &widgets() const; - - /** - * Returns the total number of child widgets of the Page. - */ - inline int widgetCount() const { return widgets().count(); } - - void setTitle(de::String const &newTitle); - de::String title() const; - - void setX(int x); - void setY(int y); - - void setPreviousPage(Page *newPreviousPage); - Page *previousPage() const; - /** * Locate a widget on the page in the specified @a group. * @@ -142,6 +135,16 @@ class Page Widget *tryFindWidget(int flags, int group = 0); + /** + * Provides access to the list of child widgets of the Page, for efficient traversal. + */ + Widgets const &widgets() const; + + /** + * Returns the total number of child widgets of the Page. + */ + inline int widgetCount() const { return widgets().count(); } + /** * Returns the in-page index of the given @a widget; otherwise @c -1 */ @@ -150,13 +153,12 @@ class Page } /** - * Attempt to give focus to the given widget which is thought to be on the page. - * If @a newFocusWidget is present and is not currently in-focus, an out-focus - * action is first sent to the presently focused widget, then this page's focused - * widget is set before finally executing an in-focus action on the new widget. - * If the widget is not found on this page then nothing will happen. + * Attempt to give focus to the widget specified. If @a newFocusWidget is not @c nullptr, + * is present and is not currently in-focus, an out-focus action is first sent to the + * presently focused widget, then this page's focused widget is set before finally + * triggering an in-focus action on the new widget. * - * @param newFocusWidget Widget to be given focus. + * @param newFocusWidget Widget to be given focus. Use @c nullptr to clear. */ void setFocus(Widget *newFocusWidget); @@ -165,9 +167,6 @@ class Page */ Widget *focusWidget(); - void clearFocusWidget(); - void refocus(); - /** * Returns the current time in tics since last page activation. */ @@ -216,11 +215,7 @@ class Page */ int lineHeight(int *lineOffset = 0); - void initialize(); - void initWidgets(); - void updateWidgets(); - - void applyLayout(); + void activate(); int handleCommand(menucommand_e cmd); diff --git a/doomsday/plugins/common/src/hu_menu.cpp b/doomsday/plugins/common/src/hu_menu.cpp index 62acb8e684..1dc108f819 100644 --- a/doomsday/plugins/common/src/hu_menu.cpp +++ b/doomsday/plugins/common/src/hu_menu.cpp @@ -148,8 +148,6 @@ static void Hu_MenuInitNewGame(dd_bool confirmed); static void initAllPages(); static void destroyAllPages(); -static void initAllObjectsOnAllPages(); - static void Hu_MenuUpdateCursorState(); static dd_bool Hu_MenuHasCursorRotation(Widget *wi); @@ -331,9 +329,9 @@ static void Hu_MenuLoadResources() void Hu_MenuInitColorWidgetPage() { #if __JHERETIC__ || __JHEXEN__ - Point2Raw const origin(98, 60); + Vector2i const origin(98, 60); #else - Point2Raw const origin(124, 60); + Vector2i const origin(124, 60); #endif Page *page = Hu_MenuAddPage(new Page("ColorWidget", origin, MPF_NEVER_SCROLL, NULL, Hu_MenuColorWidgetCmdResponder)); @@ -380,9 +378,9 @@ void Hu_MenuInitColorWidgetPage() void Hu_MenuInitMainPage() { #if __JHEXEN__ || __JHERETIC__ - Point2Raw origin(110, 56); + Vector2i origin(110, 56); #else - Point2Raw origin(97, 64); + Vector2i origin(97, 64); #endif #if __JDOOM__ @@ -518,9 +516,9 @@ void Hu_MenuInitMainPage() void Hu_MenuInitGameTypePage() { #if __JDOOM__ || __JDOOM64__ - Point2Raw origin(97, 65); + Vector2i origin(97, 65); #else - Point2Raw origin(104, 65); + Vector2i origin(104, 65); #endif Page *page = Hu_MenuAddPage(new Page("GameType", origin, 0, Hu_MenuDrawGameTypePage)); @@ -553,11 +551,11 @@ void Hu_MenuInitGameTypePage() void Hu_MenuInitSkillPage() { #if __JHEXEN__ - Point2Raw const origin(120, 44); + Vector2i const origin(120, 44); #elif __JHERETIC__ - Point2Raw const origin(38, 30); + Vector2i const origin(38, 30); #else - Point2Raw const origin(48, 63); + Vector2i const origin(48, 63); #endif Widget::Flags skillButtonFlags[NUM_SKILL_MODES] = { Widget::Id0, @@ -620,9 +618,9 @@ void Hu_MenuInitSkillPage() void Hu_MenuInitMultiplayerPage() { #if __JHERETIC__ || __JHEXEN__ - Point2Raw const origin(97, 65); + Vector2i const origin(97, 65); #else - Point2Raw const origin(97, 65); + Vector2i const origin(97, 65); #endif Page *page = Hu_MenuAddPage(new Page("Multiplayer", origin, 0, Hu_MenuDrawMultiplayerPage)); @@ -647,9 +645,9 @@ void Hu_MenuInitMultiplayerPage() void Hu_MenuInitPlayerSetupPage() { #if __JHERETIC__ || __JHEXEN__ - Point2Raw const origin(70, 44); + Vector2i const origin(70, 44); #else - Point2Raw const origin(70, 54); + Vector2i const origin(70, 54); #endif Page *page = Hu_MenuAddPage(new Page("PlayerSetup", origin, 0, Hu_MenuDrawPlayerSetupPage)); @@ -736,7 +734,7 @@ void Hu_MenuInitPlayerSetupPage() void Hu_MenuInitSaveOptionsPage() { - Page *page = Hu_MenuAddPage(new Page("SaveOptions", Point2Raw(60, 50))); + Page *page = Hu_MenuAddPage(new Page("SaveOptions", Vector2i(60, 50))); page->setTitle("Save Options"); page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA)); page->setPreviousPage(Hu_MenuPagePtr("Options")); @@ -763,7 +761,7 @@ void Hu_MenuInitSaveOptionsPage() #if __JHERETIC__ || __JHEXEN__ void Hu_MenuInitFilesPage() { - Page *page = Hu_MenuAddPage(new Page("Files", Point2Raw(110, 60), MPF_LAYOUT_FIXED | MPF_NEVER_SCROLL)); + Page *page = Hu_MenuAddPage(new Page("Files", Vector2i(110, 60), MPF_LAYOUT_FIXED | MPF_NEVER_SCROLL)); page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB)); page->setPreviousPage(Hu_MenuPagePtr("Main")); @@ -838,9 +836,9 @@ int Hu_MenuSaveSlotCommandResponder(Widget *wi, menucommand_e cmd) void Hu_MenuInitLoadGameAndSaveGamePages() { #if __JDOOM__ || __JDOOM64__ - Point2Raw const origin(80, 54); + Vector2i const origin(80, 54); #else - Point2Raw const origin(70, 30); + Vector2i const origin(70, 30); #endif Widget::Flags const saveSlotObjectIds[NUMSAVESLOTS] = { Widget::Id0, Widget::Id1, Widget::Id2, Widget::Id3, Widget::Id4, Widget::Id5, @@ -896,9 +894,9 @@ void Hu_MenuInitLoadGameAndSaveGamePages() void Hu_MenuInitOptionsPage() { #if __JHERETIC__ || __JHEXEN__ - Point2Raw const origin(110, 63); + Vector2i const origin(110, 63); #else - Point2Raw const origin(110, 63); + Vector2i const origin(110, 63); #endif Page *page = Hu_MenuAddPage(new Page("Options", origin, 0, Hu_MenuDrawOptionsPage)); @@ -979,11 +977,11 @@ void Hu_MenuInitOptionsPage() void Hu_MenuInitGameplayOptionsPage() { #if __JHEXEN__ - Point2Raw const origin(88, 25); + Vector2i const origin(88, 25); #elif __JHERETIC__ - Point2Raw const origin(30, 40); + Vector2i const origin(30, 40); #else - Point2Raw const origin(30, 40); + Vector2i const origin(30, 40); #endif Page *page = Hu_MenuAddPage(new Page("GameplayOptions", origin)); @@ -1139,9 +1137,9 @@ void Hu_MenuInitGameplayOptionsPage() void Hu_MenuInitHUDOptionsPage() { #if __JDOOM__ || __JDOOM64__ - Point2Raw const origin(97, 40); + Vector2i const origin(97, 40); #else - Point2Raw const origin(97, 28); + Vector2i const origin(97, 28); #endif Page *page = Hu_MenuAddPage(new Page("HudOptions", origin)); @@ -1482,9 +1480,9 @@ void Hu_MenuInitHUDOptionsPage() void Hu_MenuInitAutomapOptionsPage() { #if __JHERETIC__ || __JHEXEN__ - Point2Raw const origin(64, 28); + Vector2i const origin(64, 28); #else - Point2Raw const origin(70, 40); + Vector2i const origin(70, 40); #endif Page *page = Hu_MenuAddPage(new Page("AutomapOptions", origin)); @@ -1578,11 +1576,11 @@ static bool compareWeaponPriority(ListWidgetItem const *a, ListWidgetItem const void Hu_MenuInitWeaponsPage() { #if __JDOOM__ || __JDOOM64__ - Point2Raw const origin(78, 40); + Vector2i const origin(78, 40); #elif __JHERETIC__ - Point2Raw const origin(78, 26); + Vector2i const origin(78, 26); #elif __JHEXEN__ - Point2Raw const origin(78, 38); + Vector2i const origin(78, 38); #endif const struct { @@ -1712,7 +1710,7 @@ void Hu_MenuInitWeaponsPage() #if __JHERETIC__ || __JHEXEN__ void Hu_MenuInitInventoryOptionsPage() { - Page *page = Hu_MenuAddPage(new Page("InventoryOptions", Point2Raw(78, 48))); + Page *page = Hu_MenuAddPage(new Page("InventoryOptions", Vector2i(78, 48))); page->setTitle("Inventory Options"); page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA)); page->setPreviousPage(Hu_MenuPagePtr("Options")); @@ -1764,11 +1762,11 @@ void Hu_MenuInitInventoryOptionsPage() void Hu_MenuInitSoundOptionsPage() { #if __JHEXEN__ - Point2Raw const origin(97, 25); + Vector2i const origin(97, 25); #elif __JHERETIC__ - Point2Raw const origin(97, 30); + Vector2i const origin(97, 30); #elif __JDOOM__ || __JDOOM64__ - Point2Raw const origin(97, 40); + Vector2i const origin(97, 40); #endif Page *page = Hu_MenuAddPage(new Page("SoundOptions", origin)); @@ -1791,11 +1789,11 @@ void Hu_MenuInitSoundOptionsPage() void Hu_MenuInitEpisodePage() { #if __JHEXEN__ - Point2Raw const origin(120, 44); + Vector2i const origin(120, 44); #elif __JHERETIC__ - Point2Raw const origin(80, 50); + Vector2i const origin(80, 50); #else - Point2Raw const origin(48, 63); + Vector2i const origin(48, 63); #endif Page *page = Hu_MenuAddPage(new Page("Episode", origin, MPF_LAYOUT_FIXED, Hu_MenuDrawEpisodePage)); @@ -1905,7 +1903,7 @@ void Hu_MenuInitPlayerClassPage() } } - Page *page = Hu_MenuAddPage(new Page("PlayerClass", Point2Raw(66, 66), MPF_LAYOUT_FIXED | MPF_NEVER_SCROLL, Hu_MenuDrawPlayerClassPage)); + Page *page = Hu_MenuAddPage(new Page("PlayerClass", Vector2i(66, 66), MPF_LAYOUT_FIXED | MPF_NEVER_SCROLL, Hu_MenuDrawPlayerClassPage)); page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB)); page->setPreviousPage(Hu_MenuPagePtr("Episode")); @@ -2016,7 +2014,6 @@ void Hu_MenuInit() Hu_MenuLoadResources(); initAllPages(); - initAllObjectsOnAllPages(); #if __JDOOM__ if(gameModeBits & GM_ANY_DOOM2) @@ -2154,14 +2151,12 @@ void Hu_MenuSetPage(Page *page, bool canReactivate) if(currentPage == page) { if(!canReactivate) return; - page->clearFocusWidget(); + page->setFocus(0); } - page->updateWidgets(); - // This is now the "active" page. currentPage = page; - page->initialize(); + page->activate(); } bool Hu_MenuIsVisible() @@ -2380,14 +2375,6 @@ static void destroyAllPages() pages.clear(); } -static void initAllObjectsOnAllPages() -{ - foreach(Page *page, pages) - { - page->initWidgets(); - } -} - int Hu_MenuColorWidgetCmdResponder(Page *page, menucommand_e cmd) { DENG2_ASSERT(page != 0); @@ -2820,17 +2807,17 @@ void Hu_MenuActivateColorWidget(Widget *wi, Widget::mn_actionid_t action) ColorEditWidget &cbox = wi->as(); - Page &colorWidgetPage = Hu_MenuPage("ColorWidget"); + Page &colorWidgetPage = Hu_MenuPage("ColorWidget"); ColorEditWidget &cboxMix = colorWidgetPage.findWidget(Widget::Id0).as(); - SliderWidget &sldrRed = colorWidgetPage.findWidget(Widget::Id1).as(); - SliderWidget &sldrGreen = colorWidgetPage.findWidget(Widget::Id2).as(); - SliderWidget &sldrBlue = colorWidgetPage.findWidget(Widget::Id3).as(); - LabelWidget &labelAlpha = colorWidgetPage.findWidget(Widget::Id4).as(); - SliderWidget &sldrAlpha = colorWidgetPage.findWidget(Widget::Id5).as(); + SliderWidget &sldrRed = colorWidgetPage.findWidget(Widget::Id1).as(); + SliderWidget &sldrGreen = colorWidgetPage.findWidget(Widget::Id2).as(); + SliderWidget &sldrBlue = colorWidgetPage.findWidget(Widget::Id3).as(); + LabelWidget &labelAlpha = colorWidgetPage.findWidget(Widget::Id4).as(); + SliderWidget &sldrAlpha = colorWidgetPage.findWidget(Widget::Id5).as(); colorWidgetActive = true; - colorWidgetPage.initialize(); + colorWidgetPage.activate(); colorWidgetPage.setUserValue(qVariantFromValue((void *)wi)); // Ugly or what... cboxMix.setColor(cbox.color(), 0); diff --git a/doomsday/plugins/common/src/m_ctrl.cpp b/doomsday/plugins/common/src/m_ctrl.cpp index 5f4b69fb47..f95220edd5 100644 --- a/doomsday/plugins/common/src/m_ctrl.cpp +++ b/doomsday/plugins/common/src/m_ctrl.cpp @@ -225,13 +225,6 @@ void Hu_MenuActivateBindingsGrab(Widget * /*ob*/, Widget::mn_actionid_t /*action void Hu_MenuInitControlsPage() { -#if __JDOOM__ || __JDOOM64__ - Point2Raw const pageOrigin(32, 40); -#elif __JHERETIC__ - Point2Raw const pageOrigin(32, 40); -#elif __JHEXEN__ - Point2Raw const pageOrigin(32, 40); -#endif int configCount = sizeof(controlConfig) / sizeof(controlConfig[0]); LOGDEV_VERBOSE("Hu_MenuInitControlsPage: Creating controls items"); @@ -252,7 +245,7 @@ void Hu_MenuInitControlsPage() } } - Page *page = Hu_MenuAddPage(new Page("ControlOptions", pageOrigin, 0, Hu_MenuDrawControlsPage)); + Page *page = Hu_MenuAddPage(new Page("ControlOptions", Vector2i(32, 40), 0, Hu_MenuDrawControlsPage)); page->setTitle("Controls"); page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA)); page->setPreviousPage(Hu_MenuPagePtr("Options")); diff --git a/doomsday/plugins/common/src/menu/page.cpp b/doomsday/plugins/common/src/menu/page.cpp index 6a73b23a56..93a57df015 100644 --- a/doomsday/plugins/common/src/menu/page.cpp +++ b/doomsday/plugins/common/src/menu/page.cpp @@ -50,7 +50,7 @@ DENG2_PIMPL(Page) Widgets widgets; /// "Physical" geometry in fixed 320x200 screen coordinate space. - Point2Raw origin; + Vector2i origin; Rect *geometry = Rect_New(); String title; ///< Title of this page. @@ -100,6 +100,115 @@ DENG2_PIMPL(Page) } } + void applyLayout() + { + Rect_SetXY(geometry, 0, 0); + Rect_SetWidthHeight(geometry, 0, 0); + + // Apply layout logic to this page. + + if(flags & MPF_LAYOUT_FIXED) + { + // This page uses a fixed layout. + for(Widget *wi : widgets) + { + if(wi->isHidden()) continue; + + Rect_SetXY(wi->geometry(), wi->fixedOrigin()->x, wi->fixedOrigin()->y); + Rect_Unite(geometry, wi->geometry()); + } + return; + } + + // This page uses a dynamic layout. + int lineOffset; + int lh = self.lineHeight(&lineOffset); + + Vector2i origin; + + for(int i = 0; i < widgets.count(); ) + { + Widget *wi = widgets[i]; + Widget *nextWi = i + 1 < widgets.count()? widgets[i + 1] : 0; + + if(wi->isHidden()) + { + // Proceed to the next widget! + i += 1; + continue; + } + + // If the widget has a fixed position, we will ignore it while doing + // dynamic layout. + if(wi->flags() & Widget::PositionFixed) + { + Rect_SetXY(wi->geometry(), wi->fixedOrigin()->x, wi->fixedOrigin()->y); + Rect_Unite(geometry, wi->geometry()); + + // To the next object. + i += 1; + continue; + } + + // An additional offset requested? + if(wi->flags() & Widget::LayoutOffset) + { + origin.x += wi->fixedOrigin()->x; + origin.y += wi->fixedOrigin()->y; + } + + Rect_SetXY(wi->geometry(), origin.x, origin.y); + + // Orient label plus button/inline-list/textual-slider pairs about a + // vertical dividing line, with the label on the left, other widget + // on the right. + // @todo Do not assume pairing, a widget should designate it's label. + if(wi->is() && nextWi) + { + if(!nextWi->isHidden() && + (nextWi->is() || + nextWi->is() || + nextWi->is() || + nextWi->is() || + nextWi->is())) + { + int const margin = lineOffset * 2; + + Rect_SetXY(nextWi->geometry(), margin + Rect_Width(wi->geometry()), origin.y); + + RectRaw united; + origin.y += Rect_United(wi->geometry(), nextWi->geometry(), &united) + ->size.height + lineOffset; + + Rect_UniteRaw(geometry, &united); + + // Extra spacing between object groups. + if(i + 2 < widgets.count() && nextWi->group() != widgets[i + 2]->group()) + { + origin.y += lh; + } + + // Proceed to the next object! + i += 2; + continue; + } + } + + Rect_Unite(geometry, wi->geometry()); + + origin.y += Rect_Height(wi->geometry()) + lineOffset; + + // Extra spacing between object groups. + if(nextWi && nextWi->group() != wi->group()) + { + origin.y += lh; + } + + // Proceed to the next object! + i += 1; + } + } + /// @pre @a wi is a child of this page. void giveChildFocus(Widget *wi, bool allowRefocus = false) { @@ -130,6 +239,95 @@ DENG2_PIMPL(Page) } } + void refocus() + { + LOG_AS("Page"); + + // If we haven't yet visited this page then find the first focusable + // widget and select it. + if(focus < 0) + { + int i, giveFocus = -1; + + // First look for a default focus widget. There should only be one + // but find the last with this flag... + for(i = 0; i < widgets.count(); ++i) + { + Widget *wi = widgets[i]; + if((wi->flags() & Widget::DefaultFocus) && !(wi->isDisabled() || (wi->flags() & Widget::NoFocus))) + { + giveFocus = i; + } + } + + // No default focus? Find the first focusable widget. + if(-1 == giveFocus) + for(i = 0; i < widgets.count(); ++i) + { + Widget *wi = widgets[i]; + if(!(wi->isDisabled() || (wi->flags() & Widget::NoFocus))) + { + giveFocus = i; + break; + } + } + + if(-1 != giveFocus) + { + giveChildFocus(widgets[giveFocus]); + } + else + { + LOGDEV_WARNING("No focusable widget"); + } + } + else + { + // We've been here before; re-focus on the last focused object. + giveChildFocus(widgets[focus], true); + } + } + + void fetch() + { + for(Widget *wi : widgets) + { + if(CVarToggleWidget *tog = wi->maybeAs()) + { + int value = Con_GetByte(tog->cvarPath()) & (tog->cvarValueMask()? tog->cvarValueMask() : ~0); + tog->setState(value? CVarToggleWidget::Down : CVarToggleWidget::Up); + tog->setText(tog->isDown()? tog->downText() : tog->upText()); + } + if(CVarInlineListWidget *list = wi->maybeAs()) + { + int itemValue = Con_GetInteger(list->cvarPath()); + if(int valueMask = list->cvarValueMask()) + itemValue &= valueMask; + list->selectItemByValue(itemValue); + } + if(CVarLineEditWidget *edit = wi->maybeAs()) + { + edit->setText(Con_GetString(edit->cvarPath())); + } + if(CVarSliderWidget *sldr = wi->maybeAs()) + { + float value; + if(sldr->floatMode()) + value = Con_GetFloat(sldr->cvarPath()); + else + value = Con_GetInteger(sldr->cvarPath()); + sldr->setValue(value); + } + if(CVarColorEditWidget *cbox = wi->maybeAs()) + { + cbox->setColor(Vector4f(Con_GetFloat(cbox->redCVarPath()), + Con_GetFloat(cbox->greenCVarPath()), + Con_GetFloat(cbox->blueCVarPath()), + (cbox->rgbaMode()? Con_GetFloat(cbox->alphaCVarPath()) : 1.f))); + } + } + } + /** * Determines the size of the menu cursor for a focused widget. If no widget is currently * focused the default cursor size (i.e., the effective line height for @c MENU_FONT1) @@ -150,11 +348,11 @@ DENG2_PIMPL(Page) } }; -Page::Page(String name, Point2Raw const &origin, int flags, +Page::Page(String name, Vector2i const &origin, int flags, OnDrawCallback drawer, CommandResponder cmdResponder) : d(new Instance(this)) { - std::memcpy(&d->origin, &origin, sizeof(d->origin)); + d->origin = origin; d->name = name; d->flags = flags; d->drawer = drawer; @@ -173,7 +371,8 @@ Widget &Page::addWidget(Widget *widget) { DENG2_ASSERT(widget != 0); d->widgets << widget; - widget->setPage(this); + widget->setPage(this) + .setFlags(Widget::Focused, UnsetFlags); // Not focused initially. return *widget; } @@ -200,121 +399,6 @@ int Page::lineHeight(int *lineOffset) return lh; } -static inline bool widgetIsDrawable(Widget *wi) -{ - DENG2_ASSERT(wi); - return !(wi->flags() & Widget::Hidden); -} - -void Page::applyLayout() -{ - Rect_SetXY(d->geometry, 0, 0); - Rect_SetWidthHeight(d->geometry, 0, 0); - - // Apply layout logic to this page. - - if(d->flags & MPF_LAYOUT_FIXED) - { - // This page uses a fixed layout. - for(Widget *wi : d->widgets) - { - if(!widgetIsDrawable(wi)) continue; - - Rect_SetXY(wi->geometry(), wi->fixedOrigin()->x, wi->fixedOrigin()->y); - Rect_Unite(d->geometry, wi->geometry()); - } - return; - } - - // This page uses a dynamic layout. - int lineOffset; - int lh = lineHeight(&lineOffset); - - Point2Raw origin; - - for(int i = 0; i < d->widgets.count(); ) - { - Widget *wi = d->widgets[i]; - Widget *nextWi = i + 1 < d->widgets.count()? d->widgets[i + 1] : 0; - - if(!widgetIsDrawable(wi)) - { - // Proceed to the next widget! - i += 1; - continue; - } - - // If the widget has a fixed position, we will ignore it while doing - // dynamic layout. - if(wi->flags() & Widget::PositionFixed) - { - Rect_SetXY(wi->geometry(), wi->fixedOrigin()->x, wi->fixedOrigin()->y); - Rect_Unite(d->geometry, wi->geometry()); - - // To the next object. - i += 1; - continue; - } - - // An additional offset requested? - if(wi->flags() & Widget::LayoutOffset) - { - origin.x += wi->fixedOrigin()->x; - origin.y += wi->fixedOrigin()->y; - } - - Rect_SetXY(wi->geometry(), origin.x, origin.y); - - // Orient label plus button/inline-list/textual-slider pairs about a - // vertical dividing line, with the label on the left, other widget - // on the right. - // @todo Do not assume pairing, a widget should designate it's label. - if(wi->is() && nextWi) - { - if(widgetIsDrawable(nextWi) && - (nextWi->is() || - nextWi->is() || - nextWi->is() || - nextWi->is() || - nextWi->is())) - { - int const margin = lineOffset * 2; - - Rect_SetXY(nextWi->geometry(), margin + Rect_Width(wi->geometry()), origin.y); - - RectRaw united; - origin.y += Rect_United(wi->geometry(), nextWi->geometry(), &united) - ->size.height + lineOffset; - - Rect_UniteRaw(d->geometry, &united); - - // Extra spacing between object groups. - if(i + 2 < d->widgets.count() && nextWi->group() != d->widgets[i + 2]->group()) - { - origin.y += lh; - } - - // Proceed to the next object! - i += 2; - continue; - } - } - - Rect_Unite(d->geometry, wi->geometry()); - - origin.y += Rect_Height(wi->geometry()) + lineOffset; - - // Extra spacing between object groups. - if(nextWi && nextWi->group() != wi->group()) - { - origin.y += lh; - } - - // Proceed to the next object! - i += 1; - } -} - void Page::setOnActiveCallback(Page::OnActiveCallback newCallback) { d->onActiveCallback = newCallback; @@ -330,7 +414,7 @@ static void composeSubpageString(Page *page, char *buf, size_t bufSize) } #endif -static void drawPageNavigation(Page *page, int x, int y) +static void drawPageNavigation(Page *page, Vector2i const origin) { int const currentPage = 0;//(page->firstObject + page->numVisObjects/2) / page->numVisObjects + 1; int const totalPages = 1;//(int)ceil((float)page->objectsCount/page->numVisObjects); @@ -350,15 +434,15 @@ static void drawPageNavigation(Page *page, int x, int y) FR_SetColorv(cfg.menuTextColors[1]); FR_SetAlpha(mnRendState->pageAlpha); - FR_DrawTextXY3(buf, x, y, ALIGN_TOP, Hu_MenuMergeEffectWithDrawTextFlags(0)); + FR_DrawTextXY3(buf, origin.x, origin.y, ALIGN_TOP, Hu_MenuMergeEffectWithDrawTextFlags(0)); DGL_Disable(DGL_TEXTURE_2D); #else DGL_Enable(DGL_TEXTURE_2D); DGL_Color4f(1, 1, 1, mnRendState->pageAlpha); - GL_DrawPatchXY2( pInvPageLeft[currentPage == 0 || (menuTime & 8)], x - 144, y, ALIGN_RIGHT); - GL_DrawPatchXY2(pInvPageRight[currentPage == totalPages-1 || (menuTime & 8)], x + 144, y, ALIGN_LEFT); + GL_DrawPatchXY2( pInvPageLeft[currentPage == 0 || (menuTime & 8)], origin.x - 144, origin.y, ALIGN_RIGHT); + GL_DrawPatchXY2(pInvPageRight[currentPage == totalPages-1 || (menuTime & 8)], origin.x + 144, origin.y, ALIGN_LEFT); DGL_Disable(DGL_TEXTURE_2D); #endif @@ -369,7 +453,7 @@ static void drawPageHeading(Page *page, Point2Raw const *offset = nullptr) if(!page) return; if(page->title().isEmpty()) return; - Point2Raw origin(SCREENWIDTH / 2, (SCREENHEIGHT / 2) - ((SCREENHEIGHT / 2 - 5) / cfg.menuScale)); + Vector2i origin(SCREENWIDTH / 2, (SCREENHEIGHT / 2) - ((SCREENHEIGHT / 2 - 5) / cfg.menuScale)); if(offset) { origin.x += offset->x; @@ -378,7 +462,7 @@ static void drawPageHeading(Page *page, Point2Raw const *offset = nullptr) FR_PushAttrib(); Hu_MenuDrawPageTitle(page->title(), origin.x, origin.y); origin.y += 16; - drawPageNavigation(page, origin.x, origin.y); + drawPageNavigation(page, origin); FR_PopAttrib(); } @@ -436,11 +520,11 @@ void Page::draw(float alpha, bool showFocusCursor) // We can now layout the widgets of this page. /// @todo Do not modify the page layout here. - applyLayout(); + d->applyLayout(); // Determine the origin of the focus object (this dictates the page scroll location). Widget *focused = focusWidget(); - if(focused && !widgetIsDrawable(focused)) + if(focused && focused->isHidden()) { focused = 0; } @@ -534,7 +618,8 @@ void Page::draw(float alpha, bool showFocusCursor) if(d->drawer) { FR_PushAttrib(); - d->drawer(this, &d->origin); + Point2Raw offset(d->origin.x, d->origin.y); + d->drawer(this, &offset); FR_PopAttrib(); } @@ -556,6 +641,16 @@ String Page::title() const return d->title; } +void Page::setOrigin(Vector2i const &newOrigin) +{ + d->origin = newOrigin; +} + +Vector2i Page::origin() const +{ + return d->origin; +} + void Page::setX(int x) { d->origin.x = x; @@ -582,24 +677,6 @@ Widget *Page::focusWidget() return d->widgets[d->focus]; } -void Page::clearFocusWidget() -{ - if(d->focus >= 0) - { - Widget *wi = d->widgets[d->focus]; - if(wi->flags() & Widget::Active) - { - return; - } - } - d->focus = -1; - for(Widget *wi : d->widgets) - { - wi->setFlags(Widget::Focused, UnsetFlags); - } - refocus(); -} - Widget &Page::findWidget(int flags, int group) { if(Widget *wi = tryFindWidget(flags, group)) @@ -619,68 +696,38 @@ Widget *Page::tryFindWidget(int flags, int group) return 0; // Not found. } -void Page::setFocus(Widget *wi) -{ - int index = indexOf(wi); - if(index < 0) - { - DENG2_ASSERT(!"Page::Focus: Failed to determine index-in-page for widget."); - return; - } - d->giveChildFocus(d->widgets[index]); -} - -void Page::refocus() +void Page::setFocus(Widget *newFocus) { - LOG_AS("Page"); - - // If we haven't yet visited this page then find the first focusable - // widget and select it. - if(0 > d->focus) + // Are we clearing focus? + if(!newFocus) { - int i, giveFocus = -1; - - // First look for a default focus widget. There should only be one - // but find the last with this flag... - for(i = 0; i < d->widgets.count(); ++i) + if(Widget *focused = focusWidget()) { - Widget *wi = d->widgets[i]; - if((wi->flags() & Widget::DefaultFocus) && !(wi->isDisabled() || (wi->flags() & Widget::NoFocus))) - { - giveFocus = i; - } + if(focused->isActive()) return; } - // No default focus? Find the first focusable widget. - if(-1 == giveFocus) - for(i = 0; i < d->widgets.count(); ++i) - { - Widget *wi = d->widgets[i]; - if(!(wi->isDisabled() || (wi->flags() & Widget::NoFocus))) - { - giveFocus = i; - break; - } - } - - if(-1 != giveFocus) - { - d->giveChildFocus(d->widgets[giveFocus]); - } - else + d->focus = -1; + for(Widget *wi : d->widgets) { - LOGDEV_WARNING("No focusable widget"); + wi->setFlags(Widget::Focused, UnsetFlags); } + d->refocus(); + return; } - else + + int index = indexOf(newFocus); + if(index < 0) { - // We've been here before; re-focus on the last focused object. - d->giveChildFocus(d->widgets[d->focus], true); + DENG2_ASSERT(!"Page::Focus: Failed to determine index-in-page for widget."); + return; } + d->giveChildFocus(d->widgets[index]); } -void Page::initialize() +void Page::activate() { + d->fetch(); + // Reset page timer. d->timer = 0; @@ -704,7 +751,7 @@ void Page::initialize() return; } - refocus(); + d->refocus(); if(d->onActiveCallback) { @@ -712,59 +759,6 @@ void Page::initialize() } } -void Page::initWidgets() -{ - for(Widget *wi : d->widgets) - { - wi->setFlags(Widget::Focused, UnsetFlags); - } -} - -/// Main task is to update objects linked to cvars. -void Page::updateWidgets() -{ - for(Widget *wi : d->widgets) - { - if(wi->is() || wi->is()) - { - wi->setFlags(Widget::NoFocus); - } - if(CVarToggleWidget *tog = wi->maybeAs()) - { - int value = Con_GetByte(tog->cvarPath()) & (tog->cvarValueMask()? tog->cvarValueMask() : ~0); - tog->setState(value? CVarToggleWidget::Down : CVarToggleWidget::Up); - tog->setText(tog->isDown()? tog->downText() : tog->upText()); - } - if(CVarInlineListWidget *list = wi->maybeAs()) - { - int itemValue = Con_GetInteger(list->cvarPath()); - if(int valueMask = list->cvarValueMask()) - itemValue &= valueMask; - list->selectItemByValue(itemValue); - } - if(CVarLineEditWidget *edit = wi->maybeAs()) - { - edit->setText(Con_GetString(edit->cvarPath())); - } - if(CVarSliderWidget *sldr = wi->maybeAs()) - { - float value; - if(sldr->floatMode()) - value = Con_GetFloat(sldr->cvarPath()); - else - value = Con_GetInteger(sldr->cvarPath()); - sldr->setValue(value); - } - if(CVarColorEditWidget *cbox = wi->maybeAs()) - { - cbox->setColor(Vector4f(Con_GetFloat(cbox->redCVarPath()), - Con_GetFloat(cbox->greenCVarPath()), - Con_GetFloat(cbox->blueCVarPath()), - (cbox->rgbaMode()? Con_GetFloat(cbox->alphaCVarPath()) : 1.f))); - } - } -} - void Page::tick() { // Call the ticker of each object.