hikogui封装, 现新增vertical_widget

This commit is contained in:
2024-04-21 21:02:55 +08:00
parent 7806957fa1
commit 2db7d07d1b
15 changed files with 585 additions and 345 deletions

View File

@@ -1,5 +1,7 @@
#pragma once
#include <memory>
#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<window_widget>(txt("Toggle example"));
// 在窗口中放入一个标签控件(A1:B1表示控件占用两格子, 2x1大小, 可能能够通过标签控件的地址来获取控件对象)
widget->content().emplace<w_mixer_track>("A1:B1", txt("mixer_track"));
widget->content().emplace<w_mixer_track>("A1:B1");
// 将窗口指针保存到成员变量中
window_ = std::make_unique<gui_window>(std::move(widget));

View File

@@ -0,0 +1 @@
#include "compound_widget.h"

View File

@@ -0,0 +1,31 @@
#pragma once
#include <hikogui/hikogui.hpp>
#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<widget_intf&> 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;
};

View File

@@ -0,0 +1 @@
#include "leaf_widget.h"

View File

@@ -0,0 +1,6 @@
#pragma once
#include <hikogui/hikogui.hpp>
class leaf_widget : hi::widget {
};

View File

@@ -0,0 +1 @@
#include "panel_widget.h"

View File

@@ -0,0 +1,47 @@
#pragma once
#include <hikogui/hikogui.hpp>
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<typename WidgetType>
WidgetType* get_widget(int32_t index) const noexcept {
return dynamic_cast<WidgetType*>(get_widget(index));
}
hi::widget* operator[](int32_t index) const noexcept {
return get_widget(index);
}
explicit operator std::shared_ptr<hi::widget>() {
return std::shared_ptr<hi::widget>(this);
}
};
#define IMPL_PANEL_SLOT(WIDGET_TYPE, SLOT_TYPE) \
typedef SLOT_TYPE slot_type; \
template<typename WidgetType> \
slot_type& add() { \
auto child_widget = std::make_shared<WidgetType>(this); \
slot_type new_slot(this); \
new_slot.widget = child_widget; \
return push_slot(new_slot); \
} \
template<typename WidgetType, typename ...Args> \
slot_type& add(Args ...args) { \
auto child_widget = std::make_shared<WidgetType>(this, args...); \
slot_type new_slot(this); \
new_slot.widget = child_widget; \
return push_slot(new_slot); \
} \
slot_type& add(std::shared_ptr<hi::widget> 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)

View File

@@ -0,0 +1,104 @@
#pragma once
#include <hikogui/hikogui.hpp>
#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<float> value;
protected:
size_param(rule in_rule, const hi::observer<float>& in_value) : size_rule(in_rule), value(in_value) {}
};
struct stretch_param : size_param {
stretch_param(const hi::observer<float>& 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<typename SlotType>
class basic_slot {
public:
hi::widget* slot_owner;
std::shared_ptr<hi::widget> widget;
basic_slot(hi::widget* in_slot_owner) : slot_owner(in_slot_owner) {
}
template<typename WidgetType>
SlotType& make_content() {
widget = std::make_shared<WidgetType>(slot_owner);
return *static_cast<SlotType*>(this);
}
template<typename WidgetType, typename ...Args>
SlotType& make_content(Args ...args) {
widget = std::make_shared<WidgetType>(slot_owner, args...);
return *static_cast<SlotType*>(this);
}
SlotType& set_content(std::shared_ptr<hi::widget> in_widget) {
widget = in_widget;
widget->parent = slot_owner;
return *static_cast<SlotType*>(this);
}
SlotType& operator[](std::shared_ptr<hi::widget> in_widget) {
widget = in_widget;
widget->parent = slot_owner;
return *static_cast<SlotType*>(this);
}
};
class compound_slot : public basic_slot<compound_slot> {
public:
compound_slot(hi::widget* in_owner) : basic_slot<compound_slot>(in_owner) {
}
};
template<typename SlotType>
class panel_slot : public basic_slot<SlotType> {
public:
size_param size;
hi::box_shape shape;
hi::margins margin;
explicit panel_slot() : basic_slot<SlotType>(nullptr), size(auto_size_param()) {
}
panel_slot(hi::widget* in_owner) : basic_slot<SlotType>(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<float>& in_value) {
size = stretch_param(in_value);
return *this;
}
};

View File

@@ -0,0 +1 @@
#include "vertical_widget.h"

View File

@@ -0,0 +1,85 @@
#pragma once
#include "panel_widget.h"
#include "slot.h"
#include <hikogui/hikogui.hpp>
class vertical_widget : public panel_widget {
public:
class slot : public panel_slot<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<widget_intf&> 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<float> child_height_adjustment = 0.f;
private:
hi::column_layout<slot> _row_layout;
};

View File

@@ -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<T> min_value;
hi::observer<T> max_value;
hi::observer<T> value;
bool _horizontal = true;
const float bar_width = 20; // 当水平时值为bar的宽度
slider_mode _mode;
std::string value_to_string()
{
if constexpr (std::is_same_v<T, float>) {
std::ostringstream ss;
ss << std::fixed << std::setprecision(2) << *value;
return ss.str();
} else if constexpr (std::is_same_v<T, int>) {
return std::to_string(*value);
} else {
return "Unknown type";
}
}
void delta_value(float delta) {
_internal_value += delta;
_internal_value = std::clamp<T>(static_cast<T>(_internal_value), *min_value, *max_value);
value = _internal_value;
}
void set_value(T in_value) {
in_value = std::clamp<T>(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<T>(0);
max_value = static_cast<T>(1);
value = static_cast<T>(0.5);
_internal_value = *value;
_mode = in_mode;
_value_string = value_to_string();
_value_text = std::make_unique<hi::text_widget>(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<float>, 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<float>};
_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<widget_intf&> children(bool include_invisible) noexcept override {
co_yield *_value_text;
}
[[nodiscard]] hi::generator<widget_intf&> children(bool include_invisible) noexcept override;
private:
float _internal_value = 0;
float _down_value = 0;
@@ -225,3 +44,209 @@ private:
hi::observer<hi::txt> _value_string;
bool _is_mouse_hoverd = false;
};
template<typename T>
slider<T>::slider(hi::widget_intf const* in_parent, bool horizontal, slider_mode in_mode) noexcept: hi::widget(in_parent), _horizontal(horizontal) {
min_value = static_cast<T>(0);
max_value = static_cast<T>(1);
value = static_cast<T>(0.5);
_internal_value = *value;
_mode = in_mode;
_value_string = value_to_string();
_value_text = std::make_unique<hi::text_widget>(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<typename T>
std::string slider<T>::value_to_string() {
if constexpr (std::is_same_v<T, float>) {
std::ostringstream ss;
ss << std::fixed << std::setprecision(2) << *value;
return ss.str();
} else if constexpr (std::is_same_v<T, int>) {
return std::to_string(*value);
} else {
return "Unknown type";
}
}
template<typename T>
void slider<T>::delta_value(float delta) {
_internal_value += delta;
_internal_value = std::clamp<T>(static_cast<T>(_internal_value), *min_value, *max_value);
value = _internal_value;
}
template<typename T>
void slider<T>::set_current_value(T in_value) {
in_value = std::clamp<T>(in_value, *min_value, *max_value);
_internal_value = in_value;
value = _internal_value;
}
template<typename T>
hi::box_constraints slider<T>::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<float>, 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<float>};
_constraints.alignment = hi::alignment::middle_center();
}
auto text_constraints = _value_text->update_constraints();
_constraints = max(_constraints, text_constraints);
_constraints.margins = 0;
return _constraints;
}
template<typename T>
hi::hitbox slider<T>::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<typename T>
bool slider<T>::accepts_keyboard_focus(hi::keyboard_focus_group group) const noexcept {
return true;
}
template<typename T>
bool slider<T>::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<typename T>
void slider<T>::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<typename T>
void slider<T>::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<typename T>
hi::generator<hi::widget_intf&> slider<T>::children(bool include_invisible) noexcept {
co_yield *_value_text;
}

View File

@@ -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<float>, 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<float>};
_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<float>() * 2.0f, theme().margin<float>() * 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);
}

View File

@@ -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<float> 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<float>, 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<float>};
_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<float>() * 2.0f, theme().margin<float>() * 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;

View File

@@ -1 +1,2 @@
#include "w_mixer_track.h"

View File

@@ -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<typename Label>
w_mixer_track(hi::not_null<widget_intf const *> 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<hi::text_widget>(this, std::forward<Label>(in_label), hi::alignment::top_flush());
auto l_volume_bar = std::make_unique<volume_bar>(this, true);
auto r_volume_bar = std::make_unique<volume_bar>(this, true);
auto volume_control = std::make_unique<slider<float>>(this, true, slider_mode::mouse_drag);
_label = txt("mixer_track");
_text_widget = text_widget.get();
_l_volume_bar = l_volume_bar.get();
_r_volume_bar = r_volume_bar.get();
_volume_control = volume_control.get();
const auto vertical = std::make_shared<vertical_widget>();
_row.push_back(std::move(text_widget));
_row.push_back(std::move(l_volume_bar));
_row.push_back(std::move(r_volume_bar));
_row.push_back(std::move(volume_control));
}
vertical->add<text_widget>(_label.sub<"text">());
vertical->add<volume_bar>(true).horizontal_alignment(horizontal_alignment::center);
vertical->add<volume_bar>(true);
vertical->add<slider<float>>(true, slider_mode::mouse_drag);
hitbox hitbox_test(point2 position) const noexcept override {
hi_axiom(loop::main().on_thread());
if (mode() >= widget_mode::partial) {
auto r = hitbox{};
for (auto const& cell : _row) {
r = cell.value->hitbox_test_from_parent(position, r);
}
return r;
} else {
return {};
}
}
// 计算控件所需空间
hi::box_constraints update_constraints() noexcept override {
_layout = {};
for (auto& cell : _row) {
cell.set_constraints(cell.value->update_constraints());
}
auto grid_constraints = _row.constraints(os_settings::left_to_right());
return grid_constraints.constrain(*minimum, *maximum);
}
void set_layout(hi::widget_layout const& context) noexcept override {
if (compare_store(_layout, context)) {
_row.set_layout(context.shape, theme().baseline_adjustment());
}
for (auto& cell : _row) {
cell.value->set_layout(context.transform(cell.shape, transform_command::level));
}
}
void draw(hi::draw_context const& context) noexcept override {
if (mode() <= hi::widget_mode::invisible) {
return;
}
for (auto& cell : _row) {
cell.value->draw(context);
}
}
protected:
// This function MUST be overridden when a widget has children.
//
// The order of the children returned is used for determining the next widget for
// keyboard navigation.
//
// The allocator argument should not be used by the function, it is used by the caller
// to allocate the co-routine's frame on the stack.
[[nodiscard]] hi::generator<widget_intf &> children(bool include_invisible) noexcept override
{
// This function is often written as a co-routine that yields a pointer to each of its children.
for (auto& cell : _row) {
co_yield *cell.value;
}
_slot.set_content(vertical);
}
private:
// Child widgets are owned by their parent.
hi::text_widget* _text_widget;
volume_bar* _l_volume_bar;
volume_bar* _r_volume_bar;
slider<float>* _volume_control;
hi::box_constraints _label_constraints;
hi::box_constraints _l_volume_bar_constraints;
// hi::box_constraints _r_volume_bar_constraints;
hi::box_shape _label_shape;
hi::box_shape _l_volume_bar_shape;
// hi::box_shape _r_volume_bar_shape;
hi::column_layout<std::unique_ptr<widget>> _row;
observer<label> _label;
};