From 3c9882598c65dfe07550f7e129a17daabd2cfe82 Mon Sep 17 00:00:00 2001 From: jacob1 Date: Fri, 18 Jun 2021 23:07:17 -0400 Subject: [PATCH] When air temp textbox is defocused, correct out of range temperatures --- src/gui/interface/Component.cpp | 10 +++ src/gui/interface/Component.h | 3 + src/gui/interface/Textbox.cpp | 6 ++ src/gui/interface/Textbox.h | 8 +++ src/gui/interface/Window.cpp | 9 ++- src/gui/options/OptionsView.cpp | 109 ++++++++++++++++++-------------- src/gui/options/OptionsView.h | 7 +- 7 files changed, 102 insertions(+), 50 deletions(-) diff --git a/src/gui/interface/Component.cpp b/src/gui/interface/Component.cpp index f0d388367..f384c2101 100644 --- a/src/gui/interface/Component.cpp +++ b/src/gui/interface/Component.cpp @@ -252,6 +252,16 @@ void Component::OnMouseWheelInside(int localx, int localy, int d) { } +void Component::OnFocus() +{ + +} + +void Component::OnDefocus() +{ + +} + Component::~Component() { delete menu; diff --git a/src/gui/interface/Component.h b/src/gui/interface/Component.h index 9bb914dc8..2845e6736 100644 --- a/src/gui/interface/Component.h +++ b/src/gui/interface/Component.h @@ -207,5 +207,8 @@ namespace ui virtual void OnTextInput(String text); virtual void OnTextEditing(String text); + + virtual void OnFocus(); + virtual void OnDefocus(); }; } diff --git a/src/gui/interface/Textbox.cpp b/src/gui/interface/Textbox.cpp index 58b8b29db..d30d1a075 100644 --- a/src/gui/interface/Textbox.cpp +++ b/src/gui/interface/Textbox.cpp @@ -604,6 +604,12 @@ void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy) Label::OnMouseMoved(localx, localy, dx, dy); } +void Textbox::OnDefocus() +{ + if (defocusCallback.callback) + defocusCallback.callback(); +} + void Textbox::Draw(const Point& screenPos) { Label::Draw(screenPos); diff --git a/src/gui/interface/Textbox.h b/src/gui/interface/Textbox.h index 58c55286d..ee273af38 100644 --- a/src/gui/interface/Textbox.h +++ b/src/gui/interface/Textbox.h @@ -12,6 +12,11 @@ struct TextboxAction std::function change; }; +struct TextboxDefocusAction +{ + std::function callback; +}; + class Textbox : public Label { void AfterTextChange(bool changed); @@ -34,6 +39,7 @@ public: void SetHidden(bool hidden); bool GetHidden() { return masked; } void SetActionCallback(TextboxAction action) { actionCallback = action; } + void SetDefocusCallback(TextboxDefocusAction action) { defocusCallback = action; } void SetLimit(size_t limit); size_t GetLimit(); @@ -57,6 +63,7 @@ public: void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override; void OnTextInput(String text) override; void OnTextEditing(String text) override; + void OnDefocus() override; void Draw(const Point& screenPos) override; protected: @@ -69,6 +76,7 @@ protected: bool masked, border; int cursor, cursorPositionX, cursorPositionY; TextboxAction actionCallback; + TextboxDefocusAction defocusCallback; String backingText; String placeHolder; diff --git a/src/gui/interface/Window.cpp b/src/gui/interface/Window.cpp index 349fd98aa..4a1b7a983 100644 --- a/src/gui/interface/Window.cpp +++ b/src/gui/interface/Window.cpp @@ -119,7 +119,14 @@ bool Window::IsFocused(const Component* c) const void Window::FocusComponent(Component* c) { - this->focusedComponent_ = c; + if (focusedComponent_ != c) + { + if (focusedComponent_) + focusedComponent_->OnDefocus(); + this->focusedComponent_ = c; + if (c) + c->OnFocus(); + } } void Window::MakeActiveWindow() diff --git a/src/gui/options/OptionsView.cpp b/src/gui/options/OptionsView.cpp index a2d3308a8..fb140736c 100644 --- a/src/gui/options/OptionsView.cpp +++ b/src/gui/options/OptionsView.cpp @@ -26,8 +26,7 @@ #include "graphics/Graphics.h" OptionsView::OptionsView(): - ui::Window(ui::Point(-1, -1), ui::Point(320, 340)), - ambientAirTempPreviewValid(false) + ui::Window(ui::Point(-1, -1), ui::Point(320, 340)) { auto autowidth = [this](ui::Component *c) { @@ -126,28 +125,11 @@ OptionsView::OptionsView(): currentY+=20; ambientAirTemp = new ui::Textbox(ui::Point(Size.X-95, currentY), ui::Point(60, 16)); ambientAirTemp->SetActionCallback({ [this] { - float temp; - try - { - void ParseFloatProperty(String value, float &out); - ParseFloatProperty(ambientAirTemp->GetText(), temp); - ambientAirTempPreviewValid = true; - } - catch (const std::exception &ex) - { - ambientAirTempPreviewValid = false; - } - if (!(MIN_TEMP <= temp && MAX_TEMP >= temp)) - { - ambientAirTempPreviewValid = false; - } - if (ambientAirTempPreviewValid) - { - ambientAirTempPreviewValue = temp; - c->SetAmbientAirTemperature(ambientAirTempPreviewValue); - } - UpdateAmbientAirTempPreview(); + UpdateAirTemp(ambientAirTemp->GetText(), false); } }); + ambientAirTemp->SetDefocusCallback({ [this] { + UpdateAirTemp(ambientAirTemp->GetText(), true); + }}); scrollPanel->AddChild(ambientAirTemp); ambientAirTempPreview = new ui::Button(ui::Point(Size.X-31, currentY), ui::Point(16, 16), "", "Preview"); @@ -369,12 +351,12 @@ OptionsView::OptionsView(): scrollPanel->InnerSize = ui::Point(Size.X, currentY); } -void OptionsView::UpdateAmbientAirTempPreview() +void OptionsView::UpdateAmbientAirTempPreview(float airTemp, bool isValid) { - if (ambientAirTempPreviewValid) + if (isValid) { int HeatToColour(float temp); - int c = HeatToColour(ambientAirTempPreviewValue); + int c = HeatToColour(airTemp); ambientAirTempPreview->Appearance.BackgroundInactive = ui::Colour(PIXR(c), PIXG(c), PIXB(c)); ambientAirTempPreview->SetText(""); } @@ -386,6 +368,55 @@ void OptionsView::UpdateAmbientAirTempPreview() ambientAirTempPreview->Appearance.BackgroundHover = ambientAirTempPreview->Appearance.BackgroundInactive; } +void OptionsView::UpdateAirTemp(String temp, bool isDefocus) +{ + // Parse air temp and determine validity + float airTemp; + bool isValid; + try + { + void ParseFloatProperty(String value, float &out); + ParseFloatProperty(temp, airTemp); + isValid = true; + } + catch (const std::exception &ex) + { + isValid = false; + } + + // While defocusing, correct out of range temperatures and empty textboxes + if (isDefocus) + { + if (temp.empty()) + { + isValid = true; + airTemp = R_TEMP + 273.15; + } + else if (!isValid) + return; + else if (airTemp < MIN_TEMP) + airTemp = MIN_TEMP; + else if (airTemp > MAX_TEMP) + airTemp = MAX_TEMP; + else + return; + + // Update textbox with the new value + StringBuilder sb; + sb << Format::Precision(2) << airTemp; + ambientAirTemp->SetText(sb.Build()); + } + // Out of range temperatures are invalid, preview should go away + else if (airTemp < MIN_TEMP || airTemp > MAX_TEMP) + isValid = false; + + // If valid, set temp + if (isValid) + c->SetAmbientAirTemperature(airTemp); + + UpdateAmbientAirTempPreview(airTemp, isValid); +} + void OptionsView::NotifySettingsChanged(OptionsModel * sender) { heatSimulation->SetChecked(sender->GetHeatSimulation()); @@ -393,28 +424,14 @@ void OptionsView::NotifySettingsChanged(OptionsModel * sender) newtonianGravity->SetChecked(sender->GetNewtonianGravity()); waterEqualisation->SetChecked(sender->GetWaterEqualisation()); airMode->SetOption(sender->GetAirMode()); - if (!ambientAirTempPreviewValid) + // Initialize air temp and preview only when the options menu is opened, and not when user is actively editing the textbox + if (!initializedAirTempPreview) { - // * ambientAirTempPreviewValid is initially false (see constructor). Thus, when - // NotifySettingsChanged is first called when the view is registered as an - // observer, this block executes. Once this happens, NotifySettingsChanged is - // only ever called when some setting is changed by the user in OptionsView. - // This means either a change that doesn't involve the ambientAirTemp Textbox, - // or one that does involve it. The latter case can only happen if the action - // callback of the Textbox called OptionsController::SetAmbientAirTemperature, - // which in turn implies that ambientAirTempPreviewValid is already true. - // * What this all boils down to is that this block is only ever run on two - // occasions: when OptionsView is initialized and when the user decides to - // cancel changing the ambient temperature via the ambientAirTemp Textbox, - // and hasn't yet succeeded. What we want to avoid is SetText being called - // on the Textbox while the user is actively editing its contents, so this - // works out perfectly. - // * Rather twisted. I blame the MVC pattern TPT uses. -- LBPHacker - ambientAirTempPreviewValid = true; - ambientAirTempPreviewValue = sender->GetAmbientAirTemperature(); - UpdateAmbientAirTempPreview(); + initializedAirTempPreview = true; + float airTemp = sender->GetAmbientAirTemperature(); + UpdateAmbientAirTempPreview(airTemp, true); StringBuilder sb; - sb << Format::Precision(2) << ambientAirTempPreviewValue; + sb << Format::Precision(2) << airTemp; ambientAirTemp->SetText(sb.Build()); } gravityMode->SetOption(sender->GetGravityMode()); diff --git a/src/gui/options/OptionsView.h b/src/gui/options/OptionsView.h index 3dd224154..fbb210dcb 100644 --- a/src/gui/options/OptionsView.h +++ b/src/gui/options/OptionsView.h @@ -1,6 +1,7 @@ #ifndef OPTIONSVIEW_H_ #define OPTIONSVIEW_H_ +#include "common/String.h" #include "gui/interface/Window.h" #include "gui/interface/ScrollPanel.h" @@ -39,9 +40,9 @@ class OptionsView: public ui::Window ui::Checkbox * includePressure; ui::Checkbox * perfectCirclePressure; ui::ScrollPanel * scrollPanel; - bool ambientAirTempPreviewValid; - float ambientAirTempPreviewValue; - void UpdateAmbientAirTempPreview(); + bool initializedAirTempPreview = false; + void UpdateAmbientAirTempPreview(float airTemp, bool isValid); + void UpdateAirTemp(String temp, bool isDefocus); public: OptionsView(); void NotifySettingsChanged(OptionsModel * sender);