Skip to content

ResizeGripper

Header: winui/ui/panes/ResizeGripper.hpp. Spec: winui/ui/panes/ResizeGripper.hpp.

A fixed-size corner drag handle that reports two-axis deltas while dragged.

It draws three diagonal rows of dots anchored to its bottom-right corner, shows the diagonal resize cursor, and measures to a size() square whatever it is offered. It resizes nothing itself. The app applies each (dx, dy) to whatever should grow, typically a Grid row and column or an element's explicit size. It works with the mouse only and never takes focus. For a single-axis pane splitter use Splitter. ui::ResizeGripper is the declarative form.

Example:

auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
ui::TextControl* box = stack->add(std::make_unique<ui::TextControl>("Resize me"));
box->set_width(200.0f);
box->set_height(120.0f);

ui::ResizeGripperControl* grip = stack->add(std::make_unique<ui::ResizeGripperControl>());
grip->set_horizontal_alignment(ui::Alignment::Trailing);
grip->on_drag_delta([box](ui::Point delta) {
    box->set_width(std::max(40.0f, box->width().value_or(0.0f) + delta.x));
    box->set_height(std::max(24.0f, box->height().value_or(0.0f) + delta.y));
});
window.set_content(std::move(stack));

ui::ResizeGripper

A fixed-size corner drag handle. Builds a ui::ResizeGripperControl. .on_drag_delta receives the (dx, dy) movement since its previous call. A caller typically passes it on to GridControl::set_row_height() and set_column_width() on a grid it holds through .control. ui::Splitter is the single-axis version. It works with the mouse only and takes no focus.

Example:

using ui::operator""_dip;

ui::Control<ui::Grid> grid;

window.set_content(ui::Grid{
    .columns = {200_dip, ui::AutoSize},
    .rows    = {120_dip, ui::AutoSize},
    .control = grid,
    .content = {
        ui::Cell{.row = 0, .column = 0, .content = ui::Button{.text = "Resizable"}},
        ui::Cell{.row     = 1,
                 .column  = 1,
                 .content = ui::ResizeGripper{.on_drag_delta = [&](ui::Point delta)
                 {
                     grid->set_column_width(0, std::max(40.0f, grid->column_size(0) + delta.x));
                     grid->set_row_height(0, std::max(24.0f, grid->row_size(0) + delta.y));
                 }}},
    },
});

Every field, in the order a designated initializer names them:

ui::ResizeGripper{
    .size, .style, .margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
    .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility,
    .on_drag_delta, .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus,
    .on_key_down, .on_wheel, .control
}

Fields, in declaration order (designators must follow it):

Field Type Default
size ui::Binding<float> {16.0f} The square's edge length, DIPs.
on_drag_delta ui::Callback<void(ui::Point)> The (dx, dy) drag since the last call, DIPs; positive is right/down.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::ResizeGripper> Out: the handle that receives the built control.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::ResizeGripperControl; the caller owns it.

ui::ResizeGripperControl

Derives from ui::Element.

A fixed-size corner drag handle that reports two-axis deltas while dragged.

It draws three diagonal rows of dots anchored to its bottom-right corner, shows the diagonal resize cursor, and measures to a size() square whatever it is offered. It resizes nothing itself. The app applies each (dx, dy) to whatever should grow, typically a Grid row and column or an element's explicit size. It works with the mouse only and never takes focus. For a single-axis pane splitter use Splitter. ui::ResizeGripper is the declarative form.

Example:

auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
ui::TextControl* box = stack->add(std::make_unique<ui::TextControl>("Resize me"));
box->set_width(200.0f);
box->set_height(120.0f);

ui::ResizeGripperControl* grip = stack->add(std::make_unique<ui::ResizeGripperControl>());
grip->set_horizontal_alignment(ui::Alignment::Trailing);
grip->on_drag_delta([box](ui::Point delta) {
    box->set_width(std::max(40.0f, box->width().value_or(0.0f) + delta.x));
    box->set_height(std::max(24.0f, box->height().value_or(0.0f) + delta.y));
});
window.set_content(std::move(stack));

ResizeGripperControl

ResizeGripperControl() = default

A 16 DIP gripper.

size

float size() const

The square's edge length in DIPs; 16 by default.

set_size

void set_size(float size)

Sets the edge length in DIPs and lays the gripper out again.

on_drag_delta

void on_drag_delta(Callback<void(Point delta)> handler)

Fired while the pointer drags, with the (dx, dy) movement since the previous call. Works like SplitterControl::on_drag_delta, on both axes at once.

dragging

bool dragging() const

Whether a left-button drag is in progress. The dots draw at hover strength meanwhile.

hovered

bool hovered() const

Whether the pointer is over the gripper.

cursor_at

std::optional<CursorShape> cursor_at(Point) const override

The diagonal resize arrow, or none while disabled.

automation_role

AutomationRole automation_role() const override

Reported to screen readers as a Thumb.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

size() x size(), regardless of available. The gripper is never focusable and has no keyboard resize.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

Draws a triangle of dots (rows of one, two and three) in the corner, in the text color at reduced alpha.

pointer_override

bool pointer_override(const PointerEvent& e) override

A left press inside starts the drag. Each move then fires on_drag_delta() until the button is released.

enabled_changed

void enabled_changed() override

is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.

Tests

  • tests/test_resize_gripper.cpp