Browse Source

Make scrollbar mouse interaction similar to modern OS behavior

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
2d253fe2fb
4 changed files with 65 additions and 24 deletions
  1. +1
    -0
      include/ui/ScrollBar.hpp
  2. +6
    -0
      include/ui/ScrollWidget.hpp
  3. +33
    -18
      src/ui/ScrollBar.cpp
  4. +25
    -6
      src/ui/ScrollWidget.cpp

+ 1
- 0
include/ui/ScrollBar.hpp View File

@@ -17,6 +17,7 @@ struct ScrollBar : widget::OpaqueWidget {
ScrollBar();
~ScrollBar();
void draw(const DrawArgs& args) override;
void onButton(const event::Button& e) override;
void onDragStart(const event::DragStart& e) override;
void onDragEnd(const event::DragEnd& e) override;
void onDragMove(const event::DragMove& e) override;


+ 6
- 0
include/ui/ScrollWidget.hpp View File

@@ -22,6 +22,12 @@ struct ScrollWidget : widget::OpaqueWidget {

ScrollWidget();
void scrollTo(math::Rect r);
/** Returns the bound of allowed `offset` values in pixels. */
math::Rect getContainerOffsetBound();
/** Returns the handle position relative to the scrollbar. [0, 1]. */
math::Vec getHandleOffset();
/** Returns the handle size relative to the scrollbar. [0, 1]. */
math::Vec getHandleSize();
void draw(const DrawArgs& args) override;
void step() override;
void onButton(const event::Button& e) override;


+ 33
- 18
src/ui/ScrollBar.cpp View File

@@ -30,26 +30,40 @@ void ScrollBar::draw(const DrawArgs& args) {
if (APP->event->getDraggedWidget() == this)
state = BND_ACTIVE;

float offsetBound = sw->containerBox.size.get(vertical) - sw->box.size.get(vertical);
// The handle position relative to the scrollbar. [0, 1]
float scrollBarOffset = (sw->offset.get(vertical) - sw->containerBox.pos.get(vertical)) / offsetBound;
// The handle size relative to the scrollbar. [0, 1]
float scrollBarSize = sw->box.size.get(vertical) / sw->containerBox.size.get(vertical);
bndScrollBar(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, scrollBarOffset, scrollBarSize);
float handleOffset = sw->getHandleOffset().get(vertical);
float handleSize = sw->getHandleSize().get(vertical);
bndScrollBar(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, handleOffset, handleSize);
}


void ScrollBar::onDragStart(const event::DragStart& e) {
if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
APP->window->cursorLock();
void ScrollBar::onButton(const event::Button& e) {
if (e.button == GLFW_MOUSE_BUTTON_LEFT && e.action == GLFW_PRESS) {
ScrollWidget* sw = dynamic_cast<ScrollWidget*>(parent);
assert(sw);

float pos = e.pos.get(vertical);
pos /= box.size.get(vertical);
float handleOffset = sw->getHandleOffset().get(vertical);
float handleSize = sw->getHandleSize().get(vertical);
float handlePos = math::rescale(handleOffset, 0.f, 1.f, handleSize / 2.f, 1.f - handleSize / 2.f);
math::Rect offsetBound = sw->getContainerOffsetBound();

// Check if user clicked on handle
if (std::fabs(pos - handlePos) > handleSize / 2.f) {
// Jump to absolute position of the handle
float offset = math::rescale(pos, handleSize / 2.f, 1.f - handleSize / 2.f, 0.f, 1.f);
sw->offset.get(vertical) = sw->containerBox.pos.get(vertical) + offset * (sw->containerBox.size.get(vertical) - sw->box.size.get(vertical));
}
}
OpaqueWidget::onButton(e);
}


void ScrollBar::onDragStart(const event::DragStart& e) {
}


void ScrollBar::onDragEnd(const event::DragEnd& e) {
if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
APP->window->cursorUnlock();
}
}


@@ -57,13 +71,14 @@ void ScrollBar::onDragMove(const event::DragMove& e) {
ScrollWidget* sw = dynamic_cast<ScrollWidget*>(parent);
assert(sw);

// TODO
// float offsetBound = sw->containerBox.size.get(vertical) - sw->box.size.get(vertical);
// float scrollBarSize = sw->box.size.get(vertical) / sw->containerBox.size.get(vertical);
// Move handle absolutely.
float mouseDelta = e.mouseDelta.get(vertical);
mouseDelta /= getAbsoluteZoom();

const float sensitivity = 1.f;
float offsetDelta = e.mouseDelta.get(vertical);
offsetDelta *= sensitivity;
float handleSize = sw->getHandleSize().get(vertical);
float handleBound = (1.f - handleSize) * box.size.get(vertical);
float offsetBound = sw->getContainerOffsetBound().size.get(vertical);
float offsetDelta = mouseDelta * offsetBound / handleBound;
sw->offset.get(vertical) += offsetDelta;
}



+ 25
- 6
src/ui/ScrollWidget.cpp View File

@@ -31,6 +31,24 @@ void ScrollWidget::scrollTo(math::Rect r) {
}


math::Rect ScrollWidget::getContainerOffsetBound() {
math::Rect r;
r.pos = containerBox.pos;
r.size = containerBox.size.minus(box.size);
return r;
}


math::Vec ScrollWidget::getHandleOffset() {
return offset.minus(containerBox.pos).div(getContainerOffsetBound().size);
}


math::Vec ScrollWidget::getHandleSize() {
return box.size.div(containerBox.size);
}


void ScrollWidget::draw(const DrawArgs& args) {
nvgScissor(args.vg, RECT_ARGS(args.clipBox));
Widget::draw(args);
@@ -41,10 +59,11 @@ void ScrollWidget::draw(const DrawArgs& args) {
void ScrollWidget::step() {
Widget::step();

// Clamp scroll offset
// Set containerBox cache
containerBox = container->getChildrenBoundingBox();
math::Rect offsetBounds = containerBox;
offsetBounds.size = offsetBounds.size.minus(box.size);

// Clamp scroll offset
math::Rect offsetBounds = getContainerOffsetBound();
offset = offset.clamp(offsetBounds);

// Update the container's position from the offset
@@ -69,7 +88,7 @@ void ScrollWidget::onButton(const event::Button& e) {
return;

// Consume right button only if the scrollbars are visible
if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
if (!(horizontalScrollBar->isVisible() || verticalScrollBar->isVisible()))
return;

if (e.button == GLFW_MOUSE_BUTTON_MIDDLE) {
@@ -87,7 +106,7 @@ void ScrollWidget::onDragStart(const event::DragStart& e) {

void ScrollWidget::onDragMove(const event::DragMove& e) {
// Scroll only if the scrollbars are visible
if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
if (!(horizontalScrollBar->isVisible() || verticalScrollBar->isVisible()))
return;

offset = offset.minus(e.mouseDelta);
@@ -100,7 +119,7 @@ void ScrollWidget::onHoverScroll(const event::HoverScroll& e) {
return;

// Scroll only if the scrollbars are visible
if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
if (!(horizontalScrollBar->isVisible() || verticalScrollBar->isVisible()))
return;

math::Vec scrollDelta = e.scrollDelta;


Loading…
Cancel
Save