Finish scroller implementation: add scroll bars & scroll clamping
This commit is contained in:
parent
343894d06b
commit
d233955a10
2 changed files with 255 additions and 10 deletions
|
|
@ -20,6 +20,7 @@ namespace psemek::ui
|
|||
virtual bool vertical_scroll() const { return vertical_; }
|
||||
|
||||
virtual bool on_event(mouse_move const & e);
|
||||
virtual bool on_event(mouse_click const & e);
|
||||
virtual bool on_event(mouse_wheel const & e);
|
||||
|
||||
struct shape const & shape() const override { return shape_; }
|
||||
|
|
@ -34,6 +35,24 @@ namespace psemek::ui
|
|||
|
||||
~scroller() override;
|
||||
|
||||
protected:
|
||||
|
||||
virtual float width() const;
|
||||
virtual geom::box<float, 2> visible_range() const;
|
||||
|
||||
virtual geom::box<float, 2> horizontal_box() const;
|
||||
virtual geom::box<float, 2> vertical_box() const;
|
||||
|
||||
enum state_t
|
||||
{
|
||||
normal,
|
||||
mouseover,
|
||||
mousedown,
|
||||
};
|
||||
|
||||
state_t vertical_state_ = state_t::normal;
|
||||
state_t horizontal_state_ = state_t::normal;
|
||||
|
||||
private:
|
||||
box_shape shape_;
|
||||
std::optional<geom::point<int, 2>> mouse_;
|
||||
|
|
@ -46,6 +65,8 @@ namespace psemek::ui
|
|||
|
||||
std::shared_ptr<element> child_;
|
||||
element * children_[1]{nullptr};
|
||||
|
||||
void clamp_shift();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,127 @@ namespace psemek::ui
|
|||
bool scroller::on_event(mouse_move const & e)
|
||||
{
|
||||
mouse_ = e.position;
|
||||
|
||||
auto pos = geom::cast<float>(e.position);
|
||||
|
||||
if (horizontal_scroll())
|
||||
{
|
||||
auto box = horizontal_box();
|
||||
bool over = geom::contains(box, pos);
|
||||
|
||||
switch (horizontal_state_)
|
||||
{
|
||||
case state_t::normal:
|
||||
if (over)
|
||||
horizontal_state_ = state_t::mouseover;
|
||||
break;
|
||||
case state_t::mouseover:
|
||||
if (!over)
|
||||
horizontal_state_ = state_t::normal;
|
||||
break;
|
||||
case state_t::mousedown:
|
||||
{
|
||||
if (!child_)
|
||||
return false;
|
||||
|
||||
auto sc = child_->size_constraints();
|
||||
|
||||
float c = geom::unlerp<float>(box[0], e.position[0]);
|
||||
c = geom::clamp(c, {0.f, 1.f});
|
||||
|
||||
shift_tgt_[0] = shape_.box[0].length() / 2.f - c * sc[0].min;
|
||||
clamp_shift();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertical_scroll())
|
||||
{
|
||||
auto box = vertical_box();
|
||||
bool over = geom::contains(box, pos);
|
||||
|
||||
switch (vertical_state_)
|
||||
{
|
||||
case state_t::normal:
|
||||
if (over)
|
||||
vertical_state_ = state_t::mouseover;
|
||||
break;
|
||||
case state_t::mouseover:
|
||||
if (!over)
|
||||
vertical_state_ = state_t::normal;
|
||||
break;
|
||||
case state_t::mousedown:
|
||||
{
|
||||
if (!child_)
|
||||
return false;
|
||||
|
||||
auto sc = child_->size_constraints();
|
||||
|
||||
float c = geom::unlerp<float>(box[1], e.position[1]);
|
||||
c = geom::clamp(c, {0.f, 1.f});
|
||||
|
||||
shift_tgt_[1] = shape_.box[1].length() / 2.f - c * sc[1].min;
|
||||
clamp_shift();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool scroller::on_event(mouse_click const & e)
|
||||
{
|
||||
if (e.button != mouse_button::left)
|
||||
return false;
|
||||
|
||||
if (horizontal_scroll())
|
||||
{
|
||||
switch (horizontal_state_)
|
||||
{
|
||||
case state_t::normal:
|
||||
return false;
|
||||
case state_t::mouseover:
|
||||
if (e.down)
|
||||
{
|
||||
horizontal_state_ = state_t::mousedown;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case state_t::mousedown:
|
||||
if (!e.down)
|
||||
{
|
||||
horizontal_state_ = state_t::mouseover;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertical_scroll())
|
||||
{
|
||||
switch (vertical_state_)
|
||||
{
|
||||
case state_t::normal:
|
||||
return false;
|
||||
case state_t::mouseover:
|
||||
if (e.down)
|
||||
{
|
||||
vertical_state_ = state_t::mousedown;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case state_t::mousedown:
|
||||
if (!e.down)
|
||||
{
|
||||
vertical_state_ = state_t::mouseover;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -39,18 +160,39 @@ namespace psemek::ui
|
|||
{
|
||||
if (mouse_ && geom::contains(shape_.box, geom::cast<float>(*mouse_)))
|
||||
{
|
||||
if (vertical_scroll())
|
||||
geom::vector<float, 2> delta = {0.f, 0.f};
|
||||
|
||||
if (vertical_scroll() && !horizontal_scroll())
|
||||
{
|
||||
shift_tgt_[1] += e.delta * 50.f;
|
||||
post_reshape();
|
||||
return true;
|
||||
delta[1] = e.delta;
|
||||
}
|
||||
else if (horizontal_scroll())
|
||||
else if (!vertical_scroll() && horizontal_scroll())
|
||||
{
|
||||
shift_tgt_[0] += e.delta * 50.f;
|
||||
post_reshape();
|
||||
return true;
|
||||
delta[0] = e.delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pos = geom::cast<float>(*mouse_);
|
||||
|
||||
if (horizontal_scroll())
|
||||
{
|
||||
if (geom::contains(horizontal_box(), pos))
|
||||
{
|
||||
delta[0] = e.delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertical_scroll())
|
||||
{
|
||||
if (geom::contains(vertical_box(), pos))
|
||||
delta[1] = e.delta;
|
||||
}
|
||||
}
|
||||
|
||||
shift_tgt_ += delta * 50.f;
|
||||
clamp_shift();
|
||||
post_reshape();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -59,12 +201,19 @@ namespace psemek::ui
|
|||
{
|
||||
shape_.box = bbox;
|
||||
|
||||
auto st = merged_own_style();
|
||||
|
||||
if (child_)
|
||||
{
|
||||
auto child_constraints = child_->size_constraints();
|
||||
|
||||
auto child_bbox = bbox;
|
||||
|
||||
if (horizontal_scroll())
|
||||
child_bbox[1].max -= width() * *st->scale;
|
||||
if (vertical_scroll())
|
||||
child_bbox[0].max -= width() * *st->scale;
|
||||
|
||||
if (horizontal_scroll())
|
||||
{
|
||||
child_bbox[0].min += shift_[0];
|
||||
|
|
@ -98,19 +247,35 @@ namespace psemek::ui
|
|||
result[1] = child_constraints[1];
|
||||
}
|
||||
|
||||
auto st = merged_own_style();
|
||||
|
||||
if (horizontal_scroll())
|
||||
result[1].min = std::max<float>(result[1].min, width() * *st->scale);
|
||||
if (vertical_scroll())
|
||||
result[0].min = std::max<float>(result[0].min, width() * *st->scale);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void scroller::update(float dt)
|
||||
{
|
||||
shift_ += (shift_tgt_ - shift_) * std::min(10.f * dt, 1.f);
|
||||
shift_ += (shift_tgt_ - shift_) * std::min(25.f * dt, 1.f);
|
||||
post_reshape();
|
||||
}
|
||||
|
||||
void scroller::draw(painter & p) const
|
||||
{
|
||||
auto st = merged_own_style();
|
||||
float w = width() * *st->scale;
|
||||
|
||||
auto box = shape_.box;
|
||||
if (horizontal_scroll())
|
||||
box[1].max -= w;
|
||||
if (vertical_scroll())
|
||||
box[0].max -= w;
|
||||
|
||||
p.begin_stencil();
|
||||
p.draw_rect(shape_.box, {0, 0, 0, 255});
|
||||
p.draw_rect(box, {0, 0, 0, 255});
|
||||
p.commit_stencil();
|
||||
}
|
||||
|
||||
|
|
@ -124,4 +289,63 @@ namespace psemek::ui
|
|||
release_children();
|
||||
}
|
||||
|
||||
float scroller::width() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
geom::box<float, 2> scroller::visible_range() const
|
||||
{
|
||||
if (!child_)
|
||||
return {{{0.f, 1.f}, {0.f, 1.f}}};
|
||||
|
||||
geom::box<float, 2> result;
|
||||
auto c = child_->size_constraints();
|
||||
result[0] = {- shift_[0] / c[0].min, (shape_.box[0].length() - shift_[0]) / c[0].min};
|
||||
result[1] = {- shift_[1] / c[1].min, (shape_.box[1].length() - shift_[1]) / c[1].min};
|
||||
|
||||
result[0].max = std::min(result[0].max, 1.f);
|
||||
result[1].max = std::min(result[1].max, 1.f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
geom::box<float, 2> scroller::horizontal_box() const
|
||||
{
|
||||
auto box = shape_.box;
|
||||
|
||||
float w = width() * *merged_own_style()->scale;
|
||||
|
||||
box[1].min = box[1].max - w;
|
||||
if (vertical_scroll())
|
||||
box[0].max -= w;
|
||||
return box;
|
||||
}
|
||||
|
||||
geom::box<float, 2> scroller::vertical_box() const
|
||||
{
|
||||
auto box = shape_.box;
|
||||
|
||||
float w = width() * *merged_own_style()->scale;
|
||||
|
||||
box[0].min = box[0].max - w;
|
||||
if (horizontal_scroll())
|
||||
box[1].max -= w;
|
||||
return box;
|
||||
}
|
||||
|
||||
void scroller::clamp_shift()
|
||||
{
|
||||
if (!child_)
|
||||
return;
|
||||
|
||||
auto c = child_->size_constraints();
|
||||
|
||||
shift_tgt_[0] = std::max(shift_tgt_[0], shape_.box[0].length() - c[0].min);
|
||||
shift_tgt_[0] = std::min(shift_tgt_[0], 0.f);
|
||||
|
||||
shift_tgt_[1] = std::max(shift_tgt_[1], shape_.box[1].length() - c[1].min);
|
||||
shift_tgt_[1] = std::min(shift_tgt_[1], 0.f);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue