mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-10 03:44:16 +00:00
240 lines
5.6 KiB
C++
240 lines
5.6 KiB
C++
// Aseprite UI Library
|
|
// Copyright (C) 2019-2022 Igara Studio S.A.
|
|
// Copyright (C) 2001-2017 David Capello
|
|
//
|
|
// This file is released under the terms of the MIT license.
|
|
// Read LICENSE.txt for more information.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gfx/size.h"
|
|
#include "ui/message.h"
|
|
#include "ui/paint_event.h"
|
|
#include "ui/scroll_bar.h"
|
|
#include "ui/theme.h"
|
|
|
|
namespace ui {
|
|
|
|
using namespace gfx;
|
|
|
|
// Internal stuff shared by all scroll-bars (as the user cannot move
|
|
// two scroll-bars at the same time).
|
|
int ScrollBar::m_wherepos = 0;
|
|
int ScrollBar::m_whereclick = 0;
|
|
|
|
ScrollBar::ScrollBar(int align, ScrollableViewDelegate* delegate)
|
|
: Widget(kViewScrollbarWidget)
|
|
, m_delegate(delegate)
|
|
, m_thumbStyle(nullptr)
|
|
, m_barWidth(0)
|
|
, m_pos(0)
|
|
, m_size(0)
|
|
{
|
|
setAlign(align);
|
|
initTheme();
|
|
}
|
|
|
|
void ScrollBar::setPos(int pos)
|
|
{
|
|
if (m_pos != pos) {
|
|
m_pos = pos;
|
|
invalidate();
|
|
}
|
|
}
|
|
|
|
void ScrollBar::setSize(int size)
|
|
{
|
|
if (m_size != size) {
|
|
m_size = size;
|
|
invalidate();
|
|
}
|
|
}
|
|
|
|
void ScrollBar::getScrollBarThemeInfo(int* pos, int* len)
|
|
{
|
|
getScrollBarInfo(pos, len, nullptr, nullptr);
|
|
}
|
|
|
|
bool ScrollBar::onProcessMessage(Message* msg)
|
|
{
|
|
#define MOUSE_IN(x1, y1, x2, y2) \
|
|
((mousePos.x >= (x1)) && (mousePos.x <= (x2)) && \
|
|
(mousePos.y >= (y1)) && (mousePos.y <= (y2)))
|
|
|
|
switch (msg->type()) {
|
|
|
|
case kMouseDownMessage: {
|
|
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
|
|
int x1, y1, x2, y2;
|
|
int u1, v1, u2, v2;
|
|
bool ret = false;
|
|
int pos, len;
|
|
|
|
getScrollBarThemeInfo(&pos, &len);
|
|
|
|
m_wherepos = pos;
|
|
m_whereclick = (align() & HORIZONTAL) ?
|
|
mousePos.x:
|
|
mousePos.y;
|
|
|
|
x1 = bounds().x;
|
|
y1 = bounds().y;
|
|
x2 = bounds().x2()-1;
|
|
y2 = bounds().y2()-1;
|
|
|
|
u1 = x1 + border().left();
|
|
v1 = y1 + border().top();
|
|
u2 = x2 - border().right();
|
|
v2 = y2 - border().bottom();
|
|
|
|
Point scroll = m_delegate->viewScroll();
|
|
|
|
if (align() & HORIZONTAL) {
|
|
// in the bar
|
|
if (MOUSE_IN(u1+pos, v1, u1+pos+len-1, v2)) {
|
|
// capture mouse
|
|
}
|
|
// left
|
|
else if (MOUSE_IN(x1, y1, u1+pos-1, y2)) {
|
|
scroll.x -= m_delegate->visibleSize().w/2;
|
|
ret = true;
|
|
}
|
|
// right
|
|
else if (MOUSE_IN(u1+pos+len, y1, x2, y2)) {
|
|
scroll.x += m_delegate->visibleSize().w/2;
|
|
ret = true;
|
|
}
|
|
}
|
|
else {
|
|
// in the bar
|
|
if (MOUSE_IN(u1, v1+pos, u2, v1+pos+len-1)) {
|
|
// capture mouse
|
|
}
|
|
// left
|
|
else if (MOUSE_IN(x1, y1, x2, v1+pos-1)) {
|
|
scroll.y -= m_delegate->visibleSize().h/2;
|
|
ret = true;
|
|
}
|
|
// right
|
|
else if (MOUSE_IN(x1, v1+pos+len, x2, y2)) {
|
|
scroll.y += m_delegate->visibleSize().h/2;
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
m_delegate->setViewScroll(scroll);
|
|
return ret;
|
|
}
|
|
|
|
setSelected(true);
|
|
captureMouse();
|
|
|
|
// Continue to kMouseMoveMessage handler...
|
|
[[fallthrough]];
|
|
}
|
|
|
|
case kMouseMoveMessage:
|
|
if (hasCapture()) {
|
|
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
|
|
int pos, len, bar_size, viewport_size;
|
|
|
|
getScrollBarInfo(&pos, &len, &bar_size, &viewport_size);
|
|
|
|
if (bar_size > len) {
|
|
Point scroll = m_delegate->viewScroll();
|
|
|
|
if (align() & HORIZONTAL) {
|
|
pos = (m_wherepos + mousePos.x - m_whereclick);
|
|
pos = std::clamp(pos, 0, bar_size - len);
|
|
|
|
scroll.x = (m_size - viewport_size) * pos / (bar_size - len);
|
|
}
|
|
else {
|
|
pos = (m_wherepos + mousePos.y - m_whereclick);
|
|
pos = std::clamp(pos, 0, bar_size - len);
|
|
|
|
scroll.y = (m_size - viewport_size) * pos / (bar_size - len);
|
|
}
|
|
|
|
m_delegate->setViewScroll(scroll);
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case kMouseUpMessage:
|
|
setSelected(false);
|
|
releaseMouse();
|
|
break;
|
|
|
|
case kMouseEnterMessage:
|
|
case kMouseLeaveMessage:
|
|
// TODO add something to avoid this (theme specific stuff)
|
|
invalidate();
|
|
break;
|
|
}
|
|
|
|
return Widget::onProcessMessage(msg);
|
|
}
|
|
|
|
void ScrollBar::onInitTheme(InitThemeEvent& ev)
|
|
{
|
|
Widget::onInitTheme(ev);
|
|
m_barWidth = theme()->getScrollbarSize();
|
|
}
|
|
|
|
void ScrollBar::onPaint(PaintEvent& ev)
|
|
{
|
|
gfx::Rect thumbBounds = clientBounds();
|
|
if (align() & HORIZONTAL)
|
|
getScrollBarThemeInfo(&thumbBounds.x, &thumbBounds.w);
|
|
else
|
|
getScrollBarThemeInfo(&thumbBounds.y, &thumbBounds.h);
|
|
|
|
theme()->paintScrollBar(
|
|
ev.graphics(), this, style(), thumbStyle(),
|
|
clientBounds(), thumbBounds);
|
|
}
|
|
|
|
void ScrollBar::getScrollBarInfo(int *_pos, int *_len, int *_bar_size, int *_viewport_size)
|
|
{
|
|
int bar_size, viewport_size;
|
|
int pos, len;
|
|
int border_width;
|
|
|
|
if (align() & HORIZONTAL) {
|
|
bar_size = bounds().w;
|
|
viewport_size = m_delegate->visibleSize().w;
|
|
border_width = border().height();
|
|
}
|
|
else {
|
|
bar_size = bounds().h;
|
|
viewport_size = m_delegate->visibleSize().h;
|
|
border_width = border().width();
|
|
}
|
|
|
|
if (m_size <= viewport_size) {
|
|
len = bar_size;
|
|
pos = 0;
|
|
}
|
|
else if (m_size > 0) {
|
|
len = bar_size * viewport_size / m_size;
|
|
len = std::clamp(len, std::min(theme()->getScrollbarSize()*2-border_width, bar_size), bar_size);
|
|
pos = (bar_size-len) * m_pos / (m_size-viewport_size);
|
|
pos = std::clamp(pos, 0, bar_size-len);
|
|
}
|
|
else {
|
|
len = pos = 0;
|
|
}
|
|
|
|
if (_pos) *_pos = pos;
|
|
if (_len) *_len = len;
|
|
if (_bar_size) *_bar_size = bar_size;
|
|
if (_viewport_size) *_viewport_size = viewport_size;
|
|
}
|
|
|
|
} // namespace ui
|