diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index b3e0bbc869..2b7d26d2e1 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -363,6 +363,7 @@ DENG_HEADERS += \ include/ui/dialogs/networksettingsdialog.h \ include/ui/dialogs/renderersettingsdialog.h \ include/ui/dialogs/videosettingsdialog.h \ + include/ui/editors/rendererappearanceeditor.h \ include/ui/fi_main.h \ include/ui/finaleinterpreter.h \ include/ui/framework/actionitem.h \ @@ -411,7 +412,9 @@ DENG_HEADERS += \ include/ui/widgets/cvartogglewidget.h \ include/ui/widgets/dialogwidget.h \ include/ui/widgets/documentwidget.h \ + include/ui/widgets/foldpanelwidget.h \ include/ui/widgets/gameselectionwidget.h \ + include/ui/widgets/icvarwidget.h \ include/ui/widgets/labelwidget.h \ include/ui/widgets/legacywidget.h \ include/ui/widgets/lineeditwidget.h \ @@ -700,6 +703,7 @@ SOURCES += \ src/ui/dialogs/messagedialog.cpp \ src/ui/dialogs/networksettingsdialog.cpp \ src/ui/dialogs/videosettingsdialog.cpp \ + src/ui/editors/rendererappearanceeditor.cpp \ src/ui/fi_main.cpp \ src/ui/finaleinterpreter.cpp \ src/ui/framework/commandaction.cpp \ @@ -740,6 +744,7 @@ SOURCES += \ src/ui/widgets/cvartogglewidget.cpp \ src/ui/widgets/dialogwidget.cpp \ src/ui/widgets/documentwidget.cpp \ + src/ui/widgets/foldpanelwidget.cpp \ src/ui/widgets/gameselectionwidget.cpp \ src/ui/widgets/labelwidget.cpp \ src/ui/widgets/legacywidget.cpp \ diff --git a/doomsday/client/data/defaultstyle.pack/rules.dei b/doomsday/client/data/defaultstyle.pack/rules.dei index 831f14ebd9..a11b939249 100644 --- a/doomsday/client/data/defaultstyle.pack/rules.dei +++ b/doomsday/client/data/defaultstyle.pack/rules.dei @@ -28,7 +28,7 @@ group progress { } group slider { - rule width { constant $= UNIT * 50 } + rule width { constant $= UNIT * 55 } rule label { constant $= UNIT * 9 } rule editor { constant $= UNIT * 20 } } @@ -41,6 +41,10 @@ group dialog { rule download.width { constant $= UNIT * 115 } } +group sidebar { + rule width { constant $= UNIT * 80 } +} + group console { rule width { constant $= UNIT * 125 } } @@ -51,5 +55,5 @@ group gameselection { } group coloradjustment { - rule slider { constant $= slider.width.constant * 1.5 } + rule slider { constant $= slider.width.constant * 1.36 } } diff --git a/doomsday/client/include/clientapp.h b/doomsday/client/include/clientapp.h index 90c069fbfa..70db50a997 100644 --- a/doomsday/client/include/clientapp.h +++ b/doomsday/client/include/clientapp.h @@ -56,6 +56,7 @@ class ClientApp : public de::GuiApp static ClientApp &app(); static Updater &updater(); static SettingsRegister &rendererSettings(); ///< @todo Belongs in a subsystem. + static SettingsRegister &rendererAppearanceSettings(); ///< @todo Belongs in a subsystem. static SettingsRegister &audioSettings(); ///< @todo Belongs in AudioSystem. static ServerLink &serverLink(); static InputSystem &inputSystem(); diff --git a/doomsday/client/include/ui/clientwindow.h b/doomsday/client/include/ui/clientwindow.h index 7d3a9e3e28..ffeba50f97 100644 --- a/doomsday/client/include/ui/clientwindow.h +++ b/doomsday/client/include/ui/clientwindow.h @@ -63,6 +63,11 @@ class ClientWindow : public de::PersistentCanvasWindow, Busy }; + enum SidebarLocation + { + RightEdge + }; + public: ClientWindow(de::String const &id = "main"); @@ -73,6 +78,20 @@ class ClientWindow : public de::PersistentCanvasWindow, LegacyWidget &game(); BusyWidget &busy(); + /** + * Installs a sidebar widget into the window. If there is an existing + * sidebar, it will be deleted. Sidebar widgets are expected to control + * their own width (on the right/left edges) or height (on the top/bottom + * edges). + * + * @param location Location to attach the sidebar. Window takes ownership + * of the widget. + * @param sidebar Widget to install, or @c NULL to remove the sidebar. + */ + void setSidebar(SidebarLocation location, GuiWidget *sidebar); + + void unsetSidebar(SidebarLocation location) { setSidebar(location, 0); } + /** * Sets the operating mode of the window. In Busy mode, the normal * widgets of the window will be replaced with a single BusyWidget. diff --git a/doomsday/client/include/ui/dialogs/renderersettingsdialog.h b/doomsday/client/include/ui/dialogs/renderersettingsdialog.h index 06a01ed53f..9838e7b71f 100644 --- a/doomsday/client/include/ui/dialogs/renderersettingsdialog.h +++ b/doomsday/client/include/ui/dialogs/renderersettingsdialog.h @@ -41,6 +41,7 @@ protected slots: void renameProfile(); void duplicateProfile(); void deleteProfile(); + void showEditor(); private: DENG2_PRIVATE(d) diff --git a/doomsday/client/include/ui/editors/rendererappearanceeditor.h b/doomsday/client/include/ui/editors/rendererappearanceeditor.h new file mode 100644 index 0000000000..60c3b1e1ed --- /dev/null +++ b/doomsday/client/include/ui/editors/rendererappearanceeditor.h @@ -0,0 +1,49 @@ +/** @file rendererappearanceeditor.h + * + * @authors Copyright (c) 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef DENG_CLIENT_RENDERERAPPEARANCEEDITOR_H +#define DENG_CLIENT_RENDERERAPPEARANCEEDITOR_H + +#include "ui/widgets/panelwidget.h" + +/** + * Editor for modifying the settings for the renderer's visual appearance. + * + * Automatically installs itself into the main window's right sidebar. + * + * @see ClientApp::rendererAppearanceSettings() + */ +class RendererAppearanceEditor : public PanelWidget +{ + Q_OBJECT + +public: + RendererAppearanceEditor(); + +public slots: + void showRendererSettings(); + +protected: + void preparePanelForOpening(); + void panelDismissed(); + +private: + DENG2_PRIVATE(d) +}; + +#endif // DENG_CLIENT_RENDERERAPPEARANCEEDITOR_H diff --git a/doomsday/client/include/ui/ui_panel.h b/doomsday/client/include/ui/ui_panel.h index c8c9f3f78b..648d028902 100644 --- a/doomsday/client/include/ui/ui_panel.h +++ b/doomsday/client/include/ui/ui_panel.h @@ -24,9 +24,7 @@ #ifndef LIBDENG_CONTROL_PANEL_H #define LIBDENG_CONTROL_PANEL_H -#ifdef __cplusplus -extern "C" { -#endif +#if 0 void CP_Register(void); @@ -34,8 +32,6 @@ void CP_Register(void); void CP_CvarSlider(ui_object_t *ob); void CP_InitCvarSliders(ui_object_t *ob); -#ifdef __cplusplus -} // extern "C" #endif #endif /* LIBDENG_CONTROL_PANEL_H */ diff --git a/doomsday/client/include/ui/widgets/cvarchoicewidget.h b/doomsday/client/include/ui/widgets/cvarchoicewidget.h index 46945894ca..5aaf782e72 100644 --- a/doomsday/client/include/ui/widgets/cvarchoicewidget.h +++ b/doomsday/client/include/ui/widgets/cvarchoicewidget.h @@ -20,12 +20,13 @@ #define DENG_CLIENT_CVARCHOICEWIDGET_H #include "choicewidget.h" +#include "icvarwidget.h" /** * Console variable choice for integer-type cvars with a limited number of * valid settings. The choice items' user data is used as the cvar value. */ -class CVarChoiceWidget : public ChoiceWidget +class CVarChoiceWidget : public ChoiceWidget, public ICVarWidget { Q_OBJECT diff --git a/doomsday/client/include/ui/widgets/cvarsliderwidget.h b/doomsday/client/include/ui/widgets/cvarsliderwidget.h index 8c20c5d234..fae2e93c7e 100644 --- a/doomsday/client/include/ui/widgets/cvarsliderwidget.h +++ b/doomsday/client/include/ui/widgets/cvarsliderwidget.h @@ -20,11 +20,12 @@ #define DENG_CLIENT_CVARSLIDERWIDGET_H #include "sliderwidget.h" +#include "icvarwidget.h" /** * Console variable slider. */ -class CVarSliderWidget : public SliderWidget +class CVarSliderWidget : public SliderWidget, public ICVarWidget { Q_OBJECT diff --git a/doomsday/client/include/ui/widgets/cvartogglewidget.h b/doomsday/client/include/ui/widgets/cvartogglewidget.h index b558302c28..8726bbb99f 100644 --- a/doomsday/client/include/ui/widgets/cvartogglewidget.h +++ b/doomsday/client/include/ui/widgets/cvartogglewidget.h @@ -20,11 +20,12 @@ #define DENG_CLIENT_CVARTOGGLEWIDGET_H #include "togglewidget.h" +#include "icvarwidget.h" /** * Console variable toggle for on/off type of cvars (value 0 or 1). */ -class CVarToggleWidget : public ToggleWidget +class CVarToggleWidget : public ToggleWidget, public ICVarWidget { Q_OBJECT diff --git a/doomsday/client/include/ui/widgets/foldpanelwidget.h b/doomsday/client/include/ui/widgets/foldpanelwidget.h new file mode 100644 index 0000000000..312937692d --- /dev/null +++ b/doomsday/client/include/ui/widgets/foldpanelwidget.h @@ -0,0 +1,60 @@ +/** @file foldpanelwidget.h Folding panel. + * + * @authors Copyright (c) 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef DENG_CLIENT_FOLDPANELWIDGET_H +#define DENG_CLIENT_FOLDPANELWIDGET_H + +#include "panelwidget.h" +#include "buttonwidget.h" + +/** + * Folding panel. + * + * You should first set the container of the folding panel with setContent(). + * This ensures that widgets added to the panel use the appropriate stylist. + * + * When dismissed, the panel contents are GL-deinitialized and removed from + * the widget tree entirely. + * + * FoldPanelWidget creates a title button for toggling the panel open and + * closed. It is the user's responsibility to lay out this button + * appropriately. + */ +class FoldPanelWidget : public PanelWidget +{ + Q_OBJECT + +public: + FoldPanelWidget(de::String const &name = ""); + + ButtonWidget &title(); + + void setContent(GuiWidget *content); + +public slots: + void toggleFold(); + +protected: + void preparePanelForOpening(); + void panelDismissed(); + +private: + DENG2_PRIVATE(d) +}; + +#endif // DENG_CLIENT_FOLDPANELWIDGET_H diff --git a/doomsday/client/include/ui/widgets/icvarwidget.h b/doomsday/client/include/ui/widgets/icvarwidget.h new file mode 100644 index 0000000000..53d2d26de3 --- /dev/null +++ b/doomsday/client/include/ui/widgets/icvarwidget.h @@ -0,0 +1,33 @@ +/** @file icvarwidget.h Interface for console variable widgets. + * + * @authors Copyright (c) 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef DENG_CLIENT_ICVARWIDGET_H +#define DENG_CLIENT_ICVARWIDGET_H + +/** + * Interface for console variable widgets. + */ +class ICVarWidget +{ +public: + virtual ~ICVarWidget() {} + + virtual void updateFromCVar() = 0; +}; + +#endif // DENG_CLIENT_ICVARWIDGET_H diff --git a/doomsday/client/include/ui/widgets/panelwidget.h b/doomsday/client/include/ui/widgets/panelwidget.h index f748605814..8ebdd694f4 100644 --- a/doomsday/client/include/ui/widgets/panelwidget.h +++ b/doomsday/client/include/ui/widgets/panelwidget.h @@ -30,7 +30,8 @@ * determines the size of the panel. The user must define the position of the * panel. * - * Initially panels are in the open state. + * Initially panels are in the closed state. They can be opened once the + * content widget has been set. */ class PanelWidget : public GuiWidget { @@ -45,6 +46,20 @@ class PanelWidget : public GuiWidget public: PanelWidget(de::String const &name = ""); + /** + * Sets the size policy for the secondary dimension. For instance, for a + * panel that opens horizontally, this determines what is done to the + * widget height. + * + * - ui::Expand (the default) means that the widget automatically uses the + * content's size for the secondary dimension. + * - ui::Fixed means that the user is expected to define the panel's secondary + * dimension and the panel does not touch it. + * + * @param policy Size policy. + */ + void setSizePolicy(ui::SizePolicy policy); + /** * Sets the content widget of the panel. If there is an earlier content * widget, it will be destroyed. @@ -56,6 +71,8 @@ class PanelWidget : public GuiWidget GuiWidget &content() const; + GuiWidget *takeContent(); + /** * Sets the opening direction of the panel. * diff --git a/doomsday/client/include/ui/widgets/scrollareawidget.h b/doomsday/client/include/ui/widgets/scrollareawidget.h index 4c54b5163f..b4c080f731 100644 --- a/doomsday/client/include/ui/widgets/scrollareawidget.h +++ b/doomsday/client/include/ui/widgets/scrollareawidget.h @@ -23,6 +23,12 @@ /** * Scrollable area. + * + * ScrollAreaWidget does not control its own position or size. The user + * must define its rectangle. The content rule rectangle is defined in + * relation to the widget's rectangle. + * + * The user must always define the size of the content area. */ class ScrollAreaWidget : public GuiWidget { diff --git a/doomsday/client/include/ui/widgets/sliderwidget.h b/doomsday/client/include/ui/widgets/sliderwidget.h index fea56321f0..985237a1e5 100644 --- a/doomsday/client/include/ui/widgets/sliderwidget.h +++ b/doomsday/client/include/ui/widgets/sliderwidget.h @@ -35,12 +35,15 @@ class SliderWidget : public GuiWidget public: SliderWidget(de::String const &name = ""); - void setRange(de::Rangei const &intRange, int step = 0); + void setRange(de::Rangei const &intRange, int step = 1); void setRange(de::Rangef const &floatRange, float step = 0); void setRange(de::Ranged const &doubleRange, de::ddouble step = 0); void setPrecision(int precisionDecimals); void setValue(de::ddouble value); + void setMinLabel(de::String const &labelText); + void setMaxLabel(de::String const &labelText); + /** * Displayed values are multiplied by this factor when displayed. * Does not affect the real value of the slider. diff --git a/doomsday/client/src/clientapp.cpp b/doomsday/client/src/clientapp.cpp index 4b43518b24..da0080a3c0 100644 --- a/doomsday/client/src/clientapp.cpp +++ b/doomsday/client/src/clientapp.cpp @@ -117,6 +117,7 @@ DENG2_PIMPL(ClientApp) { QScopedPointer updater; SettingsRegister rendererSettings; + SettingsRegister rendererAppearanceSettings; SettingsRegister audioSettings; QMenuBar *menuBar; InputSystem *inputSys; @@ -200,6 +201,9 @@ DENG2_PIMPL(ClientApp) .define(SettingsRegister::IntCVar, "rend-dev-vertex-show-indices", 0) .define(SettingsRegister::IntCVar, "rend-dev-generator-show-indices", 0); +// rendererAppearanceSettings +// .define(); + audioSettings .define(SettingsRegister::IntCVar, "sound-volume", 255) .define(SettingsRegister::IntCVar, "music-volume", 255) @@ -348,6 +352,11 @@ SettingsRegister &ClientApp::rendererSettings() return app().d->rendererSettings; } +SettingsRegister &ClientApp::rendererAppearanceSettings() +{ + return app().d->rendererAppearanceSettings; +} + SettingsRegister &ClientApp::audioSettings() { return app().d->audioSettings; diff --git a/doomsday/client/src/gl/gl_texmanager.cpp b/doomsday/client/src/gl/gl_texmanager.cpp index d7db441c2f..dd9b42c64d 100644 --- a/doomsday/client/src/gl/gl_texmanager.cpp +++ b/doomsday/client/src/gl/gl_texmanager.cpp @@ -129,7 +129,7 @@ void GL_TexRegister() C_VAR_INT ("rend-tex-detail", &r_detail, 0, 0, 1); C_VAR_INT ("rend-tex-detail-multitex", &useMultiTexDetails, 0, 0, 1); C_VAR_FLOAT ("rend-tex-detail-scale", &detailScale, CVF_NO_MIN | CVF_NO_MAX, 0, 0); - C_VAR_FLOAT2("rend-tex-detail-strength", &detailFactor, 0, 0, 10, GL_DoResetDetailTextures); + C_VAR_FLOAT2("rend-tex-detail-strength", &detailFactor, 0, 0, 5, GL_DoResetDetailTextures); C_VAR_BYTE2 ("rend-tex-external-always", &loadExtAlways, 0, 0, 1, GL_DoTexReset); C_VAR_INT ("rend-tex-filter-anisotropic", &texAniso, 0, -1, 4); C_VAR_INT ("rend-tex-filter-mag", &texMagMode, 0, 0, 1); diff --git a/doomsday/client/src/ui/clientwindow.cpp b/doomsday/client/src/ui/clientwindow.cpp index 021fdad732..df3a264787 100644 --- a/doomsday/client/src/ui/clientwindow.cpp +++ b/doomsday/client/src/ui/clientwindow.cpp @@ -73,6 +73,7 @@ public IGameChangeObserver LabelWidget *background; GameSelectionWidget *games; BusyWidget *busy; + GuiWidget *sidebar; GuiRootWidget busyRoot; @@ -92,6 +93,7 @@ public IGameChangeObserver colorAdjust(0), background(0), games(0), + sidebar(0), busyRoot(thisPublic), fpsCounter(0), oldFps(0) @@ -136,7 +138,7 @@ public IGameChangeObserver legacy = new LegacyWidget(LEGACY_WIDGET_NAME); legacy->rule() .setLeftTop (root.viewLeft(), root.viewTop()) - .setRightBottom(root.viewWidth() / 2, root.viewBottom()); + .setRightBottom(root.viewWidth(), root.viewBottom()); // Initially the widget is disabled. It will be enabled when the window // is visible and ready to be drawn. legacy->disable(); @@ -156,7 +158,7 @@ public IGameChangeObserver notifications = new NotificationWidget; notifications->rule() .setInput(Rule::Top, root.viewTop() + style.rules().rule("gap") - notifications->shift()) - .setInput(Rule::Right, root.viewRight() - style.rules().rule("gap")); + .setInput(Rule::Right, legacy->rule().right() - style.rules().rule("gap")); root.add(notifications); // FPS counter for the notification area. @@ -340,6 +342,50 @@ public IGameChangeObserver oldFps = fps; } } + + void installSidebar(SidebarLocation location, GuiWidget *widget) + { + // Get rid of the old sidebar. + if(sidebar) + { + uninstallSidebar(location); + } + if(!widget) return; + + DENG2_ASSERT(sidebar == NULL); + + // Attach the widget. + switch(location) + { + case RightEdge: + widget->rule() + .setInput(Rule::Top, root.viewTop()) + .setInput(Rule::Right, root.viewRight()) + .setInput(Rule::Bottom, taskBar->rule().top()); + legacy->rule() + .setInput(Rule::Right, widget->rule().left()); + break; + } + + sidebar = widget; + root.insertBefore(sidebar, *notifications); + } + + void uninstallSidebar(SidebarLocation location) + { + DENG2_ASSERT(sidebar != NULL); + + switch(location) + { + case RightEdge: + legacy->rule().setInput(Rule::Right, root.viewRight()); + break; + } + + root.remove(*sidebar); + sidebar->deleteLater(); + sidebar = 0; + } }; ClientWindow::ClientWindow(String const &id) @@ -617,3 +663,10 @@ void ClientWindow::showColorAdjustments() { d->colorAdjust->open(); } + +void ClientWindow::setSidebar(SidebarLocation location, GuiWidget *sidebar) +{ + DENG2_ASSERT(location == RightEdge); + + d->installSidebar(location, sidebar); +} diff --git a/doomsday/client/src/ui/dialogs/renderersettingsdialog.cpp b/doomsday/client/src/ui/dialogs/renderersettingsdialog.cpp index e02759dc29..ed3e01d502 100644 --- a/doomsday/client/src/ui/dialogs/renderersettingsdialog.cpp +++ b/doomsday/client/src/ui/dialogs/renderersettingsdialog.cpp @@ -20,6 +20,8 @@ #include "ui/widgets/cvarsliderwidget.h" #include "ui/widgets/cvartogglewidget.h" #include "ui/widgets/cvarchoicewidget.h" +#include "ui/widgets/taskbarwidget.h" +#include "ui/editors/rendererappearanceeditor.h" #include "GridLayout" #include "SignalAction" #include "DialogContentStylist" @@ -35,15 +37,17 @@ DENG_GUI_PIMPL(RendererSettingsDialog) ButtonWidget *appearButton; CVarSliderWidget *fov; CVarToggleWidget *mirrorWeapon; + CVarToggleWidget *precacheModels; + CVarToggleWidget *precacheSprites; CVarToggleWidget *multiLight; CVarToggleWidget *multiShiny; CVarToggleWidget *multiDetail; - CVarChoiceWidget *rendTex; - CVarChoiceWidget *wireframe; // Developer settings. PopupWidget *devPopup; QScopedPointer stylist; + CVarChoiceWidget *rendTex; + CVarChoiceWidget *wireframe; CVarToggleWidget *bboxMobj; CVarToggleWidget *bboxPoly; CVarToggleWidget *thinkerIds; @@ -62,10 +66,12 @@ DENG_GUI_PIMPL(RendererSettingsDialog) fov->setPrecision(0); fov->setRange(Ranged(30, 160)); - area.add(mirrorWeapon = new CVarToggleWidget("rend-model-mirror-hud")); - area.add(multiLight = new CVarToggleWidget("rend-light-multitex")); - area.add(multiShiny = new CVarToggleWidget("rend-model-shiny-multitex")); - area.add(multiDetail = new CVarToggleWidget("rend-tex-detail-multitex")); + area.add(mirrorWeapon = new CVarToggleWidget("rend-model-mirror-hud")); + area.add(precacheModels = new CVarToggleWidget("rend-model-precache")); + area.add(precacheSprites = new CVarToggleWidget("rend-sprite-precache")); + area.add(multiLight = new CVarToggleWidget("rend-light-multitex")); + area.add(multiShiny = new CVarToggleWidget("rend-model-shiny-multitex")); + area.add(multiDetail = new CVarToggleWidget("rend-tex-detail-multitex")); // Set up a separate popup for developer settings. self.add(devPopup = new PopupWidget); @@ -116,15 +122,7 @@ DENG_GUI_PIMPL(RendererSettingsDialog) foreach(Widget *child, self.area().childWidgets() + devPopup->content().childWidgets()) { - if(CVarToggleWidget *w = child->maybeAs()) - { - w->updateFromCVar(); - } - if(CVarChoiceWidget *w = child->maybeAs()) - { - w->updateFromCVar(); - } - if(CVarSliderWidget *w = child->maybeAs()) + if(ICVarWidget *w = child->maybeAs()) { w->updateFromCVar(); } @@ -151,7 +149,11 @@ RendererSettingsDialog::RendererSettingsDialog(String const &name) LabelWidget *fovLabel = LabelWidget::newWithText(tr("Field of View:"), &area()); - d->mirrorWeapon->setText(tr("Mirror Player Weapon Model")); + d->mirrorWeapon->setText(tr("Mirror Player Weapon Model")); + + LabelWidget *precacheLabel = LabelWidget::newWithText(tr("Precaching:"), &area()); + d->precacheModels->setText(tr("3D Models")); + d->precacheSprites->setText(tr("Sprites " _E(l) "(slow)")); LabelWidget *multiLabel = LabelWidget::newWithText(tr("Multitexturing:"), &area()); d->multiLight->setText(tr("Dynamic Lights")); @@ -180,12 +182,14 @@ RendererSettingsDialog::RendererSettingsDialog(String const &name) GridLayout layout(area().contentRule().left(), area().contentRule().top()); layout.setGridSize(2, 0); layout.setColumnAlignment(0, ui::AlignRight); - layout << *appearLabel << *d->appear - << *fovLabel << *d->fov - << Const(0) << *d->mirrorWeapon - << *multiLabel << *d->multiLight - << Const(0) << *d->multiShiny - << Const(0) << *d->multiDetail; + layout << *appearLabel << *d->appear + << *fovLabel << *d->fov + << Const(0) << *d->mirrorWeapon + << *precacheLabel << *d->precacheModels + << Const(0) << *d->precacheSprites + << *multiLabel << *d->multiLight + << Const(0) << *d->multiShiny + << Const(0) << *d->multiDetail; area().setContentSize(layout.width(), layout.height()); // Attach the appearance button next to the choice. @@ -221,7 +225,7 @@ void RendererSettingsDialog::showAppearanceMenu() PopupMenuWidget *popup = new PopupMenuWidget; popup->set(popup->background().withSolidFillOpacity(1)); popup->menu().items() - << new ActionItem(tr("Edit")) + << new ActionItem(tr("Edit"), new SignalAction(this, SLOT(showEditor()))) << new ActionItem(tr("Rename...")) << new Item(Item::Separator) << new ActionItem(tr("Add Duplicate...")) @@ -235,7 +239,7 @@ void RendererSettingsDialog::showAppearanceMenu() if(d->appear->selectedItem().data().toString().isEmpty()) { // Default profile. - org.itemWidget(0)->disable(); + //org.itemWidget(0)->disable(); org.itemWidget(1)->disable(); org.itemWidget(5)->disable(); } @@ -269,3 +273,11 @@ void RendererSettingsDialog::deleteProfile() { } + +void RendererSettingsDialog::showEditor() +{ + RendererAppearanceEditor *editor = new RendererAppearanceEditor; + editor->open(); + + ClientWindow::main().taskBar().closeConfigMenu(); +} diff --git a/doomsday/client/src/ui/editors/rendererappearanceeditor.cpp b/doomsday/client/src/ui/editors/rendererappearanceeditor.cpp new file mode 100644 index 0000000000..857ea235f1 --- /dev/null +++ b/doomsday/client/src/ui/editors/rendererappearanceeditor.cpp @@ -0,0 +1,491 @@ +/** @file rendererappearanceeditor.cpp + * + * @authors Copyright (c) 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "ui/editors/rendererappearanceeditor.h" +#include "ui/dialogs/renderersettingsdialog.h" +#include "ui/widgets/buttonwidget.h" +#include "ui/widgets/scrollareawidget.h" +#include "ui/widgets/cvarchoicewidget.h" +#include "ui/widgets/cvarsliderwidget.h" +#include "ui/widgets/cvartogglewidget.h" +#include "ui/widgets/foldpanelwidget.h" +#include "ui/clientwindow.h" +#include "DialogContentStylist" +#include "SequentialLayout" +#include "SignalAction" +#include "clientapp.h" + +using namespace de; +using namespace ui; + +class Group : public FoldPanelWidget +{ +public: + Group(GuiWidget *parent, String const &titleText) + { + _group = new GuiWidget; + setContent(_group); + title().setText(titleText); + title().setTextColor("accent"); + + _layout.setGridSize(2, 0); + _layout.setColumnAlignment(0, AlignRight); + _layout.setLeftTop(_group->rule().left(), _group->rule().top()); + + parent->add(&title()); + parent->add(this); + } + + void addSpace() + { + _layout << Const(0); + } + + void addLabel(String const &text) + { + _layout << *LabelWidget::newWithText(text, _group); + } + + CVarToggleWidget *addToggle(char const *cvar, String const &label) + { + CVarToggleWidget *w = new CVarToggleWidget(cvar); + w->setText(label); + _group->add(w); + _layout << *w; + return w; + } + + CVarChoiceWidget *addChoice(char const *cvar) + { + CVarChoiceWidget *w = new CVarChoiceWidget(cvar); + w->setOpeningDirection(ui::Up); + _group->add(w); + _layout << *w; + return w; + } + + CVarSliderWidget *addSlider(char const *cvar) + { + CVarSliderWidget *w = new CVarSliderWidget(cvar); + _group->add(w); + _layout << *w; + return w; + } + + CVarSliderWidget *addSlider(char const *cvar, Ranged const &range, double step, int precision) + { + CVarSliderWidget *w = addSlider(cvar); + w->setRange(range, step); + w->setPrecision(precision); + return w; + } + + void fetch() + { + foreach(Widget *child, _group->childWidgets()) + { + if(ICVarWidget *w = child->maybeAs()) + { + w->updateFromCVar(); + } + } + } + + void commit() + { + _group->rule().setSize(_layout.width(), _layout.height()); + } + +private: + GuiWidget *_group; + GridLayout _layout; +}; + +DENG_GUI_PIMPL(RendererAppearanceEditor) +{ + SettingsRegister &settings; + DialogContentStylist stylist; + ScrollAreaWidget *container; + ButtonWidget *conf; + ButtonWidget *close; + + Group *skyGroup; + Group *shadowGroup; + Group *lightGroup; + Group *glowGroup; + Group *haloGroup; + Group *texGroup; + Group *modelGroup; + Group *spriteGroup; + Group *objectGroup; + Group *partGroup; + + Instance(Public *i) + : Base(i), + settings(ClientApp::rendererAppearanceSettings()) + { + // The contents of the editor will scroll. + container = new ScrollAreaWidget; + stylist.setContainer(*container); + + container->add(conf = new ButtonWidget); + container->add(close = new ButtonWidget); + + // Button for showing renderer settings. + conf->setImage(style().images().image("gear")); + conf->setOverrideImageSize(style().fonts().font("default").height().value()); + conf->setAction(new SignalAction(thisPublic, SLOT(showRendererSettings()))); + + close->setText(tr("Close")); + close->setAction(new SignalAction(thisPublic, SLOT(close()))); + + // Sky settings. + skyGroup = new Group(container, tr("Sky")); + + skyGroup->addLabel(tr("Sky Sphere Radius:")); + skyGroup->addSlider("rend-sky-distance", Ranged(0, 8000), 10, 0); + + skyGroup->commit(); + + // Shadow settings. + shadowGroup = new Group(container, tr("Shadows")); + + shadowGroup->addSpace(); + shadowGroup->addToggle("rend-fakeradio", tr("Ambient Occlusion")); + + shadowGroup->addLabel(tr("Occlusion Darkness:")); + shadowGroup->addSlider("rend-fakeradio-darkness"); + + shadowGroup->addSpace(); + shadowGroup->addToggle("rend-shadow", tr("Objects Cast Shadows")); + + shadowGroup->addLabel(tr("Shadow Darkness:")); + shadowGroup->addSlider("rend-shadow-darkness"); + + shadowGroup->addLabel(tr("Max Visible Distance:")); + shadowGroup->addSlider("rend-shadow-far", Ranged(0, 3000), 10, 0); + + shadowGroup->addLabel(tr("Maximum Radius:")); + shadowGroup->addSlider("rend-shadow-radius-max", Ranged(1, 128), 1, 0); + + shadowGroup->commit(); + + // Dynamic light settings. + lightGroup = new Group(container, "Dynamic Lights"); + + lightGroup->addLabel(tr("Dynamic Lights:")); + lightGroup->addChoice("rend-light")->items() + << new ChoiceItem(tr("Enabled"), 1) + << new ChoiceItem(tr("Disabled"), 0) + << new ChoiceItem(tr("Process without drawing"), 2); + + lightGroup->addSpace(); + lightGroup->addToggle("rend-light-decor", tr("Light Decorations")); + + lightGroup->addLabel(tr("Blending Mode:")); + lightGroup->addChoice("rend-light-blend")->items() + << new ChoiceItem(tr("Multiply"), 0) + << new ChoiceItem(tr("Add"), 1) + << new ChoiceItem(tr("Process without drawing"), 2); + + lightGroup->addLabel(tr("Number of Lights:")); + lightGroup->addSlider("rend-light-num", Ranged(0, 2000), 1, 0)->setMinLabel("Max"); + + lightGroup->addLabel(tr("Light Brightness:")); + lightGroup->addSlider("rend-light-bright"); + + lightGroup->addLabel(tr("Light Radius Factor:")); + lightGroup->addSlider("rend-light-radius-scale"); + + lightGroup->addLabel(tr("Light Max Radius:")); + lightGroup->addSlider("rend-light-radius-max"); + + lightGroup->addLabel(tr("Ambient Light:")); + lightGroup->addSlider("rend-light-ambient"); + + lightGroup->addLabel(tr("Light Compression:")); + lightGroup->addSlider("rend-light-compression"); + + lightGroup->commit(); + + // Glow settings. + glowGroup = new Group(container, tr("Surface Glow")); + + glowGroup->addLabel(tr("Material Glow:")); + glowGroup->addSlider("rend-glow"); + + glowGroup->addLabel(tr("Max Glow Height:")); + glowGroup->addSlider("rend-glow-height"); + + glowGroup->addLabel(tr("Glow Height Factor:")); + glowGroup->addSlider("rend-glow-scale"); + + glowGroup->addLabel(tr("Brightness in Fog:")); + glowGroup->addSlider("rend-light-fog-bright"); + + glowGroup->addSpace(); + glowGroup->addToggle("rend-glow-wall", tr("Glow Visible on Walls")); + + glowGroup->commit(); + + // Halo and lens flare settings. + haloGroup = new Group(container, tr("Lens Flares & Halos")); + + haloGroup->addSpace(); + haloGroup->addToggle("rend-halo-realistic", tr("Realistic Halos")); + + haloGroup->addLabel(tr("Flares per Halo:")); + haloGroup->addSlider("rend-halo")->setMinLabel(tr("None")); + + haloGroup->addLabel(tr("Halo Brightness:")); + haloGroup->addSlider("rend-halo-bright", Ranged(0, 100), 1, 0); + + haloGroup->addLabel(tr("Halo Size Factor:")); + haloGroup->addSlider("rend-halo-size", Ranged(0, 100), 1, 0); + + haloGroup->addLabel(tr("Occlusion Fading:")); + haloGroup->addSlider("rend-halo-occlusion", Ranged(1, 256), 1, 0); + + haloGroup->addLabel(tr("Min Halo Radius:")); + haloGroup->addSlider("rend-halo-radius-min", Ranged(1, 80), .1, 1); + + haloGroup->addLabel(tr("Min Halo Size:")); + haloGroup->addSlider("rend-halo-secondary-limit", Ranged(0, 10), .1, 1); + + haloGroup->addLabel(tr("Halo Fading Start:")); + haloGroup->addSlider("rend-halo-dim-near", Ranged(0, 200), .1, 1); + + haloGroup->addLabel(tr("Halo Fading End:")); + haloGroup->addSlider("rend-halo-dim-far", Ranged(0, 200), .1, 1); + + haloGroup->addLabel(tr("Z-Mag Divisor:")); + haloGroup->addSlider("rend-halo-zmag-div", Ranged(1, 200), .1, 1); + + haloGroup->commit(); + + // Texture settings. + texGroup = new Group(container, tr("Textures")); + + texGroup->addLabel(tr("Filtering Mode:")); + texGroup->addChoice("rend-tex-mipmap")->items() + << new ChoiceItem(tr("None"), 0) + << new ChoiceItem(tr("Linear filter, no mip"), 1) + << new ChoiceItem(tr("No filter, nearest mip"), 2) + << new ChoiceItem(tr("Linear filter, nearest mip"), 3) + << new ChoiceItem(tr("No filter, linear mip"), 4) + << new ChoiceItem(tr("Linear filter, linear mip"), 5); + + texGroup->addLabel(tr("Texture Quality:")); + texGroup->addSlider("rend-tex-quality"); + + texGroup->addSpace(); + texGroup->addToggle("rend-tex-anim-smooth", tr("Smooth Blend Animation")); + + texGroup->addSpace(); + texGroup->addToggle("rend-tex-filter-smart", tr("2x Smart Filtering")); + + texGroup->addLabel(tr("Bilinear Filtering:")); + texGroup->addToggle("rend-tex-filter-sprite", tr("Sprites")); + + texGroup->addSpace(); + texGroup->addToggle("rend-tex-filter-mag", tr("World Surfaces")); + + texGroup->addSpace(); + texGroup->addToggle("rend-tex-filter-ui", tr("User Interface")); + + texGroup->addLabel(tr("Anisotopic Filter:")); + texGroup->addChoice("rend-tex-filter-anisotropic")->items() + << new ChoiceItem(tr("Best available"), -1) + << new ChoiceItem(tr("Off"), 0) + << new ChoiceItem(tr("2x"), 1) + << new ChoiceItem(tr("4x"), 2) + << new ChoiceItem(tr("8x"), 3) + << new ChoiceItem(tr("16x"), 4); + + texGroup->addSpace(); + texGroup->addToggle("rend-tex-detail", tr("Detail Textures")); + + texGroup->addLabel(tr("Scaling Factor:")); + texGroup->addSlider("rend-tex-detail-scale", Ranged(0, 16), .01, 2); + + texGroup->addLabel(tr("Contrast:")); + texGroup->addSlider("rend-tex-detail-strength"); + + texGroup->commit(); + + // Model settings. + modelGroup = new Group(container, tr("3D Models")); + + modelGroup->addSpace(); + modelGroup->addToggle("rend-model", tr("3D Models")); + + modelGroup->addSpace(); + modelGroup->addToggle("rend-model-inter", tr("Interpolate Frames")); + + modelGroup->addLabel(tr("Max Visible Distance:")); + modelGroup->addSlider("rend-model-distance", Ranged(0, 3000), 10, 0)->setMinLabel(tr("Inf")); + + modelGroup->addLabel(tr("LOD #0 Distance:")); + modelGroup->addSlider("rend-model-lod", Ranged(0, 1000), 10, 0)->setMinLabel(tr("No LOD")); + + modelGroup->addLabel(tr("Number of Lights:")); + modelGroup->addSlider("rend-model-lights"); + + modelGroup->commit(); + + // Sprite settings. + spriteGroup = new Group(container, tr("Sprites")); + + spriteGroup->addSpace(); + spriteGroup->addToggle("rend-sprite-blend", tr("Additive Blending")); + + spriteGroup->addLabel(tr("Number of Lights:")); + spriteGroup->addSlider("rend-sprite-lights")->setMinLabel(tr("Inf")); + + spriteGroup->addLabel(tr("Sprite Alignment:")); + spriteGroup->addChoice("rend-sprite-align")->items() + << new ChoiceItem(tr("Camera"), 0) + << new ChoiceItem(tr("View plane"), 1) + << new ChoiceItem(tr("Camera (limited)"), 2) + << new ChoiceItem(tr("View plane (limited)"), 3); + + spriteGroup->addSpace(); + spriteGroup->addToggle("rend-sprite-noz", tr("Disable Z-Write")); + + spriteGroup->commit(); + + // Object settings. + objectGroup = new Group(container, tr("Objects")); + + objectGroup->addLabel(tr("Smooth Movement:")); + objectGroup->addChoice("rend-mobj-smooth-move")->items() + << new ChoiceItem(tr("Disabled"), 0) + << new ChoiceItem(tr("Models only"), 1) + << new ChoiceItem(tr("Models and sprites"), 2); + + objectGroup->addSpace(); + objectGroup->addToggle("rend-mobj-smooth-turn", tr("Smooth Turning")); + + objectGroup->commit(); + + // Particle settings. + partGroup = new Group(container, tr("Particle Effects")); + + partGroup->addSpace(); + partGroup->addToggle("rend-particle", tr("Particle Effects")); + + partGroup->addLabel(tr("Max Particles:")); + partGroup->addSlider("rend-particle-max", Ranged(0, 10000), 100, 0)->setMinLabel(tr("Inf")); + + partGroup->addLabel(tr("Spawn Rate:")); + partGroup->addSlider("rend-particle-rate"); + + partGroup->addLabel(tr("Diffusion:")); + partGroup->addSlider("rend-particle-diffuse", Ranged(0, 20), .01, 2); + + partGroup->addLabel(tr("Near Clip Distance:")); + partGroup->addSlider("rend-particle-visible-near", Ranged(0, 1000), 1, 0)->setMinLabel(tr("None")); + + partGroup->commit(); + } + + void fetch() + { + foreach(Widget *child, container->childWidgets()) + { + if(Group *g = child->maybeAs()) + { + g->fetch(); + } + } + } +}; + +RendererAppearanceEditor::RendererAppearanceEditor() + : PanelWidget("rendererappearanceeditor"), d(new Instance(this)) +{ + setSizePolicy(Fixed); + setOpeningDirection(Left); + set(Background(style().colors().colorf("background")).withSolidFillOpacity(1)); + + // Set up the editor UI. + LabelWidget *title = LabelWidget::newWithText("Renderer Appearance", d->container); + title->setFont("title"); + title->setTextColor("accent"); + + // Layout. + RuleRectangle const &area = d->container->contentRule(); + title->rule() + .setInput(Rule::Top, area.top()) + .setInput(Rule::Left, area.left()); + d->close->rule() + .setInput(Rule::Right, area.right()) + .setInput(Rule::Top, area.top()); + d->conf->rule() + .setInput(Rule::Right, d->close->rule().left()) + .setInput(Rule::Top, area.top()); + + SequentialLayout layout(area.left(), title->rule().bottom(), Down); + + layout + << d->lightGroup->title() << *d->lightGroup + << d->haloGroup->title() << *d->haloGroup + << d->glowGroup->title() << *d->glowGroup + << d->shadowGroup->title() << *d->shadowGroup + << d->texGroup->title() << *d->texGroup + << d->objectGroup->title() << *d->objectGroup + << d->modelGroup->title() << *d->modelGroup + << d->spriteGroup->title() << *d->spriteGroup + << d->partGroup->title() << *d->partGroup + << d->skyGroup->title() << *d->skyGroup; + + // Update container size. + d->container->setContentSize(OperatorRule::maximum(layout.width(), + style().rules().rule("sidebar.width")), + title->rule().height() + layout.height()); + d->container->rule().setSize(d->container->contentRule().width() + + d->container->margins().width(), + rule().height()); + setContent(d->container); + + d->fetch(); + + // Install the editor. + ClientWindow::main().setSidebar(ClientWindow::RightEdge, this); +} + +void RendererAppearanceEditor::showRendererSettings() +{ + RendererSettingsDialog *dlg = new RendererSettingsDialog; + dlg->setDeleteAfterDismissed(true); + dlg->setAnchorAndOpeningDirection(d->conf->rule(), Down); + root().add(dlg); + dlg->open(); +} + +void RendererAppearanceEditor::preparePanelForOpening() +{ + PanelWidget::preparePanelForOpening(); +} + +void RendererAppearanceEditor::panelDismissed() +{ + PanelWidget::panelDismissed(); + + ClientWindow::main().unsetSidebar(ClientWindow::RightEdge); +} diff --git a/doomsday/client/src/ui/ui_main.cpp b/doomsday/client/src/ui/ui_main.cpp index 5f4a65ca7c..901bba47b9 100644 --- a/doomsday/client/src/ui/ui_main.cpp +++ b/doomsday/client/src/ui/ui_main.cpp @@ -98,7 +98,6 @@ void UI_Register(void) // Ccmds C_CMD_FLAGS("uicolor", "sfff", UIColor, CMDF_NO_DEDICATED); - CP_Register(); Fonts_Register(); } diff --git a/doomsday/client/src/ui/ui_panel.cpp b/doomsday/client/src/ui/ui_panel.cpp index 51a8d26db4..1af193eba0 100644 --- a/doomsday/client/src/ui/ui_panel.cpp +++ b/doomsday/client/src/ui/ui_panel.cpp @@ -17,6 +17,8 @@ * http://www.gnu.org/licenses */ +#if 0 + /** * Control Panel. * @@ -445,6 +447,7 @@ ui_object_t ob_panel[] = { UI_BUTTON2, 0, 0, 680, 250, 70, 55, "input-joy", UIButton_Drawer, UIButton_Responder, 0, CP_CvarButton }, #endif +#if 0 { UI_META, 2 }, { UI_TEXT, 0, 0, 280, 0, 0, 50, "Graphics Options", UIText_BrightDrawer }, { UI_META, 2, 0, 0, -60 }, @@ -558,6 +561,7 @@ ui_object_t ob_panel[] = { UI_SLIDER, 0, UIF_FADE_AWAY, 680, 550, 300, 55, "", UISlider_Drawer, UISlider_Responder, UISlider_Ticker, CP_CvarSlider, &sld_detail_scale }, { UI_TEXT, 0, UIF_FADE_AWAY, 300, 610, 0, 55, "Detail texture contrast", UIText_Drawer }, { UI_SLIDER, 0, UIF_FADE_AWAY, 680, 610, 300, 55, "", UISlider_Drawer, UISlider_Responder, UISlider_Ticker, CP_CvarSlider, &sld_detail_strength }, +#endif { UI_META, 6 }, { UI_TEXT, 0, 0, 280, 0, 0, 50, "Graphics Options: Objects", UIText_BrightDrawer }, @@ -1354,3 +1358,4 @@ D_CMD(OpenPanel) UI_Focus(foc); return true; } +#endif diff --git a/doomsday/client/src/ui/widgets/cvarsliderwidget.cpp b/doomsday/client/src/ui/widgets/cvarsliderwidget.cpp index 930935c797..e879770b54 100644 --- a/doomsday/client/src/ui/widgets/cvarsliderwidget.cpp +++ b/doomsday/client/src/ui/widgets/cvarsliderwidget.cpp @@ -47,6 +47,13 @@ CVarSliderWidget::CVarSliderWidget(char const *cvarPath) : d(new Instance) } setPrecision(2); } + else + { + if(!(d->var()->flags & (CVF_NO_MIN | CVF_NO_MAX))) + { + setRange(Rangei(d->var()->min, d->var()->max)); + } + } updateFromCVar(); @@ -62,7 +69,6 @@ void CVarSliderWidget::updateFromCVar() } else { - setRange(Rangei(var->min, var->max)); setValue(CVar_Integer(var)); } } diff --git a/doomsday/client/src/ui/widgets/foldpanelwidget.cpp b/doomsday/client/src/ui/widgets/foldpanelwidget.cpp new file mode 100644 index 0000000000..03fe922966 --- /dev/null +++ b/doomsday/client/src/ui/widgets/foldpanelwidget.cpp @@ -0,0 +1,99 @@ +/** @file foldpanelwidget.cpp Folding panel. + * + * @authors Copyright (c) 2013 Jaakko Keränen + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "ui/widgets/foldpanelwidget.h" +#include "DialogContentStylist" +#include "SignalAction" + +using namespace de; +using namespace ui; + +DENG2_PIMPL_NOREF(FoldPanelWidget) +{ + ButtonWidget *title; + GuiWidget *container; ///< Held here while not part of the widget tree. + DialogContentStylist stylist; + + Instance() : title(0), container(0) {} +}; + +FoldPanelWidget::FoldPanelWidget(String const &name) : PanelWidget(name), d(new Instance) +{ + d->title = new ButtonWidget; + d->title->setSizePolicy(Expand, Expand); + d->title->set(Background()); + d->title->setFont("heading"); + d->title->setAction(new SignalAction(this, SLOT(toggleFold()))); +} + +ButtonWidget &FoldPanelWidget::title() +{ + return *d->title; +} + +void FoldPanelWidget::setContent(GuiWidget *content) +{ + d->stylist.setContainer(*content); + + if(!isOpen()) + { + // We'll just take it and do nothing else yet. + if(d->container) + { + d->container->deleteLater(); + } + d->container = content; + return; + } + + PanelWidget::setContent(content); +} + +void FoldPanelWidget::toggleFold() +{ + if(!isOpen()) + { + open(); + } + else + { + close(); + } +} + +void FoldPanelWidget::preparePanelForOpening() +{ + if(d->container) + { + // Insert the content back into the panel. + PanelWidget::setContent(d->container); + d->container = 0; + } + + PanelWidget::preparePanelForOpening(); +} + +void FoldPanelWidget::panelDismissed() +{ + PanelWidget::panelDismissed(); + + content().notifySelfAndTree(&Widget::deinitialize); + + DENG2_ASSERT(d->container == 0); + d->container = takeContent(); +} diff --git a/doomsday/client/src/ui/widgets/labelwidget.cpp b/doomsday/client/src/ui/widgets/labelwidget.cpp index a47d78447e..811f2b8fdc 100644 --- a/doomsday/client/src/ui/widgets/labelwidget.cpp +++ b/doomsday/client/src/ui/widgets/labelwidget.cpp @@ -158,11 +158,13 @@ public Font::RichFormat::IStyle void glDeinit() { + drawable.clear(); composer.release(); if(!image.isNull()) { image->glDeinit(); } + wrapWidth = 0; } bool hasImage() const diff --git a/doomsday/client/src/ui/widgets/panelwidget.cpp b/doomsday/client/src/ui/widgets/panelwidget.cpp index 7a59809da2..e658d593fc 100644 --- a/doomsday/client/src/ui/widgets/panelwidget.cpp +++ b/doomsday/client/src/ui/widgets/panelwidget.cpp @@ -38,6 +38,7 @@ DENG_GUI_PIMPL(PanelWidget) bool opened; ui::Direction dir; + ui::SizePolicy secondaryPolicy; GuiWidget *content; ScalarRule *openingRule; QTimer dismissTimer; @@ -51,6 +52,7 @@ DENG_GUI_PIMPL(PanelWidget) : Base(i), opened(false), dir(ui::Down), + secondaryPolicy(ui::Expand), content(0), uMvpMatrix("uMvpMatrix", GLUniform::Mat4) { @@ -89,13 +91,19 @@ DENG_GUI_PIMPL(PanelWidget) // Widget's size depends on the opening animation. if(isVerticalAnimation()) { - self.rule().setInput(Rule::Width, content->rule().width()) - .setInput(Rule::Height, *openingRule); + self.rule().setInput(Rule::Height, *openingRule); + if(secondaryPolicy == ui::Expand) + { + self.rule().setInput(Rule::Width, content->rule().width()); + } } else { - self.rule().setInput(Rule::Width, *openingRule) - .setInput(Rule::Height, content->rule().height()); + self.rule().setInput(Rule::Width, *openingRule); + if(secondaryPolicy == ui::Expand) + { + self.rule().setInput(Rule::Height, content->rule().height()); + } } } @@ -112,6 +120,19 @@ DENG_GUI_PIMPL(PanelWidget) } } + void startOpeningAnimation(TimeDelta span) + { + if(isVerticalAnimation()) + { + openingRule->set(content->rule().height(), span); + } + else + { + openingRule->set(content->rule().width(), span); + } + openingRule->setStyle(Animation::Bounce, 8); + } + void close(TimeDelta delay) { if(!opened) return; @@ -141,19 +162,14 @@ DENG_GUI_PIMPL(PanelWidget) PanelWidget::PanelWidget(String const &name) : GuiWidget(name), d(new Instance(this)) { setBehavior(ChildHitClipping); + hide(); } void PanelWidget::setContent(GuiWidget *content) { if(d->content) - { - d->content->rule().clearInput(Rule::Left); - d->content->rule().clearInput(Rule::Top); - - rule().clearInput(Rule::Width); - rule().clearInput(Rule::Height); - - delete remove(*d->content); + { + destroy(takeContent()); } d->content = content; @@ -171,11 +187,34 @@ GuiWidget &PanelWidget::content() const return *d->content; } +GuiWidget *PanelWidget::takeContent() +{ + GuiWidget *w = d->content; + d->content = 0; + + w->rule().clearInput(Rule::Left); + w->rule().clearInput(Rule::Top); + + if(d->secondaryPolicy == ui::Expand) + { + rule().clearInput(Rule::Width); + rule().clearInput(Rule::Height); + } + + remove(*w); + return w; +} + void PanelWidget::setOpeningDirection(ui::Direction dir) { d->dir = dir; } +void PanelWidget::setSizePolicy(ui::SizePolicy policy) +{ + d->secondaryPolicy = policy; +} + ui::Direction PanelWidget::openingDirection() const { return d->dir; @@ -227,15 +266,7 @@ void PanelWidget::open() preparePanelForOpening(); // Start the opening animation. - if(d->isVerticalAnimation()) - { - d->openingRule->set(d->content->rule().height(), OPENING_ANIM_SPAN); - } - else - { - d->openingRule->set(d->content->rule().width(), OPENING_ANIM_SPAN); - } - d->openingRule->setStyle(Animation::Bounce, 8); + d->startOpeningAnimation(OPENING_ANIM_SPAN); d->opened = true; diff --git a/doomsday/client/src/ui/widgets/popupwidget.cpp b/doomsday/client/src/ui/widgets/popupwidget.cpp index 5a0282af06..26c7890b4b 100644 --- a/doomsday/client/src/ui/widgets/popupwidget.cpp +++ b/doomsday/client/src/ui/widgets/popupwidget.cpp @@ -116,9 +116,6 @@ PopupWidget::PopupWidget(String const &name) : PanelWidget(name), d(new Instance { setOpeningDirection(ui::Up); - // Initially the popup is hidden. - hide(); - /// @todo Move these to an updateStyle. Style const &st = style(); set(Background(st.colors().colorf("background"), @@ -274,9 +271,13 @@ void PopupWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) } else if(dir == ui::Left) { - v.pos = anchorPos; tri << v; - v.pos = anchorPos + Vector2i(-marker, marker); tri << v; - v.pos = anchorPos + Vector2i(-marker, -marker); tri << v; + // The anchor may still get clamped out of sight. + if(anchorPos.x > rule().right().valuei()) + { + v.pos = anchorPos; tri << v; + v.pos = anchorPos + Vector2i(-marker, marker); tri << v; + v.pos = anchorPos + Vector2i(-marker, -marker); tri << v; + } } else if(dir == ui::Right) { diff --git a/doomsday/client/src/ui/widgets/sliderwidget.cpp b/doomsday/client/src/ui/widgets/sliderwidget.cpp index a36ad64d0a..6320557cd0 100644 --- a/doomsday/client/src/ui/widgets/sliderwidget.cpp +++ b/doomsday/client/src/ui/widgets/sliderwidget.cpp @@ -74,6 +74,8 @@ DENG_GUI_PIMPL(SliderWidget) ddouble step; int precision; ddouble displayFactor; + String minLabel; + String maxLabel; enum State { Inert, @@ -146,6 +148,7 @@ DENG_GUI_PIMPL(SliderWidget) } updateValueLabel(); + updateRangeLabels(); } void glDeinit() @@ -343,7 +346,18 @@ DENG_GUI_PIMPL(SliderWidget) void updateValueLabel() { - labels[Value].setText(QString::number(value * displayFactor, 'f', precision)); + if(!minLabel.isEmpty() && fequal(value, range.start)) + { + labels[Value].setText(minLabel); + } + else if(!maxLabel.isEmpty() && fequal(value, range.end)) + { + labels[Value].setText(maxLabel); + } + else + { + labels[Value].setText(QString::number(value * displayFactor, 'f', precision)); + } } void setValue(ddouble v) @@ -372,8 +386,8 @@ DENG_GUI_PIMPL(SliderWidget) void updateRangeLabels() { - labels[Start].setText(QString::number(range.start * displayFactor)); - labels[End].setText(QString::number(range.end * displayFactor)); + labels[Start].setText(minLabel.isEmpty()? QString::number(range.start * displayFactor) : minLabel); + labels[End].setText(maxLabel.isEmpty()? QString::number(range.end * displayFactor) : maxLabel); } void startGrab(MouseEvent const &ev) @@ -487,6 +501,22 @@ void SliderWidget::setValue(ddouble value) d->setValue(value); } +void SliderWidget::setMinLabel(const String &labelText) +{ + d->minLabel = labelText; + + d->updateRangeLabels(); + d->updateValueLabel(); +} + +void SliderWidget::setMaxLabel(const String &labelText) +{ + d->maxLabel = labelText; + + d->updateRangeLabels(); + d->updateValueLabel(); +} + void SliderWidget::setDisplayFactor(ddouble factor) { d->displayFactor = factor; diff --git a/doomsday/doc/engine/command/panel.ame b/doomsday/doc/engine/command/panel.ame deleted file mode 100644 index acc37762a3..0000000000 --- a/doomsday/doc/engine/command/panel.ame +++ /dev/null @@ -1,3 +0,0 @@ -@summary{ - Open the Doomsday Control Panel. -} diff --git a/doomsday/libdeng2/include/de/widgets/widget.h b/doomsday/libdeng2/include/de/widgets/widget.h index 73c9766db3..de65bb8107 100644 --- a/doomsday/libdeng2/include/de/widgets/widget.h +++ b/doomsday/libdeng2/include/de/widgets/widget.h @@ -230,6 +230,7 @@ class DENG2_PUBLIC Widget }; NotifyArgs::Result notifyTree(NotifyArgs const &args); + NotifyArgs::Result notifySelfAndTree(NotifyArgs const &args); void notifyTreeReversed(NotifyArgs const &args); bool dispatchEvent(Event const &event, bool (Widget::*memberFunc)(Event const &)); diff --git a/doomsday/libdeng2/src/widgets/widget.cpp b/doomsday/libdeng2/src/widgets/widget.cpp index 465d8fd532..74a2abb72b 100644 --- a/doomsday/libdeng2/src/widgets/widget.cpp +++ b/doomsday/libdeng2/src/widgets/widget.cpp @@ -400,6 +400,12 @@ Widget::NotifyArgs::Result Widget::notifyTree(NotifyArgs const &args) return NotifyArgs::Continue; } +Widget::NotifyArgs::Result Widget::notifySelfAndTree(NotifyArgs const &args) +{ + (this->*args.notifyFunc)(); + return notifyTree(args); +} + void Widget::notifyTreeReversed(NotifyArgs const &args) { if(args.preNotifyFunc)