From 8dfafc81c299fa0c0876c4f93e9d20d83a829dd6 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 2 Jan 2016 13:07:40 -1000 Subject: [PATCH] Add MessageWindow class --- specter/CMakeLists.txt | 2 + specter/include/Specter/FileBrowser.hpp | 4 + specter/include/Specter/MessageWindow.hpp | 78 +++++++++++++ specter/include/Specter/ModalWindow.hpp | 1 + specter/include/Specter/MultiLineTextView.hpp | 6 + specter/include/Specter/ViewResources.hpp | 2 + specter/lib/Button.cpp | 2 +- specter/lib/FileBrowser.cpp | 71 ++++++++++++ specter/lib/MessageWindow.cpp | 107 ++++++++++++++++++ specter/lib/ModalWindow.cpp | 5 +- specter/lib/MultiLineTextView.cpp | 2 +- 11 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 specter/include/Specter/MessageWindow.hpp create mode 100644 specter/lib/MessageWindow.cpp diff --git a/specter/CMakeLists.txt b/specter/CMakeLists.txt index e65d50391..d38c350aa 100644 --- a/specter/CMakeLists.txt +++ b/specter/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND SPECTER_HEADERS include/Specter/View.hpp include/Specter/RootView.hpp include/Specter/ModalWindow.hpp + include/Specter/MessageWindow.hpp include/Specter/Tooltip.hpp include/Specter/SplitView.hpp include/Specter/ScrollView.hpp @@ -66,6 +67,7 @@ list(APPEND SPECTER_SOURCES lib/View.cpp lib/RootView.cpp lib/ModalWindow.cpp + lib/MessageWindow.cpp lib/Tooltip.cpp lib/SplitView.cpp lib/ScrollView.cpp diff --git a/specter/include/Specter/FileBrowser.hpp b/specter/include/Specter/FileBrowser.hpp index 480b4f161..642619122 100644 --- a/specter/include/Specter/FileBrowser.hpp +++ b/specter/include/Specter/FileBrowser.hpp @@ -9,6 +9,7 @@ #include "Table.hpp" #include "ViewResources.hpp" #include "IViewManager.hpp" +#include "MessageWindow.hpp" #include namespace Specter @@ -20,6 +21,7 @@ public: enum class Type { SaveFile, + SaveDirectory, OpenFile, OpenDirectory, OpenHECLProject @@ -114,6 +116,8 @@ private: } } m_fileFieldBind; + std::unique_ptr m_confirmWindow; + struct FileListingDataBind : ITableDataBinding, ITableStateBinding { FileBrowser& m_fb; diff --git a/specter/include/Specter/MessageWindow.hpp b/specter/include/Specter/MessageWindow.hpp new file mode 100644 index 000000000..ebbefe029 --- /dev/null +++ b/specter/include/Specter/MessageWindow.hpp @@ -0,0 +1,78 @@ +#ifndef SPECTER_MESSAGEWINDOW_HPP +#define SPECTER_MESSAGEWINDOW_HPP + +#include "ModalWindow.hpp" +#include "MultiLineTextView.hpp" +#include "Button.hpp" + +namespace Specter +{ + +class MessageWindow : public ModalWindow +{ +public: + enum class Type + { + InfoOk, + ErrorOk, + ConfirmOkCancel + }; + +private: + Type m_type; + std::function m_func; + + std::unique_ptr m_text; + + struct OKBinding : IButtonBinding + { + MessageWindow& m_mw; + std::string m_name; + OKBinding(MessageWindow& mw, std::string&& name) : m_mw(mw), m_name(std::move(name)) {} + const char* name() const {return m_name.c_str();} + void activated(const boo::SWindowCoord& coord) + { + m_mw.m_func(true); + } + } m_okBind; + ViewChild> m_ok; + + struct CancelBinding : IButtonBinding + { + MessageWindow& m_mw; + std::string m_name; + CancelBinding(MessageWindow& mw, std::string&& name) : m_mw(mw), m_name(std::move(name)) {} + const char* name() const {return m_name.c_str();} + void activated(const boo::SWindowCoord& coord) + { + m_mw.m_func(false); + } + } m_cancelBind; + ViewChild> m_cancel; + +public: + MessageWindow(ViewResources& res, View& parentView, + Type type, const std::string& message, std::function func); + + void updateContentOpacity(float opacity) + { + Zeus::CColor color = Zeus::CColor::lerp({1,1,1,0}, {1,1,1,1}, opacity); + ModalWindow::setMultiplyColor(color); + m_text->setMultiplyColor(color); + m_ok.m_view->setMultiplyColor(color); + m_cancel.m_view->setMultiplyColor(color); + } + + void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); + void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); + void mouseMove(const boo::SWindowCoord&); + void mouseEnter(const boo::SWindowCoord&); + void mouseLeave(const boo::SWindowCoord&); + + void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); + void draw(boo::IGraphicsCommandQueue* gfxQ); +}; + +} + +#endif // SPECTER_MESSAGEWINDOW_HPP diff --git a/specter/include/Specter/ModalWindow.hpp b/specter/include/Specter/ModalWindow.hpp index 8bb6f7dbc..8c1f25946 100644 --- a/specter/include/Specter/ModalWindow.hpp +++ b/specter/include/Specter/ModalWindow.hpp @@ -59,6 +59,7 @@ protected: virtual void updateContentOpacity(float opacity) {} public: + ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint, const Zeus::CColor& bgColor); ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint); void think(); bool skipBuildInAnimation(); diff --git a/specter/include/Specter/MultiLineTextView.hpp b/specter/include/Specter/MultiLineTextView.hpp index 1ad733be1..b6e859018 100644 --- a/specter/include/Specter/MultiLineTextView.hpp +++ b/specter/include/Specter/MultiLineTextView.hpp @@ -37,6 +37,12 @@ public: void colorGlyphs(const Zeus::CColor& newColor); + void setMultiplyColor(const Zeus::CColor& color) + { + for (std::unique_ptr& l : m_lines) + l->setMultiplyColor(color); + } + void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); void draw(boo::IGraphicsCommandQueue* gfxQ); diff --git a/specter/include/Specter/ViewResources.hpp b/specter/include/Specter/ViewResources.hpp index 6f9155527..2ea1aacaf 100644 --- a/specter/include/Specter/ViewResources.hpp +++ b/specter/include/Specter/ViewResources.hpp @@ -21,6 +21,7 @@ class ThemeData Zeus::CColor m_tbBg = {0.4, 0.4, 0.4, 1.0}; Zeus::CColor m_tooltipBg = {0.1, 0.1, 0.1, 0.85}; Zeus::CColor m_splashBg = {0.075, 0.075, 0.075, 0.85}; + Zeus::CColor m_splashErrorBg = {0.1, 0.01, 0.01, 0.85}; Zeus::CColor m_splash1 = {1.0, 1.0, 1.0, 1.0}; Zeus::CColor m_splash2 = {0.3, 0.3, 0.3, 1.0}; @@ -59,6 +60,7 @@ public: virtual const Zeus::CColor& toolbarBackground() const {return m_tbBg;} virtual const Zeus::CColor& tooltipBackground() const {return m_tooltipBg;} virtual const Zeus::CColor& splashBackground() const {return m_splashBg;} + virtual const Zeus::CColor& splashErrorBackground() const {return m_splashErrorBg;} virtual const Zeus::CColor& splash1() const {return m_splash1;} virtual const Zeus::CColor& splash2() const {return m_splash2;} diff --git a/specter/lib/Button.cpp b/specter/lib/Button.cpp index cc0992ec1..e20c745de 100644 --- a/specter/lib/Button.cpp +++ b/specter/lib/Button.cpp @@ -275,7 +275,7 @@ void Button::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) float pf = rootView().viewRes().pixelFactor(); if (m_style == Style::Block) textRect.location[1] += 7 * pf; - textRect.location[0] += sub.size[0] / 2; + textRect.location[0] += m_nomWidth / 2; textRect.size[0] = m_nomWidth; textRect.size[1] = m_nomHeight; m_text->resized(root, textRect); diff --git a/specter/lib/FileBrowser.cpp b/specter/lib/FileBrowser.cpp index 1c368b060..120a327f4 100644 --- a/specter/lib/FileBrowser.cpp +++ b/specter/lib/FileBrowser.cpp @@ -1,5 +1,6 @@ #include "Specter/FileBrowser.hpp" #include "Specter/RootView.hpp" +#include "Specter/MessageWindow.hpp" namespace Specter { @@ -207,6 +208,54 @@ void FileBrowser::okActivated(bool viaButton) int err = HECL::Stat(path.c_str(), &theStat); if (m_type == Type::SaveFile) { + if (m_fileField.m_view->getText().empty()) + { + m_fileField.m_view->setErrorState( + vm.translateOr("file_field_empty", "Unable to save empty file").c_str()); + return; + } + if (!err && !S_ISDIR(theStat.st_mode)) + { + m_confirmWindow.reset(new MessageWindow(rootView().viewRes(), *this, + MessageWindow::Type::ConfirmOkCancel, + HECL::Format(vm.translateOr("overwrite_confirm", "Overwrite '%s'?").c_str(), path.c_str()), + [&,path](bool ok) + { + if (ok) + { + m_returnFunc(true, path); + m_confirmWindow->close(); + close(); + } + else + m_confirmWindow->close(); + })); + updateSize(); + return; + } + if (!err && S_ISDIR(theStat.st_mode)) + { + navigateToPath(path); + return; + } + m_returnFunc(true, path); + close(); + return; + } + else if (m_type == Type::SaveDirectory) + { + if (m_fileField.m_view->getText().empty()) + { + m_fileField.m_view->setErrorState( + vm.translateOr("directory_field_empty", "Unable to make empty-named directory").c_str()); + return; + } + if (!err && !S_ISDIR(theStat.st_mode)) + { + m_fileField.m_view->setErrorState( + vm.translateOr("no_overwrite_file", "Unable to make directory over file").c_str()); + return; + } if (!err && S_ISDIR(theStat.st_mode)) { navigateToPath(path); @@ -302,6 +351,10 @@ void FileBrowser::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton bu { if (skipBuildInAnimation() || closed()) return; + + if (m_confirmWindow) + m_confirmWindow->mouseDown(coord, button, mod); + m_split.mouseDown(coord, button, mod); for (PathButton& b : m_pathButtons) b.m_button.mouseDown(coord, button, mod); @@ -336,6 +389,9 @@ void FileBrowser::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton butt m_recentBookmarks.m_view->mouseUp(coord, button, mod); m_ok.m_button.mouseUp(coord, button, mod); m_cancel.m_button.mouseUp(coord, button, mod); + + if (m_confirmWindow) + m_confirmWindow->mouseUp(coord, button, mod); } void FileBrowser::mouseMove(const boo::SWindowCoord& coord) @@ -352,6 +408,9 @@ void FileBrowser::mouseMove(const boo::SWindowCoord& coord) m_recentBookmarks.m_view->mouseMove(coord); m_ok.m_button.mouseMove(coord); m_cancel.m_button.mouseMove(coord); + + if (m_confirmWindow) + m_confirmWindow->mouseMove(coord); } void FileBrowser::mouseEnter(const boo::SWindowCoord& coord) @@ -369,6 +428,9 @@ void FileBrowser::mouseLeave(const boo::SWindowCoord& coord) m_fileListing.mouseLeave(coord); m_ok.m_button.mouseLeave(coord); m_cancel.m_button.mouseLeave(coord); + + if (m_confirmWindow) + m_confirmWindow->mouseLeave(coord); } void FileBrowser::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll) @@ -422,6 +484,9 @@ void FileBrowser::resized(const boo::SWindowRect& root, const boo::SWindowRect& if (m_split.m_view) m_split.m_view->resized(root, centerRect); + + if (m_confirmWindow) + m_confirmWindow->resized(root, sub); } void FileBrowser::LeftSide::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) @@ -500,12 +565,18 @@ void FileBrowser::think() m_systemBookmarks.m_view->think(); m_projectBookmarks.m_view->think(); m_recentBookmarks.m_view->think(); + + if (m_confirmWindow) + m_confirmWindow->think(); } void FileBrowser::draw(boo::IGraphicsCommandQueue* gfxQ) { ModalWindow::draw(gfxQ); m_split.m_view->draw(gfxQ); + + if (m_confirmWindow) + m_confirmWindow->draw(gfxQ); } void FileBrowser::LeftSide::draw(boo::IGraphicsCommandQueue* gfxQ) diff --git a/specter/lib/MessageWindow.cpp b/specter/lib/MessageWindow.cpp new file mode 100644 index 000000000..05363170f --- /dev/null +++ b/specter/lib/MessageWindow.cpp @@ -0,0 +1,107 @@ +#include "Specter/MessageWindow.hpp" +#include "Specter/ViewResources.hpp" +#include "Specter/RootView.hpp" + +namespace Specter +{ + +MessageWindow::MessageWindow(ViewResources& res, View& parentView, + Type type, const std::string& message, + std::function func) +: ModalWindow(res, parentView, RectangleConstraint(400 * res.pixelFactor(), 150 * res.pixelFactor()), + type==Type::ErrorOk ? res.themeData().splashErrorBackground() : res.themeData().splashBackground()), + m_type(type), m_func(func), + m_okBind(*this, rootView().viewManager().translateOr("ok", "OK")), + m_cancelBind(*this, rootView().viewManager().translateOr("cancel", "Cancel")) +{ + commitResources(res); + + m_text.reset(new MultiLineTextView(res, *this, res.m_mainFont, TextView::Alignment::Center)); + m_text->typesetGlyphs(message, res.themeData().uiText(), 380 * res.pixelFactor()); + + m_ok.m_view.reset(new Button(res, *this, &m_okBind, m_okBind.m_name, + Button::Style::Block, RectangleConstraint(150 * res.pixelFactor()))); + if (type == Type::ConfirmOkCancel) + m_cancel.m_view.reset(new Button(res, *this, &m_cancelBind, m_cancelBind.m_name, + Button::Style::Block, RectangleConstraint(150 * res.pixelFactor()))); + + updateContentOpacity(0.0); +} + +void MessageWindow::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods) +{ + if (closed() || skipBuildInAnimation()) + return; + m_ok.mouseDown(coord, button, mods); + m_cancel.mouseDown(coord, button, mods); +} + +void MessageWindow::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods) +{ + if (closed()) + return; + m_ok.mouseUp(coord, button, mods); + m_cancel.mouseUp(coord, button, mods); +} + +void MessageWindow::mouseMove(const boo::SWindowCoord& coord) +{ + if (closed()) + return; + m_ok.mouseMove(coord); + m_cancel.mouseMove(coord); +} + +void MessageWindow::mouseEnter(const boo::SWindowCoord& coord) +{ + if (closed()) + return; + m_ok.mouseEnter(coord); + m_cancel.mouseEnter(coord); +} + +void MessageWindow::mouseLeave(const boo::SWindowCoord& coord) +{ + if (closed()) + return; + m_ok.mouseLeave(coord); + m_cancel.mouseLeave(coord); +} + +void MessageWindow::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) +{ + ModalWindow::resized(root, sub); + boo::SWindowRect buttonRect = subRect(); + float pf = rootView().viewRes().pixelFactor(); + buttonRect.location[1] += 25 * pf; + buttonRect.size[0] = m_ok.m_view->nominalWidth(); + buttonRect.size[1] = m_ok.m_view->nominalHeight(); + if (m_type == Type::ConfirmOkCancel) + { + buttonRect.location[0] += 45 * pf; + m_ok.m_view->resized(root, buttonRect); + buttonRect.location[0] += 160 * pf; + m_cancel.m_view->resized(root, buttonRect); + } + else + { + buttonRect.location[0] += 125 * pf; + m_ok.m_view->resized(root, buttonRect); + } + + boo::SWindowRect textRect = subRect(); + textRect.location[0] += 200 * pf; + textRect.location[1] += 120 * pf; + m_text->resized(root, textRect); +} + +void MessageWindow::draw(boo::IGraphicsCommandQueue* gfxQ) +{ + ModalWindow::draw(gfxQ); + m_text->draw(gfxQ); + m_ok.m_view->draw(gfxQ); + if (m_type == Type::ConfirmOkCancel) + m_cancel.m_view->draw(gfxQ); +} + +} diff --git a/specter/lib/ModalWindow.cpp b/specter/lib/ModalWindow.cpp index c78e4b430..1fca6796b 100644 --- a/specter/lib/ModalWindow.cpp +++ b/specter/lib/ModalWindow.cpp @@ -276,9 +276,12 @@ void ModalWindow::setFillColors(float t) } ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint) +: ModalWindow(res, parentView, constraint, res.themeData().splashBackground()) {} + +ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint, const Zeus::CColor& bgColor) : View(res, parentView), m_constraint(constraint), - m_windowBg(res.themeData().splashBackground()), + m_windowBg(bgColor), m_windowBgClear(m_windowBg), m_line1(res.themeData().splash1()), m_line2(res.themeData().splash2()), diff --git a/specter/lib/MultiLineTextView.cpp b/specter/lib/MultiLineTextView.cpp index 7845ca576..eb731b03c 100644 --- a/specter/lib/MultiLineTextView.cpp +++ b/specter/lib/MultiLineTextView.cpp @@ -58,7 +58,7 @@ std::string MultiLineTextView::LineWrap(const std::string& str, int wrap) continue; } - if (sz == 1 && (it[0] == ' ' || it[0] == '-')) + if (sz == 1 && (it[0] == ' ' || it[0] == '-' || it[0] == '/' || it[0] == '\\')) { lastSpaceIt = it + 1; lastSpaceRem = rem - 1;