diff --git a/Arona/src/arona_application.h b/Arona/src/arona_application.h index a902f2e..82219f1 100644 --- a/Arona/src/arona_application.h +++ b/Arona/src/arona_application.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "application/application.h" #include "hikogui/hikogui.hpp" #include "widget/mixer/w_mixer_track.h" @@ -31,9 +33,10 @@ public: void init_window() { // get_plugin_host_manager().create_instrument_plugin_host(R"(F:\VST\VST64\Serum_x64.dll)"); // 创建一个窗口 + auto widget = std::make_unique(txt("Toggle example")); // 在窗口中放入一个标签控件(A1:B1表示控件占用两格子, 2x1大小, 可能能够通过标签控件的地址来获取控件对象) - widget->content().emplace("A1:B1", txt("mixer_track")); + widget->content().emplace("A1:B1"); // 将窗口指针保存到成员变量中 window_ = std::make_unique(std::move(widget)); diff --git a/Arona/src/widget/layout/compound_widget.cpp b/Arona/src/widget/layout/compound_widget.cpp new file mode 100644 index 0000000..2aca56e --- /dev/null +++ b/Arona/src/widget/layout/compound_widget.cpp @@ -0,0 +1 @@ +#include "compound_widget.h" diff --git a/Arona/src/widget/layout/compound_widget.h b/Arona/src/widget/layout/compound_widget.h new file mode 100644 index 0000000..e260ad2 --- /dev/null +++ b/Arona/src/widget/layout/compound_widget.h @@ -0,0 +1,31 @@ +#pragma once +#include + +#include "slot.h" + +class compound_widget : public hi::widget { +public: + compound_widget() : hi::widget(nullptr), _slot(this) { + } + + compound_widget(hi::widget_intf const* parent) : hi::widget(parent), _slot(this) { + } + + hi::box_constraints update_constraints() noexcept override { + return _slot.widget->update_constraints(); + } + void set_layout(hi::widget_layout const& context) noexcept override { + return _slot.widget->set_layout(context); + } + void draw(hi::draw_context const& context) noexcept override { + return _slot.widget->draw(context); + } + [[nodiscard]] hi::generator children(bool include_invisible) noexcept override { + co_yield *_slot.widget; + } + hi::hitbox hitbox_test(hi::point2 position) const noexcept override { + return _slot.widget->hitbox_test(position); + } +protected: + compound_slot _slot; +}; diff --git a/Arona/src/widget/layout/leaf_widget.cpp b/Arona/src/widget/layout/leaf_widget.cpp new file mode 100644 index 0000000..bd9f44d --- /dev/null +++ b/Arona/src/widget/layout/leaf_widget.cpp @@ -0,0 +1 @@ +#include "leaf_widget.h" diff --git a/Arona/src/widget/layout/leaf_widget.h b/Arona/src/widget/layout/leaf_widget.h new file mode 100644 index 0000000..54df3dc --- /dev/null +++ b/Arona/src/widget/layout/leaf_widget.h @@ -0,0 +1,6 @@ +#pragma once +#include + +class leaf_widget : hi::widget { + +}; diff --git a/Arona/src/widget/layout/panel_widget.cpp b/Arona/src/widget/layout/panel_widget.cpp new file mode 100644 index 0000000..1ddbed4 --- /dev/null +++ b/Arona/src/widget/layout/panel_widget.cpp @@ -0,0 +1 @@ +#include "panel_widget.h" diff --git a/Arona/src/widget/layout/panel_widget.h b/Arona/src/widget/layout/panel_widget.h new file mode 100644 index 0000000..85b4730 --- /dev/null +++ b/Arona/src/widget/layout/panel_widget.h @@ -0,0 +1,47 @@ +#pragma once +#include + +class panel_widget : public hi::widget { +public: + panel_widget() : hi::widget(nullptr) {} + panel_widget(hi::widget_intf const* parent) : hi::widget(parent) {} + + virtual hi::widget* get_widget(int32_t index) const noexcept = 0; + virtual size_t size() const noexcept = 0; + virtual bool empty() const { return size() == 0; } + template + WidgetType* get_widget(int32_t index) const noexcept { + return dynamic_cast(get_widget(index)); + } + + hi::widget* operator[](int32_t index) const noexcept { + return get_widget(index); + } + + explicit operator std::shared_ptr() { + return std::shared_ptr(this); + } +}; + +#define IMPL_PANEL_SLOT(WIDGET_TYPE, SLOT_TYPE) \ + typedef SLOT_TYPE slot_type; \ + template \ + slot_type& add() { \ + auto child_widget = std::make_shared(this); \ + slot_type new_slot(this); \ + new_slot.widget = child_widget; \ + return push_slot(new_slot); \ + } \ + template \ + slot_type& add(Args ...args) { \ + auto child_widget = std::make_shared(this, args...); \ + slot_type new_slot(this); \ + new_slot.widget = child_widget; \ + return push_slot(new_slot); \ + } \ + slot_type& add(std::shared_ptr child_widget) { \ + slot_type new_slot(this); \ + new_slot.widget = child_widget; \ + return push_slot(new_slot); \ + } \ + slot_type& push_slot(const slot_type& in_slot) diff --git a/Arona/src/widget/layout/slot.h b/Arona/src/widget/layout/slot.h new file mode 100644 index 0000000..d77c0c6 --- /dev/null +++ b/Arona/src/widget/layout/slot.h @@ -0,0 +1,104 @@ +#pragma once +#include + +#define BEGIN_SLOT_PARAMS(SLOT_TYPE) typedef SLOT_TYPE slot_type; + +#define SLOT_PARAM(Type, Name) \ +public:\ +slot_type& Name(Type in_##Name) { \ + _##Name = in_##Name; \ + return *this; \ +} \ +const Type& get_##Name() const { \ + return _##Name; \ +} \ +protected: \ +Type _##Name; + +struct size_param { + enum rule { + auto_size, + stretch, + }; + rule size_rule; + hi::observer value; +protected: + size_param(rule in_rule, const hi::observer& in_value) : size_rule(in_rule), value(in_value) {} +}; + +struct stretch_param : size_param { + stretch_param(const hi::observer& in_value) : size_param(size_param::stretch, in_value) {} + stretch_param() : size_param(size_param::stretch, 1.f) {} +}; + +struct auto_size_param : size_param { + auto_size_param() : size_param(size_param::auto_size, 0.f) {} +}; + +template +class basic_slot { +public: + hi::widget* slot_owner; + std::shared_ptr widget; + + basic_slot(hi::widget* in_slot_owner) : slot_owner(in_slot_owner) { + + } + + template + SlotType& make_content() { + widget = std::make_shared(slot_owner); + return *static_cast(this); + } + + template + SlotType& make_content(Args ...args) { + widget = std::make_shared(slot_owner, args...); + return *static_cast(this); + } + + SlotType& set_content(std::shared_ptr in_widget) { + widget = in_widget; + widget->parent = slot_owner; + return *static_cast(this); + } + + SlotType& operator[](std::shared_ptr in_widget) { + widget = in_widget; + widget->parent = slot_owner; + return *static_cast(this); + } +}; + +class compound_slot : public basic_slot { +public: + compound_slot(hi::widget* in_owner) : basic_slot(in_owner) { + } +}; + +template +class panel_slot : public basic_slot { +public: + size_param size; + hi::box_shape shape; + hi::margins margin; + + explicit panel_slot() : basic_slot(nullptr), size(auto_size_param()) { + } + + panel_slot(hi::widget* in_owner) : basic_slot(in_owner), size(auto_size_param()) { + } + + SlotType& auto_size() { + size = auto_size_param(); + return *this; + } + SlotType& stretch() { + size = stretch_param(); + return *this; + } + SlotType& stretch(const hi::observer& in_value) { + size = stretch_param(in_value); + return *this; + } +}; diff --git a/Arona/src/widget/layout/vertical_widget.cpp b/Arona/src/widget/layout/vertical_widget.cpp new file mode 100644 index 0000000..3d8cac0 --- /dev/null +++ b/Arona/src/widget/layout/vertical_widget.cpp @@ -0,0 +1 @@ +#include "vertical_widget.h" diff --git a/Arona/src/widget/layout/vertical_widget.h b/Arona/src/widget/layout/vertical_widget.h new file mode 100644 index 0000000..be1e1c5 --- /dev/null +++ b/Arona/src/widget/layout/vertical_widget.h @@ -0,0 +1,85 @@ +#pragma once +#include "panel_widget.h" +#include "slot.h" +#include + + + +class vertical_widget : public panel_widget { +public: + class slot : public panel_slot { + public: + slot() : panel_slot(), _horizontal_alignment(hi::horizontal_alignment::left), _vertical_alignment(hi::vertical_alignment::top) {} + explicit slot(vertical_widget* owner) : panel_slot(owner), _horizontal_alignment(hi::horizontal_alignment::left), _vertical_alignment(hi::vertical_alignment::top) { + } + + BEGIN_SLOT_PARAMS(slot) + SLOT_PARAM(hi::horizontal_alignment, horizontal_alignment) + SLOT_PARAM(hi::vertical_alignment, vertical_alignment) + }; + vertical_widget() = default; + explicit vertical_widget(hi::widget* parent) : panel_widget(parent) {} + + IMPL_PANEL_SLOT(vertical_widget, slot) { + return _row_layout.push_back(in_slot).value; + } + + void draw(hi::draw_context const& context) noexcept override { + if (mode() <= hi::widget_mode::invisible) { + return; + } + for (auto& slot : _row_layout) { + slot.value.widget->draw(context); + } + } + + hi::hitbox hitbox_test(hi::point2 position) const noexcept override { + hi_axiom(hi::loop::main().on_thread()); + if (mode() >= hi::widget_mode::partial) { + auto r = hi::hitbox{}; + for (auto& slot : _row_layout) { + r = slot.value.widget->hitbox_test_from_parent(position, r); + } + return r; + } + return hi::hitbox{}; + } + + void set_layout(hi::widget_layout const& context) noexcept override { + if (hi::compare_store(_layout, context)) { + _row_layout.set_layout(context.shape, theme().baseline_adjustment()); + } + + for (const auto& cell: _row_layout) { + cell.value.widget->set_layout(context.transform(cell.shape, hi::transform_command::level)); + } + } + + hi::box_constraints update_constraints() noexcept override { + _layout = {}; + for (auto& cell: _row_layout) { + cell.set_constraints(cell.value.widget->update_constraints()); + } + auto grid_constraints = _row_layout.constraints(hi::os_settings::left_to_right()); + return grid_constraints.constrain(*minimum, *maximum); + } + + [[nodiscard]] hi::generator children(bool include_invisible) noexcept override { + for (auto& slot : _row_layout) { + co_yield *slot.value.widget; + } + } + + hi::widget* get_widget(int32_t index) const noexcept override { + return _row_layout[index].value.widget.get(); + } + size_t size() const noexcept override { + return _row_layout.size(); + } + + hi::observer child_height_adjustment = 0.f; +private: + hi::column_layout _row_layout; +}; + + diff --git a/Arona/src/widget/misc/slider.h b/Arona/src/widget/misc/slider.h index 59209fb..a53a6de 100644 --- a/Arona/src/widget/misc/slider.h +++ b/Arona/src/widget/misc/slider.h @@ -11,208 +11,27 @@ class slider : public hi::widget { public: using super = hi::widget; + slider(hi::widget_intf const* in_parent, bool horizontal, slider_mode in_mode) noexcept; + + slider_mode _mode; hi::observer min_value; hi::observer max_value; hi::observer value; bool _horizontal = true; - const float bar_width = 20; // 当水平时,值为bar的宽度 - slider_mode _mode; - std::string value_to_string() - { - if constexpr (std::is_same_v) { - std::ostringstream ss; - ss << std::fixed << std::setprecision(2) << *value; - return ss.str(); - } else if constexpr (std::is_same_v) { - return std::to_string(*value); - } else { - return "Unknown type"; - } - } - void delta_value(float delta) { - _internal_value += delta; - _internal_value = std::clamp(static_cast(_internal_value), *min_value, *max_value); - value = _internal_value; - } - - void set_value(T in_value) { - in_value = std::clamp(in_value, *min_value, *max_value); - _internal_value = in_value; - value = _internal_value; - } - - slider(hi::widget const* parent, bool horizontal, slider_mode in_mode) noexcept : hi::widget(parent), _horizontal(horizontal) { - min_value = static_cast(0); - max_value = static_cast(1); - value = static_cast(0.5); - _internal_value = *value; - _mode = in_mode; - _value_string = value_to_string(); - _value_text = std::make_unique(this, _value_string, hi::alignment::middle_center()); - - _value_changed = value.subscribe([this](T in_value) { - _internal_value = in_value; - _value_string = value_to_string(); - request_redraw(); - }); - _max_value_changed = max_value.subscribe([this](T in_value) { - if (_internal_value > in_value) { - _internal_value = in_value; - value = _internal_value; - } - request_redraw(); - }); - _min_value_changed = min_value.subscribe([this](T in_value) { - if (_internal_value < in_value) { - _internal_value = in_value; - value = _internal_value; - } - request_redraw(); - }); - } - - hi::box_constraints update_constraints() noexcept override { - auto _constraints = hi::widget::update_constraints(); - if (_horizontal) { - _constraints.minimum = {1.0f, bar_width}; - _constraints.preferred = {1.0f, bar_width}; - _constraints.maximum = {hi::large_number_v, bar_width}; - _constraints.alignment = hi::alignment::middle_center(); - } else { - _constraints.minimum = {bar_width, 1.0f}; - _constraints.preferred = {bar_width, 1.0f}; - _constraints.maximum = {bar_width, hi::large_number_v}; - _constraints.alignment = hi::alignment::middle_center(); - } - auto text_constraints = _value_text->update_constraints(); - _constraints = max(_constraints, text_constraints); - _constraints.margins = 0; - return _constraints; - } - - hi::hitbox hitbox_test(hi::point2 position) const noexcept override { - hi_axiom(hi::loop::main().on_thread()); - - if (mode() >= hi::widget_mode::partial and layout().contains(position)) { - return {id, _layout.elevation, hi::hitbox_type::scroll_bar}; - } - return {}; - } - - bool accepts_keyboard_focus(hi::keyboard_focus_group group) const noexcept override { - return true; - } - - bool handle_event(hi::gui_event const& event) noexcept override { - hi_axiom(hi::loop::main().on_thread()); - if (mode() < hi::widget_mode::partial) - return super::handle_event(event); - switch (event.type()) { - case hi::gui_event_type::keyboard_down: - if (hi::to_bool(event.keyboard_modifiers & hi::keyboard_modifiers::control)) { - _micro_control = true; - return true; - } - break; - case hi::gui_event_type::mouse_down: - if (event.mouse().cause.left_button) { - _down_value = _internal_value; - return true; - } - break; - case hi::gui_event_type::mouse_drag: - if (event.mouse().cause.left_button) { - if (_mode == slider_mode::mouse_snap) { - auto position = event.mouse().position; - if (_micro_control) { - position.x() *= 0.1f; - position.y() *= 0.1f; - } - if (_horizontal) { - float const current_width = _layout.rectangle().width(); - float const new_value_width = std::clamp(position.x(), 0.0f, current_width); - set_value(new_value_width / current_width); - } else { - float const current_height = _layout.rectangle().height(); - float const new_value_height = std::clamp(position.y(), 0.0f, current_height); - set_value(new_value_height / current_height); - } - } else { - if (_horizontal) { - const float delta = event.drag_delta().x() / _layout.rectangle().width(); - set_value(delta + _down_value); - } else { - const float delta = event.drag_delta().y() / _layout.rectangle().height(); - set_value(delta + _down_value); - } - } - return true; - } - break; - case hi::gui_event_type::mouse_wheel: - { - delta_value(event.mouse().wheel_delta.y() * (_micro_control ? 0.0001f : 0.001f)); - return true; - } - break; - case hi::gui_event_type::mouse_enter: - _is_mouse_hoverd = true; - break; - case hi::gui_event_type::mouse_exit: - case hi::gui_event_type::mouse_exit_window: - case hi::gui_event_type::keyboard_exit: - _micro_control = false; - _is_mouse_hoverd = false; - default:; - } - - return super::handle_event(event); - } - - void draw(hi::draw_context const& context) noexcept override { - if (mode() <= hi::widget_mode::invisible || !overlaps(context, layout())) { - return; - } - - auto const current_value = *value; - if (current_value > 0.0f) { - auto const bar_color = theme().color(hi::semantic_color::blue, _layout.layer - 1); - if (_horizontal) { - auto bar_rectangle = _layout.rectangle(); - - float const value_width = bar_rectangle.width() * current_value; - bar_rectangle.set_width(value_width); - - context.draw_box( - _layout, - bar_rectangle, - bar_color); - } else { - auto bar_rectangle = _layout.rectangle(); - auto const bar_height = bar_rectangle.height() * current_value; - bar_rectangle.set_height(bar_height); - context.draw_box( - _layout, - bar_rectangle, - bar_color); - } - } - // Draw value text - if (_is_mouse_hoverd) - _value_text->draw(context); - } - - void set_layout(hi::widget_layout const &context) noexcept override { - if (hi::compare_store(_layout, context)) { - _value_text->set_layout(context.transform(context.shape, hi::transform_command::level)); - } - } + std::string value_to_string(); + void delta_value(float delta); + void set_current_value(T in_value); + hi::box_constraints update_constraints() noexcept override; + hi::hitbox hitbox_test(hi::point2 position) const noexcept override; + bool accepts_keyboard_focus(hi::keyboard_focus_group group) const noexcept override; + bool handle_event(hi::gui_event const& event) noexcept override; + void draw(hi::draw_context const& context) noexcept override; + void set_layout(hi::widget_layout const &context) noexcept override; protected: - [[nodiscard]] hi::generator children(bool include_invisible) noexcept override { - co_yield *_value_text; - } + [[nodiscard]] hi::generator children(bool include_invisible) noexcept override; + private: float _internal_value = 0; float _down_value = 0; @@ -225,3 +44,209 @@ private: hi::observer _value_string; bool _is_mouse_hoverd = false; }; + +template +slider::slider(hi::widget_intf const* in_parent, bool horizontal, slider_mode in_mode) noexcept: hi::widget(in_parent), _horizontal(horizontal) { + min_value = static_cast(0); + max_value = static_cast(1); + value = static_cast(0.5); + _internal_value = *value; + _mode = in_mode; + _value_string = value_to_string(); + _value_text = std::make_unique(this, _value_string, hi::alignment::middle_center()); + + _value_changed = value.subscribe([this](T in_value) { + _internal_value = in_value; + _value_string = value_to_string(); + request_redraw(); + }); + _max_value_changed = max_value.subscribe([this](T in_value) { + if (_internal_value > in_value) { + _internal_value = in_value; + value = _internal_value; + } + request_redraw(); + }); + _min_value_changed = min_value.subscribe([this](T in_value) { + if (_internal_value < in_value) { + _internal_value = in_value; + value = _internal_value; + } + request_redraw(); + }); +} + +template +std::string slider::value_to_string() { + if constexpr (std::is_same_v) { + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << *value; + return ss.str(); + } else if constexpr (std::is_same_v) { + return std::to_string(*value); + } else { + return "Unknown type"; + } +} + +template +void slider::delta_value(float delta) { + _internal_value += delta; + _internal_value = std::clamp(static_cast(_internal_value), *min_value, *max_value); + value = _internal_value; +} + +template +void slider::set_current_value(T in_value) { + in_value = std::clamp(in_value, *min_value, *max_value); + _internal_value = in_value; + value = _internal_value; +} + +template +hi::box_constraints slider::update_constraints() noexcept { + auto _constraints = hi::widget::update_constraints(); + if (_horizontal) { + _constraints.minimum = {1.0f, bar_width}; + _constraints.preferred = {1.0f, bar_width}; + _constraints.maximum = {hi::large_number_v, bar_width}; + _constraints.alignment = hi::alignment::middle_center(); + } else { + _constraints.minimum = {bar_width, 1.0f}; + _constraints.preferred = {bar_width, 1.0f}; + _constraints.maximum = {bar_width, hi::large_number_v}; + _constraints.alignment = hi::alignment::middle_center(); + } + auto text_constraints = _value_text->update_constraints(); + _constraints = max(_constraints, text_constraints); + _constraints.margins = 0; + return _constraints; +} + +template +hi::hitbox slider::hitbox_test(hi::point2 position) const noexcept { + hi_axiom(hi::loop::main().on_thread()); + + if (mode() >= hi::widget_mode::partial and layout().contains(position)) { + return {id, _layout.elevation, hi::hitbox_type::scroll_bar}; + } + return {}; +} + +template +bool slider::accepts_keyboard_focus(hi::keyboard_focus_group group) const noexcept { + return true; +} + +template +bool slider::handle_event(hi::gui_event const& event) noexcept { + hi_axiom(hi::loop::main().on_thread()); + if (mode() < hi::widget_mode::partial) + return super::handle_event(event); + switch (event.type()) { + case hi::gui_event_type::keyboard_down: + if (hi::to_bool(event.keyboard_modifiers & hi::keyboard_modifiers::control)) { + _micro_control = true; + return true; + } + break; + case hi::gui_event_type::mouse_down: + if (event.mouse().cause.left_button) { + _down_value = _internal_value; + return true; + } + break; + case hi::gui_event_type::mouse_drag: + if (event.mouse().cause.left_button) { + if (_mode == slider_mode::mouse_snap) { + auto position = event.mouse().position; + if (_micro_control) { + position.x() *= 0.1f; + position.y() *= 0.1f; + } + if (_horizontal) { + float const current_width = _layout.rectangle().width(); + float const new_value_width = std::clamp(position.x(), 0.0f, current_width); + set_current_value(new_value_width / current_width); + } else { + float const current_height = _layout.rectangle().height(); + float const new_value_height = std::clamp(position.y(), 0.0f, current_height); + set_current_value(new_value_height / current_height); + } + } else { + if (_horizontal) { + const float delta = event.drag_delta().x() / _layout.rectangle().width(); + set_current_value(delta + _down_value); + } else { + const float delta = event.drag_delta().y() / _layout.rectangle().height(); + set_current_value(delta + _down_value); + } + } + return true; + } + break; + case hi::gui_event_type::mouse_wheel: + { + delta_value(event.mouse().wheel_delta.y() * (_micro_control ? 0.0001f : 0.001f)); + return true; + } + break; + case hi::gui_event_type::mouse_enter: + _is_mouse_hoverd = true; + break; + case hi::gui_event_type::mouse_exit: + case hi::gui_event_type::mouse_exit_window: + case hi::gui_event_type::keyboard_exit: + _micro_control = false; + _is_mouse_hoverd = false; + default:; + } + + return super::handle_event(event); +} + +template +void slider::draw(hi::draw_context const& context) noexcept { + if (mode() <= hi::widget_mode::invisible || !overlaps(context, layout())) { + return; + } + + auto const current_value = *value; + if (current_value > 0.0f) { + auto const bar_color = theme().color(hi::semantic_color::blue, _layout.layer - 1); + if (_horizontal) { + auto bar_rectangle = _layout.rectangle(); + + float const value_width = bar_rectangle.width() * current_value; + bar_rectangle.set_width(value_width); + + context.draw_box( + _layout, + bar_rectangle, + bar_color); + } else { + auto bar_rectangle = _layout.rectangle(); + auto const bar_height = bar_rectangle.height() * current_value; + bar_rectangle.set_height(bar_height); + context.draw_box( + _layout, + bar_rectangle, + bar_color); + } + } + // Draw value text + if (_is_mouse_hoverd) + _value_text->draw(context); +} + +template +void slider::set_layout(hi::widget_layout const& context) noexcept { + if (hi::compare_store(_layout, context)) { + _value_text->set_layout(context.transform(context.shape, hi::transform_command::level)); + } +} + +template +hi::generator slider::children(bool include_invisible) noexcept { + co_yield *_value_text; +} diff --git a/Arona/src/widget/misc/volume_bar.cpp b/Arona/src/widget/misc/volume_bar.cpp index c4c38ee..be7a161 100644 --- a/Arona/src/widget/misc/volume_bar.cpp +++ b/Arona/src/widget/misc/volume_bar.cpp @@ -1 +1,67 @@ #include "volume_bar.h" + +volume_bar::volume_bar(const bool horizontal) noexcept: hi::widget(nullptr), _horizontal(horizontal) { + +} + +hi::box_constraints volume_bar::update_constraints() noexcept { + _constraints = hi::widget::update_constraints(); + if (_horizontal) { + _constraints.minimum = {1.0f, bar_width}; + _constraints.preferred = {1.0f, bar_width}; + _constraints.maximum = {large_number_v, bar_width}; + _constraints.alignment = hi::alignment::top_left(); + } else { + _constraints.minimum = {bar_width, 1.0f}; + _constraints.preferred = {bar_width, 1.0f}; + _constraints.maximum = {bar_width, large_number_v}; + _constraints.alignment = hi::vertical_alignment::top; + } + // On left side a check mark, on right side short-cut. Around the label extra margin. + // auto const extra_size = extent2{theme().margin() * 2.0f, theme().margin() * 2.0f}; + // + // auto constraints = _constraints + extra_size; + // constraints.margins = 0; + _constraints.margins = 0; + return _constraints; +} + +void volume_bar::draw(hi::draw_context const& context) noexcept { + if (mode() <= hi::widget_mode::invisible || !overlaps(context, layout())) { + return; + } + + // Draw the volume bar itself. + auto const current_value = *value; + if (current_value > 0.0f) { + auto const bar_color = theme().color(semantic_color::blue, _layout.layer - 1); + if (_horizontal) { + auto bar_rectangle = _layout.rectangle(); + + float const value_width = bar_rectangle.width() * current_value; + bar_rectangle.set_width(value_width); + + context.draw_box( + _layout, + bar_rectangle, + bar_color); + } else { + auto bar_rectangle = _layout.rectangle(); + auto const bar_height = bar_rectangle.height() * current_value; + bar_rectangle.set_height(bar_height); + context.draw_box( + _layout, + bar_rectangle, + bar_color); + } + } + + // Draw the background of the volume bar. + context.draw_box( + _layout, + _layout.rectangle(), + hi::color::transparent(), + hi::color::white(), + theme().border_width(), + hi::border_side::on); +} diff --git a/Arona/src/widget/misc/volume_bar.h b/Arona/src/widget/misc/volume_bar.h index 58f69e4..0d567aa 100644 --- a/Arona/src/widget/misc/volume_bar.h +++ b/Arona/src/widget/misc/volume_bar.h @@ -6,73 +6,17 @@ using namespace hi; class volume_bar : public hi::widget { public: // Construct a volume bar widget. - volume_bar(hi::widget const *parent, const bool horizontal) noexcept : hi::widget(parent), _horizontal(horizontal) { - + volume_bar(widget_intf const* parent, const bool horizontal) : widget(parent), _horizontal(horizontal) { } + volume_bar(const bool horizontal) noexcept; + hi::observer value = 0.3f; float bar_width = 10; // 当水平时,值为bar的宽度 - hi::box_constraints update_constraints() noexcept override { - _constraints = hi::widget::update_constraints(); - if (_horizontal) { - _constraints.minimum = {1.0f, bar_width}; - _constraints.preferred = {1.0f, bar_width}; - _constraints.maximum = {large_number_v, bar_width}; - _constraints.alignment = hi::alignment::top_left(); - } else { - _constraints.minimum = {bar_width, 1.0f}; - _constraints.preferred = {bar_width, 1.0f}; - _constraints.maximum = {bar_width, large_number_v}; - _constraints.alignment = hi::vertical_alignment::top; - } - // On left side a check mark, on right side short-cut. Around the label extra margin. - // auto const extra_size = extent2{theme().margin() * 2.0f, theme().margin() * 2.0f}; - // - // auto constraints = _constraints + extra_size; - // constraints.margins = 0; - _constraints.margins = 0; - return _constraints; - } + hi::box_constraints update_constraints() noexcept override; - void draw(hi::draw_context const& context) noexcept override { - if (mode() <= hi::widget_mode::invisible || !overlaps(context, layout())) { - return; - } + void draw(hi::draw_context const& context) noexcept override; - // Draw the volume bar itself. - auto const current_value = *value; - if (current_value > 0.0f) { - auto const bar_color = theme().color(semantic_color::blue, _layout.layer - 1); - if (_horizontal) { - auto bar_rectangle = _layout.rectangle(); - - float const value_width = bar_rectangle.width() * current_value; - bar_rectangle.set_width(value_width); - - context.draw_box( - _layout, - bar_rectangle, - bar_color); - } else { - auto bar_rectangle = _layout.rectangle(); - auto const bar_height = bar_rectangle.height() * current_value; - bar_rectangle.set_height(bar_height); - context.draw_box( - _layout, - bar_rectangle, - bar_color); - } - } - - // Draw the background of the volume bar. - context.draw_box( - _layout, - _layout.rectangle(), - hi::color::transparent(), - hi::color::white(), - theme().border_width(), - hi::border_side::on); - } private: const bool _horizontal = false; hi::box_constraints _constraints; diff --git a/Arona/src/widget/mixer/w_mixer_track.cpp b/Arona/src/widget/mixer/w_mixer_track.cpp index d8834dc..3e4da51 100644 --- a/Arona/src/widget/mixer/w_mixer_track.cpp +++ b/Arona/src/widget/mixer/w_mixer_track.cpp @@ -1 +1,2 @@ #include "w_mixer_track.h" + diff --git a/Arona/src/widget/mixer/w_mixer_track.h b/Arona/src/widget/mixer/w_mixer_track.h index b26634b..b2efb30 100644 --- a/Arona/src/widget/mixer/w_mixer_track.h +++ b/Arona/src/widget/mixer/w_mixer_track.h @@ -1,103 +1,27 @@ #pragma once #include "hikogui/hikogui.hpp" +#include "widget/layout/compound_widget.h" +#include "widget/layout/vertical_widget.h" #include "widget/misc/slider.h" #include "widget/misc/volume_bar.h" -class w_mixer_track : public hi::widget { +class w_mixer_track : public compound_widget { public: // Every constructor of a widget starts with a `window` and `parent` argument. // In most cases these are automatically filled in when calling a container widget's `emplace()` function. - template - w_mixer_track(hi::not_null parent, Label&& in_label) noexcept : widget(parent) + w_mixer_track(widget_intf const* parent) : compound_widget(parent) { - // Our child widget is a `label_widget` which requires a label to be passed as an third argument. - // We use a templated argument to forward the label into the `label_widget`. - auto text_widget = std::make_unique(this, std::forward