diff --git a/doomsday/client/include/ui/widgets/consolecommandwidget.h b/doomsday/client/include/ui/widgets/consolecommandwidget.h index 1502c926cb..da9201ed4f 100644 --- a/doomsday/client/include/ui/widgets/consolecommandwidget.h +++ b/doomsday/client/include/ui/widgets/consolecommandwidget.h @@ -28,14 +28,20 @@ * Whenever the current game changes, the lexicon is automatically updated to * include the terms of the loaded game. */ -class ConsoleCommandWidget : public LineEditWidget +class ConsoleCommandWidget : public QObject, public LineEditWidget { + Q_OBJECT + public: ConsoleCommandWidget(de::String const &name = ""); // Events. + void focusGained(); bool handleEvent(de::Event const &event); +signals: + void gotFocus(); + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/client/include/ui/widgets/consolewidget.h b/doomsday/client/include/ui/widgets/consolewidget.h index b0040448de..be90abb345 100644 --- a/doomsday/client/include/ui/widgets/consolewidget.h +++ b/doomsday/client/include/ui/widgets/consolewidget.h @@ -22,6 +22,7 @@ #include #include "guiwidget.h" +#include "buttonwidget.h" #include "consolecommandwidget.h" #include "logwidget.h" @@ -38,20 +39,24 @@ class ConsoleWidget : public QObject, public GuiWidget public: ConsoleWidget(); + ButtonWidget &button(); ConsoleCommandWidget &commandLine(); LogWidget &log(); + de::Rule const &shift(); - bool isOpen() const; + bool isLogOpen() const; // Events. void viewResized(); + void update(); bool handleEvent(de::Event const &event); public slots: - void open(); - void close(); + void openLog(); + void closeLog(); void clearLog(); + void showFullLog(); protected slots: void logContentHeightIncreased(int delta); diff --git a/doomsday/client/include/ui/widgets/taskbarwidget.h b/doomsday/client/include/ui/widgets/taskbarwidget.h index a4552d6524..3a75061d43 100644 --- a/doomsday/client/include/ui/widgets/taskbarwidget.h +++ b/doomsday/client/include/ui/widgets/taskbarwidget.h @@ -23,7 +23,7 @@ #include #include "guiwidget.h" -#include "consolecommandwidget.h" +#include "consolewidget.h" /** * Task bar that acts as the primary UI element of the client's UI. @@ -35,6 +35,9 @@ class TaskBarWidget : public QObject, public GuiWidget public: TaskBarWidget(); + ConsoleWidget &console(); + ConsoleCommandWidget &commandLine(); + bool isOpen() const; de::Rule const &shift(); diff --git a/doomsday/client/src/con_main.cpp b/doomsday/client/src/con_main.cpp index f336a5f81a..b98ddc196b 100644 --- a/doomsday/client/src/con_main.cpp +++ b/doomsday/client/src/con_main.cpp @@ -63,6 +63,8 @@ # include "clientapp.h" # include "ui/windowsystem.h" # include "ui/clientwindow.h" +# include "ui/widgets/consolewidget.h" +# include "ui/widgets/taskbarwidget.h" # include "ui/busyvisual.h" # include "updater/downloaddialog.h" #endif @@ -1463,11 +1465,11 @@ void Con_Open(int yes) #ifdef __CLIENT__ if(yes) { - ClientWindow::main().console().open(); + ClientWindow::main().console().openLog(); } else { - ClientWindow::main().console().close(); + ClientWindow::main().console().closeLog(); } #endif @@ -2619,7 +2621,7 @@ D_CMD(OpenClose) } else { - Con_Open(!ClientWindow::main().console().isOpen()); + Con_Open(!ClientWindow::main().console().isLogOpen()); } return true; } diff --git a/doomsday/client/src/dd_main.cpp b/doomsday/client/src/dd_main.cpp index 9e3d957fab..a1abc9b7db 100644 --- a/doomsday/client/src/dd_main.cpp +++ b/doomsday/client/src/dd_main.cpp @@ -1452,6 +1452,7 @@ bool DD_ChangeGame(de::Game& game, bool allowReload = false) GL_ResetTextureManager(); GL_SetFilter(false); + // Trap the mouse automatically when loading a game in fullscreen. { ClientWindow &mainWin = ClientWindow::main(); mainWin.taskBar().close(); @@ -1702,6 +1703,10 @@ bool DD_ChangeGame(de::Game& game, bool allowReload = false) { ClientWindow::main().taskBar().open(); } + else + { + ClientWindow::main().console().clearLog(); + } #endif return true; } diff --git a/doomsday/client/src/ui/clientwindow.cpp b/doomsday/client/src/ui/clientwindow.cpp index 17f80bc9da..9ca6c917af 100644 --- a/doomsday/client/src/ui/clientwindow.cpp +++ b/doomsday/client/src/ui/clientwindow.cpp @@ -63,7 +63,7 @@ DENG2_OBSERVES(Canvas, FocusChange) /// Root of the nomal UI widgets of this window. GuiRootWidget root; TaskBarWidget *taskBar; - ConsoleWidget *console; + //ConsoleWidget *console; GuiRootWidget busyRoot; @@ -73,7 +73,7 @@ DENG2_OBSERVES(Canvas, FocusChange) needRecreateCanvas(false), mode(Normal), root(thisPublic), - console(0), + //console(0), busyRoot(thisPublic) { /// @todo The decision whether to receive input notifications from the @@ -110,6 +110,7 @@ DENG2_OBSERVES(Canvas, FocusChange) .setInput(Rule::Width, root.viewWidth()); root.add(taskBar); + /* Rule const &unit = ClientApp::windowSystem().style().rules().rule("unit"); console = new ConsoleWidget; @@ -117,13 +118,17 @@ DENG2_OBSERVES(Canvas, FocusChange) .setInput(Rule::Bottom, taskBar->rule().top() - unit) .setInput(Rule::Left, root.viewLeft() + console->shift()); root.add(console); - - QObject::connect(taskBar, SIGNAL(closed()), console, SLOT(close())); - QObject::connect(taskBar, SIGNAL(opened()), console, SLOT(clearLog())); + */ taskBar->setOpeningAction(new CommandAction("menu open")); taskBar->setClosingAction(new CommandAction("menu close")); + //connect(taskBar, SIGNAL(opened()), console, SLOT(clearLog())); + //connect(taskBar, SIGNAL(opened()), console, SLOT(open())); + //connect(taskBar, SIGNAL(closed()), console, SLOT(close())); + + root.setFocus(&taskBar->commandLine()); + // Initially the widget is disabled. It will be enabled when the window // is visible and ready to be drawn. legacy->disable(); @@ -284,7 +289,7 @@ TaskBarWidget &ClientWindow::taskBar() ConsoleWidget &ClientWindow::console() { - return *d->console; + return d->taskBar->console(); } void ClientWindow::setMode(Mode const &mode) diff --git a/doomsday/client/src/ui/widgets/consolecommandwidget.cpp b/doomsday/client/src/ui/widgets/consolecommandwidget.cpp index 919580372c..44ca99e1e0 100644 --- a/doomsday/client/src/ui/widgets/consolecommandwidget.cpp +++ b/doomsday/client/src/ui/widgets/consolecommandwidget.cpp @@ -66,6 +66,11 @@ ConsoleCommandWidget::ConsoleCommandWidget(String const &name) d->updateLexicon(); } +void ConsoleCommandWidget::focusGained() +{ + emit gotFocus(); +} + bool ConsoleCommandWidget::handleEvent(Event const &event) { if(hasFocus() && event.isKeyDown()) diff --git a/doomsday/client/src/ui/widgets/consolewidget.cpp b/doomsday/client/src/ui/widgets/consolewidget.cpp index 1b483344a5..2386f33bd4 100644 --- a/doomsday/client/src/ui/widgets/consolewidget.cpp +++ b/doomsday/client/src/ui/widgets/consolewidget.cpp @@ -32,6 +32,7 @@ using namespace de; DENG2_PIMPL(ConsoleWidget) { + ButtonWidget *button; ConsoleCommandWidget *cmdLine; LogWidget *log; ScalarRule *horizShift; @@ -45,6 +46,7 @@ DENG2_PIMPL(ConsoleWidget) Instance(Public *i) : Base(i), + button(0), cmdLine(0), log(0), opened(true), @@ -78,18 +80,11 @@ DENG2_PIMPL(ConsoleWidget) { if(height->scalar().target() == 0) { - // On the first expansion make sure the margin is taken into account. - delta += log->topMargin(); + // On the first expansion make sure the margins are taken into account. + delta += 2 * log->topMargin(); } height->set(height->scalar().target() + delta, .25f); - if(self.rule().top().valuei() <= 0) - { - // The expansion has reached the top of the screen, so we - // can enable PageUp/Dn keys for the log. - log->enablePageKeys(true); - } - if(useOffsetAnimation) { // Sync the log content with the height animation. @@ -100,33 +95,43 @@ DENG2_PIMPL(ConsoleWidget) ConsoleWidget::ConsoleWidget() : GuiWidget("Console"), d(new Instance(this)) { - Rule const &gap = style().rules().rule("gap"); + Rule const &unit = style().rules().rule("unit"); - ButtonWidget *consoleButton = new ButtonWidget; - consoleButton->setText(DENG2_ESC("b") ">"); - consoleButton->rule() + d->button = new ButtonWidget; + d->button->setText(DENG2_ESC("b") ">"); + add(d->button); + + /* + d->button->rule() .setInput(Rule::Left, rule().left()) .setInput(Rule::Height, style().fonts().font("default").height() + gap * 2) - .setInput(Rule::Width, consoleButton->rule().height()); + .setInput(Rule::Width, d->button->rule().height()); add(consoleButton); + */ - // The task bar has a number of child widgets. d->cmdLine = new ConsoleCommandWidget("commandline"); + add(d->cmdLine); + + connect(d->cmdLine, SIGNAL(gotFocus()), this, SLOT(openLog())); + + /* + // The command line is attached to the button. d->cmdLine->rule() - .setInput(Rule::Left, consoleButton->rule().right()) + .setInput(Rule::Left, d->button->rule().right()) .setInput(Rule::Bottom, rule().bottom()) .setInput(Rule::Right, rule().right()); add(d->cmdLine); + */ - // Keep the button at the top of the expanding command line. - consoleButton->rule().setInput(Rule::Top, d->cmdLine->rule().top()); + // Keep the button at the bottom of the expanding command line. + //consoleButton->rule().setInput(Rule::Bottom, d->cmdLine->rule().bottom()); - // Log. + // The Log is attached to the top of the command line. d->log = new LogWidget("log"); d->log->rule() .setInput(Rule::Left, rule().left()) .setInput(Rule::Right, rule().right()) - .setInput(Rule::Bottom, d->cmdLine->rule().top()) + .setInput(Rule::Bottom, rule().bottom()) .setInput(Rule::Top, OperatorRule::maximum(rule().top(), Const(0))); add(d->log); @@ -136,9 +141,15 @@ ConsoleWidget::ConsoleWidget() : GuiWidget("Console"), d(new Instance(this)) rule() .setInput(Rule::Width, OperatorRule::minimum(ClientWindow::main().root().viewWidth(), OperatorRule::maximum(*d->width, Const(320)))) - .setInput(Rule::Height, d->cmdLine->rule().height() + *d->height); + .setInput(Rule::Height, /*d->cmdLine->rule().height() +*/ *d->height) + .setInput(Rule::Bottom, d->cmdLine->rule().top() - unit); + + closeLog(); +} - close(); +ButtonWidget &ConsoleWidget::button() +{ + return *d->button; } ConsoleCommandWidget &ConsoleWidget::commandLine() @@ -156,7 +167,7 @@ Rule const &ConsoleWidget::shift() return *d->horizShift; } -bool ConsoleWidget::isOpen() const +bool ConsoleWidget::isLogOpen() const { return d->opened; } @@ -170,6 +181,18 @@ void ConsoleWidget::viewResized() } } +void ConsoleWidget::update() +{ + GuiWidget::update(); + + if(rule().top().valuei() <= 0) + { + // The expansion has reached the top of the screen, so we + // can enable PageUp/Dn keys for the log. + d->log->enablePageKeys(true); + } +} + void ConsoleWidget::glInit() { LOG_AS("ConsoleWidget"); @@ -183,6 +206,7 @@ void ConsoleWidget::glDeinit() bool ConsoleWidget::handleEvent(Event const &event) { + /* if(!d->opened) { // Just check for the opening event. @@ -199,6 +223,7 @@ bool ConsoleWidget::handleEvent(Event const &event) } return false; } + */ // Hovering over the right edge shows the <-> cursor. if(event.type() == Event::MousePosition) @@ -251,17 +276,19 @@ bool ConsoleWidget::handleEvent(Event const &event) { KeyEvent const &key = event.as(); - if(!d->grabbed && key.qtKey() == Qt::Key_Escape) + /* + if(!d->grabbed && key.qtKey() == Qt::Key_Escape && + key.modifiers().testFlag(KeyEvent::Shift)) { close(); return true; } + */ if(key.qtKey() == Qt::Key_PageUp || key.qtKey() == Qt::Key_PageDown) { - d->log->enablePageKeys(true); - d->expandLog(rule().top().valuei(), false); + showFullLog(); return true; } @@ -274,30 +301,27 @@ bool ConsoleWidget::handleEvent(Event const &event) return false; } -void ConsoleWidget::open() +void ConsoleWidget::openLog() { if(d->opened) return; d->opened = true; d->horizShift->set(0, .3f); + /* if(hasRoot()) { root().setFocus(d->cmdLine); } + */ } -void ConsoleWidget::close() +void ConsoleWidget::closeLog() { if(!d->opened) return; d->opened = false; d->horizShift->set(-rule().width().valuei() - 1, .3f); - - if(hasRoot()) - { - root().setFocus(0); - } } void ConsoleWidget::clearLog() @@ -307,6 +331,12 @@ void ConsoleWidget::clearLog() d->log->enablePageKeys(false); } +void ConsoleWidget::showFullLog() +{ + d->log->enablePageKeys(true); + d->expandLog(rule().top().valuei(), false); +} + void ConsoleWidget::logContentHeightIncreased(int delta) { d->expandLog(delta, true); diff --git a/doomsday/client/src/ui/widgets/legacywidget.cpp b/doomsday/client/src/ui/widgets/legacywidget.cpp index 46d78c36ed..035e697c82 100644 --- a/doomsday/client/src/ui/widgets/legacywidget.cpp +++ b/doomsday/client/src/ui/widgets/legacywidget.cpp @@ -203,8 +203,16 @@ bool LegacyWidget::handleEvent(Event const &event) App_GameLoaded()) { MouseEvent const &mouse = event.as(); - if(mouse.state() == MouseEvent::Pressed) + if(mouse.state() == MouseEvent::Pressed && hitTest(mouse.pos())) { + if(root().focus()) + { + // First click will remove UI focus, allowing LegacyWidget + // to receive events. + root().setFocus(0); + return true; + } + canvas.trapMouse(); root().window().taskBar().close(); return true; diff --git a/doomsday/client/src/ui/widgets/logwidget.cpp b/doomsday/client/src/ui/widgets/logwidget.cpp index 48566f437d..012d1e765c 100644 --- a/doomsday/client/src/ui/widgets/logwidget.cpp +++ b/doomsday/client/src/ui/widgets/logwidget.cpp @@ -538,7 +538,7 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle int contentHeight() const { - return int(self.rule().height().valuei()) - topMargin; + return int(self.rule().height().valuei()) - 2 * topMargin; } int maxVisibleOffset(int visibleHeight) @@ -779,10 +779,10 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle // Draw the background. background.draw(); - if(pos.height() > duint(topMargin)) + if(pos.height() > 2 * duint(topMargin)) { GLState &st = GLState::push(); - st.setScissor(pos.adjusted(Vector2i(0, topMargin), Vector2i())); + st.setScissor(pos.adjusted(Vector2i(0, topMargin), Vector2i(0, -topMargin))); // First draw the shadow of the text. uMvpMatrix = projMatrix * diff --git a/doomsday/client/src/ui/widgets/taskbarwidget.cpp b/doomsday/client/src/ui/widgets/taskbarwidget.cpp index b136c48298..e04f8652f3 100644 --- a/doomsday/client/src/ui/widgets/taskbarwidget.cpp +++ b/doomsday/client/src/ui/widgets/taskbarwidget.cpp @@ -20,6 +20,7 @@ #include "ui/widgets/guirootwidget.h" #include "ui/widgets/labelwidget.h" #include "ui/widgets/buttonwidget.h" +#include "ui/widgets/consolecommandwidget.h" #include "ui/clientwindow.h" #include "ui/commandaction.h" @@ -39,6 +40,7 @@ DENG2_OBSERVES(Games, GameChange) typedef DefaultVertexBuf VertexBuf; bool opened; + ConsoleWidget *console; ButtonWidget *logo; LabelWidget *status; ScalarRule *vertShift; @@ -131,9 +133,44 @@ DENG2_OBSERVES(Games, GameChange) TaskBarWidget::TaskBarWidget() : GuiWidget("TaskBar"), d(new Instance(this)) { Rule const &gap = style().rules().rule("gap"); + //Rule const &unit = style().rules().rule("unit"); Background bg(style().colors().colorf("background")); + /* + d->consoleButton = new ButtonWidget; + d->consoleButton->setText(DENG2_ESC("b") ">"); + d->consoleButton->rule() + .setInput(Rule::Left, rule().left()) + .setInput(Rule::Height, style().fonts().font("default").height() + gap * 2) + .setInput(Rule::Width, d->consoleButton->rule().height()); + add(d->consoleButton); + + // The task bar has a number of child widgets. + d->cmdLine = new ConsoleCommandWidget("commandline"); + d->cmdLine->rule() + .setInput(Rule::Left, d->consoleButton->rule().right()) + .setInput(Rule::Bottom, rule().bottom()); + add(d->cmdLine); + */ + + d->console = new ConsoleWidget; + d->console->rule() + .setInput(Rule::Left, rule().left() + d->console->shift()); + add(d->console); + + // Position the console button and command line in the task bar. + d->console->button().rule() + .setInput(Rule::Left, rule().left()) + .setInput(Rule::Width, d->console->button().rule().height()) + .setInput(Rule::Bottom, rule().bottom()) + .setInput(Rule::Height, rule().height()); + + d->console->commandLine().rule() + .setInput(Rule::Left, d->console->button().rule().right()) + .setInput(Rule::Bottom, rule().bottom()); + + // DE logo. d->logo = new ButtonWidget; d->logo->setAction(new CommandAction("panel")); d->logo->setImage(style().images().image("logo.px128")); @@ -158,8 +195,12 @@ TaskBarWidget::TaskBarWidget() : GuiWidget("TaskBar"), d(new Instance(this)) .setInput(Rule::Right, d->logo->rule().left()); add(d->status); + // The command line extends all the way to the status indicator. + d->console->commandLine().rule().setInput(Rule::Right, d->status->rule().left()); + d->updateStatus(); + /* ButtonWidget *console = new ButtonWidget; console->setText("Console"); console->setWidthPolicy(LabelWidget::Expand); @@ -170,7 +211,6 @@ TaskBarWidget::TaskBarWidget() : GuiWidget("TaskBar"), d(new Instance(this)) .setInput(Rule::Bottom, rule().bottom()); add(console); - /* ButtonWidget *panel = new ButtonWidget; panel->setImage(style().images().image("gear")); panel->setWidthPolicy(LabelWidget::Expand); @@ -187,6 +227,16 @@ TaskBarWidget::TaskBarWidget() : GuiWidget("TaskBar"), d(new Instance(this)) rule().setInput(Rule::Height, style().fonts().font("default").height() + gap * 2); } +ConsoleWidget &TaskBarWidget::console() +{ + return *d->console; +} + +ConsoleCommandWidget &TaskBarWidget::commandLine() +{ + return d->console->commandLine(); +} + bool TaskBarWidget::isOpen() const { return d->opened; @@ -234,15 +284,33 @@ bool TaskBarWidget::handleEvent(Event const &event) if(event.type() == Event::KeyPress) { KeyEvent const &key = event.as(); + + // Esc opens and closes the task bar. if(key.qtKey() == Qt::Key_Escape) { + // Shift-Esc opens the console. + if(key.modifiers().testFlag(KeyEvent::Shift)) + { + root().setFocus(&d->console->commandLine()); + if(isOpen()) return true; + } + if(isOpen()) + { + if(d->console->isLogOpen()) + { + d->console->closeLog(); + root().setFocus(0); + return true; + } + // Also closes the console log. close(); + } else + { open(); - - if(!key.modifiers().testFlag(KeyEvent::Shift)) - return true; + } + return true; } } return false; @@ -254,6 +322,8 @@ void TaskBarWidget::open() { d->opened = true; + d->console->clearLog(); + d->vertShift->set(0, .2f); d->logo->setOpacity(1, .2f); d->status->setOpacity(1, .2f); @@ -274,6 +344,11 @@ void TaskBarWidget::open() { canvas.trapMouse(false); } + + if(!App_GameLoaded()) + { + root().setFocus(&d->console->commandLine()); + } } } } @@ -283,10 +358,17 @@ void TaskBarWidget::close() if(d->opened) { d->opened = false; + + // Slide the task bar down. d->vertShift->set(rule().height().valuei() + style().rules().rule("unit").valuei(), .2f); d->logo->setOpacity(0, .2f); d->status->setOpacity(0, .2f); + d->console->closeLog(); + + // Clear focus now; callbacks/signal handlers may set the focus elsewhere. + if(hasRoot()) root().setFocus(0); + emit closed(); if(!d->closeAction.isNull()) @@ -305,4 +387,3 @@ void TaskBarWidget::close() } } } -