77 observer<semantic_text_style>
text_style = semantic_text_style::label;
81 observer<alignment>
alignment = alignment::middle_flush();
83 virtual ~text_field_widget()
85 hi_assert_not_null(delegate);
86 delegate->deinit(*
this);
92 hi_assert_not_null(this->delegate);
93 _delegate_cbt = this->delegate->subscribe([&] {
94 ++global_counter<
"text_field_widget:delegate:layout">;
97 this->delegate->init(*
this);
99 _scroll_widget = std::make_unique<scroll_widget<axis::none>>(
this);
103 _error_label_widget =
104 std::make_unique<label_widget>(
this, _error_label, alignment::top_left(), semantic_text_style::error);
106 _continues_cbt =
continues.subscribe([&](
auto...) {
107 ++global_counter<
"text_field_widget:continues:constrain">;
110 _text_style_cbt =
text_style.subscribe([&](
auto...) {
111 ++global_counter<
"text_field_widget:text_style:constrain">;
114 _text_cbt = _text.subscribe([&](
auto...) {
115 ++global_counter<
"text_field_widget:text:constrain">;
118 _error_label_cbt = _error_label.subscribe([&](
auto const& new_value) {
119 ++global_counter<
"text_field_widget:error_label:constrain">;
126 std::shared_ptr<delegate_type> delegate,
127 text_field_widget_attribute
auto&&...attributes) noexcept :
130 set_attributes(hi_forward(attributes)...);
148 [[nodiscard]] generator<widget_intf&> children(
bool include_invisible)
noexcept override
150 co_yield *_scroll_widget;
155 hi_assert_not_null(delegate);
156 hi_assert_not_null(_error_label_widget);
157 hi_assert_not_null(_scroll_widget);
159 if (*_text_widget->focus) {
161 _error_label = delegate->validate(*
this, *_text);
169 _scroll_constraints = _scroll_widget->update_constraints();
171 hilet scroll_width = 100;
172 hilet box_size = extent2{
173 _scroll_constraints.margins.left() + scroll_width + _scroll_constraints.margins.right(),
174 _scroll_constraints.margins.top() + _scroll_constraints.preferred.height() + _scroll_constraints.margins.bottom()};
176 auto size = box_size;
177 auto margins = theme().margin();
178 if (_error_label->empty()) {
180 _error_label_constraints = _error_label_widget->update_constraints();
184 _error_label_constraints = _error_label_widget->update_constraints();
185 inplace_max(size.width(), _error_label_constraints.preferred.width());
186 size.height() += _error_label_constraints.margins.top() + _error_label_constraints.preferred.height();
187 inplace_max(margins.left(), _error_label_constraints.margins.left());
188 inplace_max(margins.right(), _error_label_constraints.margins.right());
189 inplace_max(margins.bottom(), _error_label_constraints.margins.bottom());
193 hilet resolved_alignment = resolve_mirror(*
alignment, os_settings::left_to_right());
195 return {size, size, size, resolved_alignment, margins};
197 void set_layout(widget_layout
const& context)
noexcept override
200 hilet scroll_size = extent2{
202 _scroll_constraints.margins.top() + _scroll_constraints.preferred.height() +
203 _scroll_constraints.margins.bottom()};
205 hilet scroll_rectangle = aarectangle{point2{0, context.height() - scroll_size.height()}, scroll_size};
206 _scroll_shape = box_shape{_scroll_constraints, scroll_rectangle, theme().baseline_adjustment()};
209 hilet error_label_rectangle =
210 aarectangle{0, 0, context.rectangle().width(), _error_label_constraints.preferred.height()};
211 _error_label_shape = box_shape{_error_label_constraints, error_label_rectangle, theme().baseline_adjustment()};
216 _error_label_widget->set_layout(context.transform(_error_label_shape));
218 _scroll_widget->set_layout(context.transform(_scroll_shape));
220 void draw(draw_context
const& context)
noexcept override
223 draw_background_box(context);
225 _scroll_widget->draw(context);
226 _error_label_widget->draw(context);
229 bool handle_event(gui_event
const& event)
noexcept override
231 switch (event.type()) {
232 case gui_event_type::gui_cancel:
239 case gui_event_type::gui_activate:
251 hitbox hitbox_test(point2 position)
const noexcept override
255 r = _scroll_widget->hitbox_test_from_parent(position, r);
256 r = _error_label_widget->hitbox_test_from_parent(position, r);
262 [[nodiscard]]
bool accepts_keyboard_focus(keyboard_focus_group group)
const noexcept override
265 return _scroll_widget->accepts_keyboard_focus(group);
270 [[nodiscard]] color focus_color() const noexcept
override
273 if (not _error_label->empty()) {
274 return theme().text_style(semantic_text_style::error)->color;
275 }
else if (*_text_widget->focus) {
276 return theme().color(semantic_color::accent);
289 notifier<>::callback_token _delegate_cbt;
293 std::unique_ptr<scroll_widget<axis::none>> _scroll_widget;
294 box_constraints _scroll_constraints;
295 box_shape _scroll_shape;
299 text_widget *_text_widget =
nullptr;
303 observer<gstring> _text;
307 observer<label> _error_label;
308 std::unique_ptr<label_widget> _error_label_widget;
309 box_constraints _error_label_constraints;
310 box_shape _error_label_shape;
312 typename decltype(
continues)::callback_token _continues_cbt;
313 typename decltype(
text_style)::callback_token _text_style_cbt;
314 typename decltype(_text)::callback_token _text_cbt;
315 typename decltype(_error_label)::callback_token _error_label_cbt;
317 void set_attributes() noexcept {}
318 void set_attributes(text_field_widget_attribute
auto&& first, text_field_widget_attribute
auto&&...rest)
noexcept
320 if constexpr (forward_of<
decltype(first), observer<hi::alignment>>) {
322 }
else if constexpr (forward_of<
decltype(first), observer<hi::semantic_text_style>>) {
325 hi_static_no_default();
328 set_attributes(hi_forward(rest)...);
331 void revert(
bool force)
noexcept
333 hi_assert_not_null(delegate);
334 _text = delegate->text(*
this);
335 _error_label = label{};
337 void commit(
bool force)
noexcept
339 hi_axiom(loop::main().on_thread());
340 hi_assert_not_null(delegate);
345 if (delegate->validate(*
this, text).empty()) {
347 delegate->set_text(*
this, text);
351 _text = delegate->text(*
this);
352 _error_label = label{};
355 void draw_background_box(draw_context
const& context)
const noexcept
357 hilet outline = _scroll_shape.rectangle;
359 hilet corner_radii = hi::corner_radii(0.0f, 0.0f, theme().rounding_radius<float>(), theme().rounding_radius<float>());
360 context.draw_box(
layout(), outline, background_color(), corner_radii);
362 hilet line = line_segment(get<0>(outline), get<1>(outline));
363 context.draw_line(
layout(), translate3{0.0f, 0.5f, 0.1f} * line, theme().border_width(), focus_color());