| @@ -17,6 +17,7 @@ struct ScrollBar : widget::OpaqueWidget { | |||||
| ScrollBar(); | ScrollBar(); | ||||
| ~ScrollBar(); | ~ScrollBar(); | ||||
| void draw(const DrawArgs& args) override; | void draw(const DrawArgs& args) override; | ||||
| void onButton(const event::Button& e) override; | |||||
| void onDragStart(const event::DragStart& e) override; | void onDragStart(const event::DragStart& e) override; | ||||
| void onDragEnd(const event::DragEnd& e) override; | void onDragEnd(const event::DragEnd& e) override; | ||||
| void onDragMove(const event::DragMove& e) override; | void onDragMove(const event::DragMove& e) override; | ||||
| @@ -22,6 +22,12 @@ struct ScrollWidget : widget::OpaqueWidget { | |||||
| ScrollWidget(); | ScrollWidget(); | ||||
| void scrollTo(math::Rect r); | 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 draw(const DrawArgs& args) override; | ||||
| void step() override; | void step() override; | ||||
| void onButton(const event::Button& e) override; | void onButton(const event::Button& e) override; | ||||
| @@ -30,26 +30,40 @@ void ScrollBar::draw(const DrawArgs& args) { | |||||
| if (APP->event->getDraggedWidget() == this) | if (APP->event->getDraggedWidget() == this) | ||||
| state = BND_ACTIVE; | 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) { | 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); | ScrollWidget* sw = dynamic_cast<ScrollWidget*>(parent); | ||||
| assert(sw); | 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; | 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) { | void ScrollWidget::draw(const DrawArgs& args) { | ||||
| nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
| Widget::draw(args); | Widget::draw(args); | ||||
| @@ -41,10 +59,11 @@ void ScrollWidget::draw(const DrawArgs& args) { | |||||
| void ScrollWidget::step() { | void ScrollWidget::step() { | ||||
| Widget::step(); | Widget::step(); | ||||
| // Clamp scroll offset | |||||
| // Set containerBox cache | |||||
| containerBox = container->getChildrenBoundingBox(); | 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); | offset = offset.clamp(offsetBounds); | ||||
| // Update the container's position from the offset | // Update the container's position from the offset | ||||
| @@ -69,7 +88,7 @@ void ScrollWidget::onButton(const event::Button& e) { | |||||
| return; | return; | ||||
| // Consume right button only if the scrollbars are visible | // Consume right button only if the scrollbars are visible | ||||
| if (!(horizontalScrollBar->visible || verticalScrollBar->visible)) | |||||
| if (!(horizontalScrollBar->isVisible() || verticalScrollBar->isVisible())) | |||||
| return; | return; | ||||
| if (e.button == GLFW_MOUSE_BUTTON_MIDDLE) { | 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) { | void ScrollWidget::onDragMove(const event::DragMove& e) { | ||||
| // Scroll only if the scrollbars are visible | // Scroll only if the scrollbars are visible | ||||
| if (!(horizontalScrollBar->visible || verticalScrollBar->visible)) | |||||
| if (!(horizontalScrollBar->isVisible() || verticalScrollBar->isVisible())) | |||||
| return; | return; | ||||
| offset = offset.minus(e.mouseDelta); | offset = offset.minus(e.mouseDelta); | ||||
| @@ -100,7 +119,7 @@ void ScrollWidget::onHoverScroll(const event::HoverScroll& e) { | |||||
| return; | return; | ||||
| // Scroll only if the scrollbars are visible | // Scroll only if the scrollbars are visible | ||||
| if (!(horizontalScrollBar->visible || verticalScrollBar->visible)) | |||||
| if (!(horizontalScrollBar->isVisible() || verticalScrollBar->isVisible())) | |||||
| return; | return; | ||||
| math::Vec scrollDelta = e.scrollDelta; | math::Vec scrollDelta = e.scrollDelta; | ||||