@@ -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; | |||
@@ -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; | |||
@@ -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; | |||
} | |||
@@ -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; | |||