From b8a220e1dd1affc9c5e94d86e1a5d513426f4e60 Mon Sep 17 00:00:00 2001 From: casey langen Date: Thu, 9 Mar 2017 21:13:36 -0800 Subject: [PATCH] - Moved the hotkey tester to an InputOverlay so it takes up less space - Fixed bug where overlays may become obscured if a layout change happens in the background while one is visible. - Added "SyncOnStartup" setting to SettingsLayout --- src/musikbox/app/layout/SettingsLayout.cpp | 41 ++++++++++++++-------- src/musikbox/app/layout/SettingsLayout.h | 8 ++--- src/musikbox/cursespp/App.cpp | 12 ++++++- src/musikbox/cursespp/App.h | 8 +++++ src/musikbox/cursespp/IWindow.h | 1 + src/musikbox/cursespp/InputOverlay.cpp | 14 ++++++++ src/musikbox/cursespp/InputOverlay.h | 2 ++ src/musikbox/cursespp/LayoutBase.cpp | 2 +- src/musikbox/cursespp/OverlayBase.h | 14 ++++++++ src/musikbox/cursespp/Window.cpp | 13 ++++++- src/musikbox/cursespp/Window.h | 3 +- 11 files changed, 96 insertions(+), 22 deletions(-) diff --git a/src/musikbox/app/layout/SettingsLayout.cpp b/src/musikbox/app/layout/SettingsLayout.cpp index a83e3183f..7d45d1e3b 100755 --- a/src/musikbox/app/layout/SettingsLayout.cpp +++ b/src/musikbox/app/layout/SettingsLayout.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -101,7 +102,11 @@ SettingsLayout::~SettingsLayout() { } void SettingsLayout::OnCheckboxChanged(cursespp::Checkbox* cb, bool checked) { - if (cb == removeCheckbox.get()) { + if (cb == syncOnStartupCheckbox.get()) { + this->libraryPrefs->SetBool(core::prefs::keys::SyncOnStartup, checked); + this->libraryPrefs->Save(); + } + else if (cb == removeCheckbox.get()) { this->libraryPrefs->SetBool(core::prefs::keys::RemoveMissingFiles, checked); this->libraryPrefs->Save(); } @@ -152,6 +157,12 @@ void SettingsLayout::OnPluginsDropdownActivate(cursespp::TextLabel* label) { PluginOverlay::Show(); } +void SettingsLayout::OnHotkeyDropdownActivate(cursespp::TextLabel* label) { + std::shared_ptr overlay(new InputOverlay()); + overlay->SetTitle("hotkey tester").SetInputMode(IInput::InputRaw); + App::Overlays().Push(overlay); +} + void SettingsLayout::OnLayout() { int x = this->GetX(), y = this->GetY(); int cx = this->GetWidth(), cy = this->GetHeight(); @@ -175,12 +186,11 @@ void SettingsLayout::OnLayout() { this->outputDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); this->transportDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); this->pluginsDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); + this->hotkeyDropdown->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); this->dotfileCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); + this->syncOnStartupCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); this->removeCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); this->customColorsCheckbox->MoveAndResize(1, y++, cx - 1, LABEL_HEIGHT); - - this->hotkeyLabel->MoveAndResize(1, y + 1, this->hotkeyLabel->Length(), LABEL_HEIGHT); - this->hotkeyInput->MoveAndResize(RIGHT(this->hotkeyLabel), y, HOTKEY_INPUT_WIDTH, INPUT_HEIGHT); } void SettingsLayout::RefreshAddedPaths() { @@ -249,23 +259,25 @@ void SettingsLayout::InitializeWindows() { this->pluginsDropdown->SetText(arrow + " enable/disable plugins"); this->pluginsDropdown->Activated.connect(this, &SettingsLayout::OnPluginsDropdownActivate); + this->hotkeyDropdown.reset(new TextLabel()); + this->hotkeyDropdown->SetText(arrow + " hotkey tester"); + this->hotkeyDropdown->Activated.connect(this, &SettingsLayout::OnHotkeyDropdownActivate); + CREATE_CHECKBOX(this->dotfileCheckbox, "show dotfiles in directory browser"); + CREATE_CHECKBOX(this->syncOnStartupCheckbox, "sync metadata on startup"); CREATE_CHECKBOX(this->removeCheckbox, "remove missing files from library"); CREATE_CHECKBOX(this->customColorsCheckbox, "disable custom colors (requires restart)"); - this->hotkeyLabel.reset(new TextLabel()); - this->hotkeyLabel->SetText("hotkey tester: "); - this->hotkeyInput.reset(new TextInput(IInput::InputRaw)); - this->browseList->SetFocusOrder(0); this->addedPathsList->SetFocusOrder(1); this->outputDropdown->SetFocusOrder(2); this->transportDropdown->SetFocusOrder(3); this->pluginsDropdown->SetFocusOrder(4); - this->dotfileCheckbox->SetFocusOrder(5); - this->removeCheckbox->SetFocusOrder(6); - this->customColorsCheckbox->SetFocusOrder(7); - this->hotkeyInput->SetFocusOrder(8); + this->hotkeyDropdown->SetFocusOrder(5); + this->dotfileCheckbox->SetFocusOrder(6); + this->syncOnStartupCheckbox->SetFocusOrder(7); + this->removeCheckbox->SetFocusOrder(8); + this->customColorsCheckbox->SetFocusOrder(9); this->AddWindow(this->browseLabel); this->AddWindow(this->addedPathsLabel); @@ -274,11 +286,11 @@ void SettingsLayout::InitializeWindows() { this->AddWindow(this->outputDropdown); this->AddWindow(this->transportDropdown); this->AddWindow(this->pluginsDropdown); + this->AddWindow(this->hotkeyDropdown); this->AddWindow(this->dotfileCheckbox); + this->AddWindow(this->syncOnStartupCheckbox); this->AddWindow(this->removeCheckbox); this->AddWindow(this->customColorsCheckbox); - this->AddWindow(this->hotkeyLabel); - this->AddWindow(this->hotkeyInput); } void SettingsLayout::SetShortcutsWindow(ShortcutsWindow* shortcuts) { @@ -333,6 +345,7 @@ void SettingsLayout::CheckShowFirstRunDialog() { } void SettingsLayout::LoadPreferences() { + this->syncOnStartupCheckbox->SetChecked(this->libraryPrefs->GetBool(core::prefs::keys::SyncOnStartup, true)); this->removeCheckbox->SetChecked(this->libraryPrefs->GetBool(core::prefs::keys::RemoveMissingFiles, true)); this->customColorsCheckbox->SetChecked(this->libraryPrefs->GetBool(box::prefs::keys::DisableCustomColors)); diff --git a/src/musikbox/app/layout/SettingsLayout.h b/src/musikbox/app/layout/SettingsLayout.h index e3c13d340..6fecd9e9a 100755 --- a/src/musikbox/app/layout/SettingsLayout.h +++ b/src/musikbox/app/layout/SettingsLayout.h @@ -97,6 +97,7 @@ namespace musik { void OnOutputDropdownActivated(cursespp::TextLabel* label); void OnTransportDropdownActivate(cursespp::TextLabel* label); void OnPluginsDropdownActivate(cursespp::TextLabel* label); + void OnHotkeyDropdownActivate(cursespp::TextLabel* label); int64 ListItemDecorator( cursespp::ScrollableWindow* w, @@ -114,9 +115,11 @@ namespace musik { std::shared_ptr outputDropdown; std::shared_ptr transportDropdown; std::shared_ptr pluginsDropdown; + std::shared_ptr hotkeyDropdown; - std::shared_ptr removeCheckbox; std::shared_ptr dotfileCheckbox; + std::shared_ptr syncOnStartupCheckbox; + std::shared_ptr removeCheckbox; std::shared_ptr customColorsCheckbox; std::shared_ptr browseLabel; @@ -124,9 +127,6 @@ namespace musik { std::shared_ptr browseList; std::shared_ptr addedPathsList; - std::shared_ptr hotkeyLabel; - std::shared_ptr hotkeyInput; - std::shared_ptr firstRunDialog; std::shared_ptr addedPathsAdapter; diff --git a/src/musikbox/cursespp/App.cpp b/src/musikbox/cursespp/App.cpp index 616b29b44..c50765d8b 100755 --- a/src/musikbox/cursespp/App.cpp +++ b/src/musikbox/cursespp/App.cpp @@ -236,7 +236,14 @@ void App::Run(ILayoutPtr layout) { because they may muck around with layout, then redraw the window. if done in the reverse order, the user may observe more flicker. */ Window::MessageQueue().Dispatch(); - Window::WriteToScreen(this->state.input); + + if (Window::WriteToScreen(this->state.input)) { + /* if we wrote to the screen that means panels could have shifted + around. ensure any visible overlay is still on top. */ + if (this->state.overlayWindow && !this->state.overlayWindow->IsTop()) { + this->state.overlay->BringToTop(); + } + } } overlays.Clear(); @@ -282,6 +289,9 @@ void App::CheckShowOverlay() { this->state.overlay = top; + this->state.overlayWindow = + top ? dynamic_cast(top.get()) : nullptr; + ILayoutPtr newTopLayout = this->state.ActiveLayout(); if (newTopLayout) { newTopLayout->Layout(); diff --git a/src/musikbox/cursespp/App.h b/src/musikbox/cursespp/App.h index 303f7c1fc..158cdbb73 100755 --- a/src/musikbox/cursespp/App.h +++ b/src/musikbox/cursespp/App.h @@ -68,12 +68,20 @@ namespace cursespp { private: struct WindowState { ILayoutPtr overlay; + IWindow* overlayWindow; ILayoutPtr layout; IWindowPtr focused; IViewRoot* viewRoot; IInput* input; IKeyHandler* keyHandler; + WindowState() { + this->overlayWindow = nullptr; + this->viewRoot = nullptr; + this->input = nullptr; + this->keyHandler = nullptr; + } + inline ILayoutPtr ActiveLayout() { /* if there's a visible overlay, it's always the current layout and will consume all key events */ diff --git a/src/musikbox/cursespp/IWindow.h b/src/musikbox/cursespp/IWindow.h index 3762fd528..0330fdeaf 100755 --- a/src/musikbox/cursespp/IWindow.h +++ b/src/musikbox/cursespp/IWindow.h @@ -83,6 +83,7 @@ namespace cursespp { virtual bool IsVisible() = 0; virtual bool IsFocused() = 0; virtual void OnParentVisibilityChanged(bool visible) = 0; + virtual bool IsTop() = 0; }; typedef std::shared_ptr IWindowPtr; diff --git a/src/musikbox/cursespp/InputOverlay.cpp b/src/musikbox/cursespp/InputOverlay.cpp index 1ebad6dd4..270f2b9ea 100644 --- a/src/musikbox/cursespp/InputOverlay.cpp +++ b/src/musikbox/cursespp/InputOverlay.cpp @@ -64,6 +64,7 @@ InputOverlay::InputOverlay() { this->textInput->SetFocusedFrameColor(CURSESPP_OVERLAY_INPUT_FRAME); this->textInput->SetFocusedContentColor(CURSESPP_OVERLAY_BACKGROUND); this->textInput->EnterPressed.connect(this, &InputOverlay::OnInputEnterPressed); + this->textInput->TextChanged.connect(this, &InputOverlay::OnInputKeyPress); this->AddWindow(this->textInput); } @@ -114,6 +115,15 @@ InputOverlay& InputOverlay::SetWidth(int width) { return *this; } +void InputOverlay::OnInputKeyPress(TextInput* input, std::string key) { + /* raw input mode will not allow the ESC key to propagate. + we can catch it here, and pass it through to the regular key + handler to close the dialog */ + if (input->GetInputMode() == IInput::InputRaw && key == "^[") { + this->KeyPress(key); + } +} + bool InputOverlay::KeyPress(const std::string& key) { if (key == "^[") { /* esc closes */ this->Dismiss(); @@ -132,6 +142,10 @@ void InputOverlay::OnInputEnterPressed(TextInput* input) { this->Dismiss(); } } +InputOverlay& InputOverlay::SetInputMode(IInput::InputMode mode) { + this->textInput->SetInputMode(mode); + return *this; +} InputOverlay& InputOverlay::SetInputAcceptedCallback(InputAcceptedCallback cb) { this->inputAcceptedCallback = cb; diff --git a/src/musikbox/cursespp/InputOverlay.h b/src/musikbox/cursespp/InputOverlay.h index 2e04b4bfc..eb856c75c 100644 --- a/src/musikbox/cursespp/InputOverlay.h +++ b/src/musikbox/cursespp/InputOverlay.h @@ -55,6 +55,7 @@ namespace cursespp { InputOverlay& SetText(const std::string& text); InputOverlay& SetInputAcceptedCallback(InputAcceptedCallback cb); InputOverlay& SetWidth(int width); + InputOverlay& SetInputMode(IInput::InputMode mode); virtual void Layout(); virtual bool KeyPress(const std::string& key); @@ -62,6 +63,7 @@ namespace cursespp { protected: virtual void OnVisibilityChanged(bool visible); virtual void OnInputEnterPressed(TextInput* input); + virtual void OnInputKeyPress(TextInput* input, std::string key); private: void Redraw(); diff --git a/src/musikbox/cursespp/LayoutBase.cpp b/src/musikbox/cursespp/LayoutBase.cpp index 99f28039d..a51bf8a62 100755 --- a/src/musikbox/cursespp/LayoutBase.cpp +++ b/src/musikbox/cursespp/LayoutBase.cpp @@ -164,7 +164,7 @@ void LayoutBase::Invalidate() { bool LayoutBase::AddWindow(IWindowPtr window) { if (!window) { - throw std::runtime_error("asdf"); + throw std::runtime_error("window cannot be null!"); } if (find(this->children, window) >= 0) { diff --git a/src/musikbox/cursespp/OverlayBase.h b/src/musikbox/cursespp/OverlayBase.h index 47f650298..014a1c889 100644 --- a/src/musikbox/cursespp/OverlayBase.h +++ b/src/musikbox/cursespp/OverlayBase.h @@ -49,6 +49,20 @@ namespace cursespp { this->stack = stack; } + virtual bool IsTop() { + if (LayoutBase::IsTop()) { + return true; + } + + for (size_t i = 0; i < this->GetWindowCount(); i++) { + if (this->GetWindowAt(i)->IsTop()) { + return true; + } + } + + return false; + } + protected: OverlayStack* GetOverlayStack() { return this->stack; diff --git a/src/musikbox/cursespp/Window.cpp b/src/musikbox/cursespp/Window.cpp index a527cb4d4..3cf0b881e 100755 --- a/src/musikbox/cursespp/Window.cpp +++ b/src/musikbox/cursespp/Window.cpp @@ -46,8 +46,10 @@ using namespace cursespp; using namespace musik::core::runtime; static int NEXT_ID = 0; + static bool drawPending = false; static bool freeze = false; +static Window* top = nullptr; static MessageQueue messageQueue; @@ -68,13 +70,16 @@ static inline void DrawCursor(IInput* input) { } } -void Window::WriteToScreen(IInput* input) { +bool Window::WriteToScreen(IInput* input) { if (drawPending && !freeze) { drawPending = false; update_panels(); doupdate(); DrawCursor(input); + return true; } + + return false; } void Window::InvalidateScreen() { @@ -151,9 +156,15 @@ void Window::BringToTop() { if (this->contentPanel != this->framePanel) { top_panel(this->contentPanel); } + + ::top = this; } } +bool Window::IsTop() { + return (::top == this); +} + void Window::SendToBottom() { if (this->framePanel) { bottom_panel(this->contentPanel); diff --git a/src/musikbox/cursespp/Window.h b/src/musikbox/cursespp/Window.h index 4f997c89b..121f545b9 100755 --- a/src/musikbox/cursespp/Window.h +++ b/src/musikbox/cursespp/Window.h @@ -105,12 +105,13 @@ namespace cursespp { virtual bool IsVisible(); virtual bool IsFocused(); + virtual bool IsTop(); virtual void OnParentVisibilityChanged(bool visible); bool HasBadBounds() { return this->badBounds; } - static void WriteToScreen(IInput* input); + static bool WriteToScreen(IInput* input); static void InvalidateScreen(); static void Freeze(); static void Unfreeze();