genode/repos/demo/src/app/scout/scrollbar.cc

331 lines
7.6 KiB
C++

/*
* \brief Scrollbar implementation
* \date 2005-10-24
* \author Norman Feske <norman.feske@genode-labs.com>
*/
/*
* Copyright (C) 2005-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <scout/tick.h>
#include "scrollbar.h"
using namespace Scout;
#define SLIDER_RGBA _binary_slider_rgba_start
#define UPARROW_RGBA _binary_uparrow_rgba_start
#define DNARROW_RGBA _binary_downarrow_rgba_start
extern unsigned char SLIDER_RGBA[];
extern unsigned char UPARROW_RGBA[];
extern unsigned char DNARROW_RGBA[];
/********************
** Event handlers **
********************/
template <typename PT>
class Arrow_event_handler : public Event_handler, public Tick
{
private:
/*
* Noncopyable
*/
Arrow_event_handler(Arrow_event_handler const &);
Arrow_event_handler &operator = (Arrow_event_handler const &);
/**
* Constants
*/
static const int _max_speed = 16*256;
Scrollbar<PT> *_sb;
Fade_icon<PT, 32, 32> *_icon;
unsigned char *_rgba;
int _direction;
int _curr_speed = 0;
int _dst_speed = 0;
int _view_pos = 0;
int _accel = 1;
public:
/**
* Constructor
*/
Arrow_event_handler(Scrollbar<PT> *sb,
Fade_icon<PT, 32, 32> *icon,
int direction,
unsigned char *rgba)
:
_sb(sb), _icon(icon), _rgba(rgba), _direction(direction)
{ }
/**
* Event handler interface
*/
void handle_event(Event const &ev) override
{
static int key_cnt;
if (ev.type == Event::PRESS) key_cnt++;
if (ev.type == Event::RELEASE) key_cnt--;
/* start movement with zero speed */
if ((ev.type == Event::PRESS) && (key_cnt == 1)) {
/* press icon (slight vertical shift, darker shadow) */
_icon->rgba(_rgba, 1, 3);
_icon->refresh();
_curr_speed = _direction*256;
_dst_speed = _direction*_max_speed;
_accel = 16;
_view_pos = _sb->view_pos() << 8;
schedule(10);
}
if ((ev.type == Event::RELEASE) && (key_cnt == 0)) {
/* release icon */
_icon->rgba(_rgba);
_icon->refresh();
_accel = 64;
_dst_speed = 0;
}
}
/**
* Tick interface
*/
int on_tick() override
{
/* accelerate */
if (_curr_speed < _dst_speed)
_curr_speed = min(_curr_speed + _accel, _dst_speed);
/* decelerate */
if (_curr_speed > _dst_speed)
_curr_speed = max(_curr_speed - _accel, _dst_speed);
/* soft stopping on boundaries */
while ((_curr_speed < 0) && (_view_pos > 0)
&& (_curr_speed*_curr_speed > _view_pos*_accel*4))
_curr_speed = min(0, _curr_speed + _accel*4);
int max_pos;
while ((_curr_speed > 0)
&& ((max_pos = (_sb->real_size() - _sb->view_size())*256 - _view_pos) > 0)
&& (_curr_speed*_curr_speed > max_pos*_accel*4))
_curr_speed = max(0, _curr_speed - _accel*4);
/* move view position with current speed */
_view_pos = max(0, _view_pos + _curr_speed);
/* set new view position */
int old_view_pos = _sb->view_pos();
_sb->view(_sb->real_size(), _sb->view_size(), _view_pos>>8);
if (old_view_pos != _sb->view_pos())
_sb->notify_listener();
/* keep ticking as long as we are on speed */
return (_curr_speed != 0);
}
};
template <typename PT>
class Slider_event_handler : public Event_handler
{
private:
/*
* Noncopyable
*/
Slider_event_handler(Slider_event_handler const &);
Slider_event_handler &operator = (Slider_event_handler const &);
Scrollbar<PT> *_sb;
Fade_icon<PT, 32, 32> *_icon;
unsigned char *_rgba;
public:
/**
* Constructor
*/
Slider_event_handler(Scrollbar<PT> *sb,
Fade_icon<PT, 32, 32> *icon,
unsigned char *rgba)
:
_sb(sb), _icon(icon), _rgba(rgba)
{ }
/**
* Event handler interface
*/
void handle_event(Event const &ev) override
{
static int key_cnt;
static int curr_my, orig_my;
static int orig_slider_pos;
if (ev.type == Event::PRESS) key_cnt++;
if (ev.type == Event::RELEASE) key_cnt--;
/* start movement with zero speed */
if ((ev.type == Event::PRESS) && (key_cnt == 1)) {
/* press icon (slight vertical shift, darker shadow) */
_icon->rgba(_rgba, 1, 3);
_icon->refresh();
orig_my = curr_my = ev.mouse_position.y();
orig_slider_pos = _sb->slider_pos();
}
if ((ev.type == Event::RELEASE) && (key_cnt == 0)) {
/* release icon */
_icon->rgba(_rgba);
_icon->refresh();
}
if (key_cnt && (ev.mouse_position.y() != curr_my)) {
curr_my = ev.mouse_position.y();
_sb->slider_pos(orig_slider_pos + curr_my - orig_my);
_sb->notify_listener();
}
}
};
/*************************
** Scrollbar interface **
*************************/
template <typename PT>
Scrollbar<PT>::Scrollbar()
{
/* init scrollbar elements */
_slider.rgba(SLIDER_RGBA);
_uparrow.rgba(UPARROW_RGBA);
_dnarrow.rgba(DNARROW_RGBA);
_uparrow.alpha(0);
_dnarrow.alpha(0);
_slider .alpha(0);
append(&_uparrow);
append(&_dnarrow);
append(&_slider);
_min_size = Area(sb_elem_w, sb_elem_h*3);
/* define event handlers for scrollbar elements */
_uparrow.event_handler(new Arrow_event_handler<PT>(this, &_uparrow, -1, UPARROW_RGBA));
_dnarrow.event_handler(new Arrow_event_handler<PT>(this, &_dnarrow, 1, DNARROW_RGBA));
_slider.event_handler(new Slider_event_handler<PT>(this, &_slider, SLIDER_RGBA));
}
template <typename PT>
int Scrollbar<PT>::slider_size()
{
return max((unsigned)sb_elem_h, ((_size.h() - sb_elem_h*2)*_view_size)/_real_size);
}
template <typename PT>
int Scrollbar<PT>::slider_pos()
{
int real_range = _real_size - _view_size;
int slider_range = _size.h() - sb_elem_h*2 - slider_size();
int pos = real_range ? (slider_range*_view_pos)/real_range : 0;
return pos + sb_elem_h;
}
template <typename PT>
void Scrollbar<PT>::slider_pos(int pos)
{
int slider_bg_h = _size.h() - sb_elem_h*2;
_view_pos = ((pos - sb_elem_h)*_real_size)/slider_bg_h;
_view_pos = max(0, min(_view_pos, _real_size - _view_size));
_slider.geometry(Rect(Point(0, slider_pos()), Area(sb_elem_w, slider_size())));
}
template <typename PT>
void Scrollbar<PT>::view(int real_size, int view_size, int view_pos)
{
_real_size = real_size;
_view_size = min(view_size, real_size);
_view_pos = max(0, min(view_pos, _real_size - _view_size));
geometry(Rect(_position, _size));
}
template <typename PT>
void Scrollbar<PT>::notify_listener()
{
if (_listener)
_listener->handle_scroll(_view_pos);
}
/***********************
** Element interface **
***********************/
template <typename PT>
void Scrollbar<PT>::geometry(Rect rect)
{
Element::geometry(rect);
int new_visibility = _visible();
if (new_visibility) {
_uparrow.geometry(Rect(Point(0, 0), Area(sb_elem_w, sb_elem_h)));
_dnarrow.geometry(Rect(Point(0, rect.h() - sb_elem_h),
Area(sb_elem_w, sb_elem_h)));
_slider. geometry(Rect(Point(0, slider_pos()),
Area(sb_elem_w, slider_size())));
}
if (_visibility ^ new_visibility) {
int alpha = new_visibility ? _uparrow.default_alpha() : 0;
int speed = new_visibility ? 3 : 2;
_uparrow.fade_to(alpha, speed);
_dnarrow.fade_to(alpha, speed);
_slider. fade_to(alpha, speed);
}
_visibility = new_visibility;
}
template <typename PT>
Element *Scrollbar<PT>::find(Point position)
{
if (_visibility)
return Parent_element::find(position);
return 0;
}
template class Scrollbar<Genode::Pixel_rgb565>;