From d1c3e993086edf43dc5245fd7fd7e26ae1d8d792 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Oct 2026 20:25:33 -0400 Subject: [PATCH] A number typed into a spinner nobody wrote is the number committed An unset spinner shows the number in force behind an empty box. Committing what was typed into it leaves the unset state first, and leaving it wrote the number in force into the box over the typed one before the commit read it back -- so the first number typed into a setting at its default in the debug settings floater, or any property grid field nobody had written, committed the old number, and the second try worked. Leaving the unset state now keeps what is typed and fills the box only where it is empty. Co-Authored-By: Claude Opus 5.5 --- indra/llui/llspinctrl.cpp | 7 +++++ indra/llui/tests/llspinctrl_test.cpp | 44 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index a2295cf985..e69d3c34d1 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -597,6 +597,13 @@ void LLSpinCtrl::setUnset(bool unset) if (!unset) { mEditor->setLabel(LLStringUtil::null); + // An unset box is empty but for what is typed into it, and what + // is typed is the number being chosen: the one in force goes in + // only where nothing was, or a commit reads it back instead. + if (!mEditor->getText().empty()) + { + return; + } } updateEditor(); } diff --git a/indra/llui/tests/llspinctrl_test.cpp b/indra/llui/tests/llspinctrl_test.cpp index 1ff3c1198c..2d7221eb99 100644 --- a/indra/llui/tests/llspinctrl_test.cpp +++ b/indra/llui/tests/llspinctrl_test.cpp @@ -179,4 +179,48 @@ namespace tut spin->die(); } + + // A number typed into a box nobody wrote is the number committed, the + // first time as every time after: leaving the unset state must not put + // the number in force in over it before it is read. + template<> template<> + void llspinctrl_object::test<6>() + { + if (!ui.ok()) + { + skip("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); + } + + // As the property grid builds a real number's box. + LLSpinCtrl::Params p(LLUICtrlFactory::getDefaultParams()); + p.name = "spin"; + p.rect = LLRect(0, 20, 120, 0); + p.label_width = 0; + p.decimal_digits = 3; + p.min_value = -100000.f; + p.max_value = 100000.f; + p.initial_value = 1.0; + LLSpinCtrl* spin = LLUICtrlFactory::create(p); + LLLineEditor* editor = spin->getChild("SpinCtrl Editor"); + + F32 committed = -1.f; + spin->setCommitCallback([&committed](LLUICtrl* ctrl, const LLSD&) { committed = (F32)ctrl->getValue().asReal(); }); + + spin->setUnset(true); + // A whole number is a real number too. + editor->setText(std::string("2")); + spin->forceEditorCommit(); + ensure_equals("what was typed is what is committed", committed, 2.f); + ensure_equals("and what the spinner holds", spin->getValueF32(), 2.f); + ensure_equals("shown as the spinner shows a number", editor->getText(), std::string("2.000")); + ensure("which somebody chose", !spin->isUnset()); + + // Leaving the unset state with nothing typed still shows the number + // in force, as choosing it by a step or a drag needs. + spin->setUnset(true); + spin->setUnset(false); + ensure_equals("nothing typed, the number in force", editor->getText(), std::string("2.000")); + + spin->die(); + } }