diff --git a/etc/Icons/Decrement.svg b/etc/Icons/Decrement.svg new file mode 100644 index 000000000..6ba0c10ae --- /dev/null +++ b/etc/Icons/Decrement.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/etc/Icons/Increment.svg b/etc/Icons/Increment.svg new file mode 100644 index 000000000..a8b055d14 --- /dev/null +++ b/etc/Icons/Increment.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/lib/tlUI/CMakeLists.txt b/lib/tlUI/CMakeLists.txt index 7a9c2b635..aa45ace46 100644 --- a/lib/tlUI/CMakeLists.txt +++ b/lib/tlUI/CMakeLists.txt @@ -15,6 +15,7 @@ set(HEADERS IWidgetInline.h IWidgetOptions.h IconLibrary.h + IncButton.h IntEdit.h IntModel.h IntSlider.h @@ -57,6 +58,7 @@ set(SOURCE IWidget.cpp IWidgetOptions.cpp IconLibrary.cpp + IncButton.cpp IntEdit.cpp IntModel.cpp IntSlider.cpp diff --git a/lib/tlUI/FloatEdit.cpp b/lib/tlUI/FloatEdit.cpp index e10207a67..cdb6a4cbb 100644 --- a/lib/tlUI/FloatEdit.cpp +++ b/lib/tlUI/FloatEdit.cpp @@ -4,6 +4,9 @@ #include +#include +#include + #include namespace tl @@ -13,6 +16,9 @@ namespace tl struct FloatEdit::Private { std::shared_ptr model; + std::shared_ptr lineEdit; + std::shared_ptr incrementButton; + std::shared_ptr decrementButton; int digits = 3; int precision = 2; @@ -24,13 +30,16 @@ namespace tl const std::shared_ptr& context, const std::shared_ptr& parent) { - LineEdit::_init(context, parent); - _name = "tl::ui::FloatEdit"; + IWidget::_init("tl::ui::FloatEdit", context, parent); TLRENDER_P(); + p.lineEdit = LineEdit::create(context, shared_from_this()); + p.incrementButton = IncButton::create(context, shared_from_this()); + p.decrementButton = IncButton::create(context, shared_from_this()); + setModel(FloatModel::create(context)); - _floatUpdate(); + _textUpdate(); } FloatEdit::FloatEdit() : @@ -66,16 +75,16 @@ namespace tl p.model->observeValue(), [this](float) { - _floatUpdate(); + _textUpdate(); }); p.rangeObserver = observer::ValueObserver::create( p.model->observeRange(), [this](const math::FloatRange&) { - _floatUpdate(); + _textUpdate(); }); } - _floatUpdate(); + _textUpdate(); } void FloatEdit::setDigits(int value) @@ -84,7 +93,7 @@ namespace tl if (value == p.digits) return; p.digits = value; - _floatUpdate(); + _textUpdate(); } void FloatEdit::setPrecision(int value) @@ -93,44 +102,45 @@ namespace tl if (value == p.precision) return; p.precision = value; - _floatUpdate(); + _textUpdate(); + } + + void FloatEdit::setFontRole(FontRole value) + { + _p->lineEdit->setFontRole(value); } - void FloatEdit::keyPressEvent(KeyEvent& event) + void FloatEdit::setGeometry(const math::BBox2i& value) { - LineEdit::keyPressEvent(event); + IWidget::setGeometry(value); TLRENDER_P(); - if (!event.accept) - { - switch (event.key) - { - case Key::Down: - event.accept = true; - p.model->subtractStep(); - break; - case Key::Up: - event.accept = true; - p.model->addStep(); - break; - case Key::PageUp: - event.accept = true; - p.model->addLargeStep(); - break; - case Key::PageDown: - event.accept = true; - p.model->subtractLargeStep(); - break; - } - } + math::BBox2i g = value; + const int buttonsWidth = std::max( + p.incrementButton->getSizeHint().x, + p.decrementButton->getSizeHint().x); + g.max.x -= buttonsWidth; + p.lineEdit->setGeometry(g); + g = value; + g.min.x = g.max.x - buttonsWidth; + g.max.y = g.min.y + g.h() / 2; + p.incrementButton->setGeometry(g); + g.min.y = g.max.y; + g.max.y = value.max.y; + p.decrementButton->setGeometry(g); } - void FloatEdit::keyReleaseEvent(KeyEvent& event) + void FloatEdit::sizeHintEvent(const SizeHintEvent& event) { - LineEdit::keyPressEvent(event); - event.accept = true; + IWidget::sizeHintEvent(event); + TLRENDER_P(); + _sizeHint = p.lineEdit->getSizeHint(); + const int buttonsWidth = std::max( + p.incrementButton->getSizeHint().x, + p.decrementButton->getSizeHint().x); + _sizeHint.x += buttonsWidth; } - void FloatEdit::_floatUpdate() + void FloatEdit::_textUpdate() { TLRENDER_P(); std::string text; @@ -143,8 +153,8 @@ namespace tl arg(range.getMin() < 0 ? "-" : ""). arg(0.F, p.precision, p.precision + 1 + p.digits); } - setText(text); - setFormat(format); + p.lineEdit->setText(text); + p.lineEdit->setFormat(format); } } } diff --git a/lib/tlUI/FloatEdit.h b/lib/tlUI/FloatEdit.h index cc9af0d86..aefbd316b 100644 --- a/lib/tlUI/FloatEdit.h +++ b/lib/tlUI/FloatEdit.h @@ -4,7 +4,7 @@ #pragma once -#include +#include #include namespace tl @@ -12,7 +12,7 @@ namespace tl namespace ui { //! Floating point number editor. - class FloatEdit : public LineEdit + class FloatEdit : public IWidget { TLRENDER_NON_COPYABLE(FloatEdit); @@ -43,11 +43,14 @@ namespace tl //! Set the display precision. void setPrecision(int); - void keyPressEvent(KeyEvent&) override; - void keyReleaseEvent(KeyEvent&) override; + //! Set the font role. + void setFontRole(FontRole); + + void setGeometry(const math::BBox2i&) override; + void sizeHintEvent(const SizeHintEvent&) override; private: - void _floatUpdate(); + void _textUpdate(); TLRENDER_PRIVATE(); }; diff --git a/lib/tlUI/FloatModel.cpp b/lib/tlUI/FloatModel.cpp index 14eb19d8e..5fb5dbb63 100644 --- a/lib/tlUI/FloatModel.cpp +++ b/lib/tlUI/FloatModel.cpp @@ -87,13 +87,13 @@ namespace tl _p->step = value; } - void FloatModel::addStep() + void FloatModel::incrementStep() { TLRENDER_P(); setValue(p.value->get() + p.step); } - void FloatModel::subtractStep() + void FloatModel::decrementStep() { TLRENDER_P(); setValue(p.value->get() - p.step); @@ -109,13 +109,13 @@ namespace tl _p->largeStep = value; } - void FloatModel::addLargeStep() + void FloatModel::incrementLargeStep() { TLRENDER_P(); setValue(p.value->get() + p.largeStep); } - void FloatModel::subtractLargeStep() + void FloatModel::decrementLargeStep() { TLRENDER_P(); setValue(p.value->get() - p.largeStep); diff --git a/lib/tlUI/FloatModel.h b/lib/tlUI/FloatModel.h index 4079ef2eb..2d9d5cd23 100644 --- a/lib/tlUI/FloatModel.h +++ b/lib/tlUI/FloatModel.h @@ -58,15 +58,15 @@ namespace tl void setStep(float); - void addStep(); - void subtractStep(); + void incrementStep(); + void decrementStep(); float getLargeStep() const; void setLargeStep(float); - void addLargeStep(); - void subtractLargeStep(); + void incrementLargeStep(); + void decrementLargeStep(); ///@} diff --git a/lib/tlUI/FloatSlider.cpp b/lib/tlUI/FloatSlider.cpp index e25b36f35..11f69060d 100644 --- a/lib/tlUI/FloatSlider.cpp +++ b/lib/tlUI/FloatSlider.cpp @@ -223,20 +223,20 @@ namespace tl case Key::Left: case Key::Down: event.accept = true; - p.model->subtractStep(); + p.model->decrementStep(); break; case Key::Right: case Key::Up: event.accept = true; - p.model->addStep(); + p.model->incrementStep(); break; case Key::PageUp: event.accept = true; - p.model->addLargeStep(); + p.model->incrementLargeStep(); break; case Key::PageDown: event.accept = true; - p.model->subtractLargeStep(); + p.model->decrementLargeStep(); break; case Key::End: event.accept = true; diff --git a/lib/tlUI/IconLibrary.cpp b/lib/tlUI/IconLibrary.cpp index 103d0a548..fd2872222 100644 --- a/lib/tlUI/IconLibrary.cpp +++ b/lib/tlUI/IconLibrary.cpp @@ -40,6 +40,8 @@ namespace #include "Resources/CompareWipe_96.h" #include "Resources/Copy_192.h" #include "Resources/Copy_96.h" +#include "Resources/Decrement_192.h" +#include "Resources/Decrement_96.h" #include "Resources/Devices_192.h" #include "Resources/Devices_96.h" #include "Resources/DockWidgetClose_192.h" @@ -64,6 +66,8 @@ namespace #include "Resources/FrameNext_96.h" #include "Resources/FramePrev_192.h" #include "Resources/FramePrev_96.h" +#include "Resources/Increment_192.h" +#include "Resources/Increment_96.h" #include "Resources/Info_192.h" #include "Resources/Info_96.h" #include "Resources/Messages_192.h" @@ -161,6 +165,7 @@ namespace tl p.iconData["CompareVertical_96.png"] = CompareVertical_96_png; p.iconData["CompareWipe_96.png"] = CompareWipe_96_png; p.iconData["Copy_96.png"] = Copy_96_png; + p.iconData["Decrement_96.png"] = Decrement_96_png; p.iconData["Devices_96.png"] = Devices_96_png; p.iconData["DockWidgetClose_96.png"] = DockWidgetClose_96_png; p.iconData["DockWidgetNormal_96.png"] = DockWidgetNormal_96_png; @@ -173,6 +178,7 @@ namespace tl p.iconData["Files_96.png"] = Files_96_png; p.iconData["FrameNext_96.png"] = FrameNext_96_png; p.iconData["FramePrev_96.png"] = FramePrev_96_png; + p.iconData["Increment_96.png"] = Increment_96_png; p.iconData["Info_96.png"] = Info_96_png; p.iconData["Messages_96.png"] = Messages_96_png; p.iconData["Mute_96.png"] = Mute_96_png; @@ -206,6 +212,7 @@ namespace tl p.iconData["CompareVertical_192.png"] = CompareVertical_192_png; p.iconData["CompareWipe_192.png"] = CompareWipe_192_png; p.iconData["Copy_192.png"] = Copy_192_png; + p.iconData["Decrement_192.png"] = Decrement_192_png; p.iconData["Devices_192.png"] = Devices_192_png; p.iconData["DockWidgetClose_192.png"] = DockWidgetClose_192_png; p.iconData["DockWidgetNormal_192.png"] = DockWidgetNormal_192_png; @@ -218,6 +225,7 @@ namespace tl p.iconData["Files_192.png"] = Files_192_png; p.iconData["FrameNext_192.png"] = FrameNext_192_png; p.iconData["FramePrev_192.png"] = FramePrev_192_png; + p.iconData["Increment_192.png"] = Increment_192_png; p.iconData["Info_192.png"] = Info_192_png; p.iconData["Messages_192.png"] = Messages_192_png; p.iconData["Mute_192.png"] = Mute_192_png; diff --git a/lib/tlUI/IncButton.cpp b/lib/tlUI/IncButton.cpp new file mode 100644 index 000000000..bf583b040 --- /dev/null +++ b/lib/tlUI/IncButton.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2021-2023 Darby Johnston +// All rights reserved. + +#include + +namespace tl +{ + namespace ui + { + struct IncButton::Private + { + struct SizeData + { + int margin = 0; + }; + SizeData size; + }; + + void IncButton::_init( + const std::shared_ptr& context, + const std::shared_ptr& parent) + { + IButton::_init("tl::ui::IncButton", context, parent); + } + + IncButton::IncButton() : + _p(new Private) + {} + + IncButton::~IncButton() + {} + + std::shared_ptr IncButton::create( + const std::shared_ptr& context, + const std::shared_ptr& parent) + { + auto out = std::shared_ptr(new IncButton); + out->_init(context, parent); + return out; + } + + void IncButton::sizeHintEvent(const SizeHintEvent& event) + { + IButton::sizeHintEvent(event); + TLRENDER_P(); + + p.size.margin = event.style->getSizeRole(SizeRole::MarginInside, event.displayScale); + + _sizeHint = math::Vector2i(); + if (_iconImage) + { + _sizeHint.x = _iconImage->getWidth(); + _sizeHint.y = _iconImage->getHeight(); + } + _sizeHint.x += p.size.margin * 2; + _sizeHint.y += p.size.margin * 2; + } + + void IncButton::drawEvent(const DrawEvent& event) + { + IButton::drawEvent(event); + TLRENDER_P(); + + const math::BBox2i g = _geometry; + + const ColorRole colorRole = _checked ? + ColorRole::Checked : + _buttonRole; + if (colorRole != ColorRole::None) + { + event.render->drawRect( + g, + event.style->getColorRole(colorRole)); + } + + if (_pressed && _geometry.contains(_cursorPos)) + { + event.render->drawRect( + g, + event.style->getColorRole(ColorRole::Pressed)); + } + else if (_inside) + { + event.render->drawRect( + g, + event.style->getColorRole(ColorRole::Hover)); + } + + int x = g.x() + p.size.margin; + if (_iconImage) + { + const auto iconSize = _iconImage->getSize(); + event.render->drawImage( + _iconImage, + math::BBox2i( + x, + g.y() + g.h() / 2 - iconSize.h / 2, + iconSize.w, + iconSize.h)); + } + } + } +} diff --git a/lib/tlUI/IncButton.h b/lib/tlUI/IncButton.h new file mode 100644 index 000000000..a68749f95 --- /dev/null +++ b/lib/tlUI/IncButton.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2021-2023 Darby Johnston +// All rights reserved. + +#pragma once + +#include + +namespace tl +{ + namespace ui + { + //! Numeric widget increment button. + class IncButton : public IButton + { + TLRENDER_NON_COPYABLE(IncButton); + + protected: + void _init( + const std::shared_ptr&, + const std::shared_ptr& parent = nullptr); + + IncButton(); + + public: + ~IncButton() override; + + //! Create a new increment button. + static std::shared_ptr create( + const std::shared_ptr&, + const std::shared_ptr& parent = nullptr); + + void sizeHintEvent(const SizeHintEvent&) override; + void drawEvent(const DrawEvent&) override; + + private: + TLRENDER_PRIVATE(); + }; + } +} diff --git a/lib/tlUI/IntEdit.cpp b/lib/tlUI/IntEdit.cpp index 8895809da..749d02a84 100644 --- a/lib/tlUI/IntEdit.cpp +++ b/lib/tlUI/IntEdit.cpp @@ -4,6 +4,8 @@ #include +#include + #include namespace tl @@ -13,6 +15,7 @@ namespace tl struct IntEdit::Private { std::shared_ptr model; + std::shared_ptr lineEdit; int digits = 3; std::shared_ptr > valueObserver; @@ -23,13 +26,14 @@ namespace tl const std::shared_ptr& context, const std::shared_ptr& parent) { - LineEdit::_init(context, parent); - _name = "tl::ui::IntEdit"; + IWidget::_init("tl::ui::IntEdit", context, parent); TLRENDER_P(); + p.lineEdit = LineEdit::create(context, shared_from_this()); + setModel(IntModel::create(context)); - _intUpdate(); + _textUpdate(); } IntEdit::IntEdit() : @@ -65,16 +69,16 @@ namespace tl p.model->observeValue(), [this](int) { - _intUpdate(); + _textUpdate(); }); p.rangeObserver = observer::ValueObserver::create( p.model->observeRange(), [this](const math::IntRange&) { - _intUpdate(); + _textUpdate(); }); } - _intUpdate(); + _textUpdate(); } void IntEdit::setDigits(int value) @@ -83,44 +87,27 @@ namespace tl if (value == p.digits) return; p.digits = value; - _intUpdate(); + _textUpdate(); } - void IntEdit::keyPressEvent(KeyEvent& event) + void IntEdit::setFontRole(FontRole value) { - LineEdit::keyPressEvent(event); - TLRENDER_P(); - if (!event.accept) - { - switch (event.key) - { - case Key::Down: - event.accept = true; - p.model->subtractStep(); - break; - case Key::Up: - event.accept = true; - p.model->addStep(); - break; - case Key::PageUp: - event.accept = true; - p.model->addLargeStep(); - break; - case Key::PageDown: - event.accept = true; - p.model->subtractLargeStep(); - break; - } - } + _p->lineEdit->setFontRole(value); + } + + void IntEdit::setGeometry(const math::BBox2i& value) + { + IWidget::setGeometry(value); + _p->lineEdit->setGeometry(value); } - void IntEdit::keyReleaseEvent(KeyEvent& event) + void IntEdit::sizeHintEvent(const SizeHintEvent& event) { - LineEdit::keyPressEvent(event); - event.accept = true; + IWidget::sizeHintEvent(event); + _sizeHint = _p->lineEdit->getSizeHint(); } - void IntEdit::_intUpdate() + void IntEdit::_textUpdate() { TLRENDER_P(); std::string text; @@ -133,8 +120,8 @@ namespace tl arg(range.getMin() < 0 ? "-" : ""). arg(0, p.digits); } - setText(text); - setFormat(format); + p.lineEdit->setText(text); + p.lineEdit->setFormat(format); } } } diff --git a/lib/tlUI/IntEdit.h b/lib/tlUI/IntEdit.h index a524593c5..761cbda78 100644 --- a/lib/tlUI/IntEdit.h +++ b/lib/tlUI/IntEdit.h @@ -4,7 +4,7 @@ #pragma once -#include +#include #include namespace tl @@ -12,7 +12,7 @@ namespace tl namespace ui { //! Integer number editor. - class IntEdit : public LineEdit + class IntEdit : public IWidget { TLRENDER_NON_COPYABLE(IntEdit); @@ -40,11 +40,14 @@ namespace tl //! Set the number of digits to display. void setDigits(int); - void keyPressEvent(KeyEvent&) override; - void keyReleaseEvent(KeyEvent&) override; + //! Set the font role. + void setFontRole(FontRole); + + void setGeometry(const math::BBox2i&) override; + void sizeHintEvent(const SizeHintEvent&) override; private: - void _intUpdate(); + void _textUpdate(); TLRENDER_PRIVATE(); }; diff --git a/lib/tlUI/IntModel.cpp b/lib/tlUI/IntModel.cpp index 5908c9770..aad97917e 100644 --- a/lib/tlUI/IntModel.cpp +++ b/lib/tlUI/IntModel.cpp @@ -87,13 +87,13 @@ namespace tl _p->step = value; } - void IntModel::addStep() + void IntModel::incrementStep() { TLRENDER_P(); setValue(p.value->get() + p.step); } - void IntModel::subtractStep() + void IntModel::decrementStep() { TLRENDER_P(); setValue(p.value->get() - p.step); @@ -109,13 +109,13 @@ namespace tl _p->largeStep = value; } - void IntModel::addLargeStep() + void IntModel::incrementLargeStep() { TLRENDER_P(); setValue(p.value->get() + p.largeStep); } - void IntModel::subtractLargeStep() + void IntModel::decrementLargeStep() { TLRENDER_P(); setValue(p.value->get() - p.largeStep); diff --git a/lib/tlUI/IntModel.h b/lib/tlUI/IntModel.h index 1c0d3db81..f4264cdf6 100644 --- a/lib/tlUI/IntModel.h +++ b/lib/tlUI/IntModel.h @@ -58,15 +58,15 @@ namespace tl void setStep(int); - void addStep(); - void subtractStep(); + void incrementStep(); + void decrementStep(); int getLargeStep() const; void setLargeStep(int); - void addLargeStep(); - void subtractLargeStep(); + void incrementLargeStep(); + void decrementLargeStep(); ///@} diff --git a/lib/tlUI/IntSlider.cpp b/lib/tlUI/IntSlider.cpp index 1fad0f5fc..a33fff8a7 100644 --- a/lib/tlUI/IntSlider.cpp +++ b/lib/tlUI/IntSlider.cpp @@ -223,20 +223,20 @@ namespace tl case Key::Left: case Key::Down: event.accept = true; - p.model->subtractStep(); + p.model->decrementStep(); break; case Key::Right: case Key::Up: event.accept = true; - p.model->addStep(); + p.model->incrementStep(); break; case Key::PageUp: event.accept = true; - p.model->addLargeStep(); + p.model->incrementLargeStep(); break; case Key::PageDown: event.accept = true; - p.model->subtractLargeStep(); + p.model->decrementLargeStep(); break; case Key::End: event.accept = true;