using namespace Gtk;
using namespace std;
-#define CORNER_RADIUS 4
+#define CORNER_RADIUS 2.5
#define CORNER_SIZE 2
#define CORNER_OFFSET 1
-#define FADER_RESERVE 5
+#define FADER_RESERVE 6
std::list<PixFader::FaderImage*> PixFader::_patterns;
PixFader::PixFader (Gtk::Adjustment& adj, int orientation, int fader_length, int fader_girth)
- : adjustment (adj)
+ : _layout (0)
+ , _tweaks (Tweaks(0))
+ , _adjustment (adj)
+ , _text_width (0)
+ , _text_height (0)
, _span (fader_length)
, _girth (fader_girth)
+ , _min_span (fader_length)
+ , _min_girth (fader_girth)
, _orien (orientation)
, _pattern (0)
, _hovering (false)
- , _last_drawn (-1)
, _dragging (false)
, _centered_text (true)
, _current_parent (0)
{
- _default_value = adjustment.get_value();
+ _default_value = _adjustment.get_value();
update_unity_position ();
add_events (
| Gdk::LEAVE_NOTIFY_MASK
);
- adjustment.signal_value_changed().connect (mem_fun (*this, &PixFader::adjustment_changed));
- adjustment.signal_changed().connect (mem_fun (*this, &PixFader::adjustment_changed));
-
+ _adjustment.signal_value_changed().connect (mem_fun (*this, &PixFader::adjustment_changed));
+ _adjustment.signal_changed().connect (mem_fun (*this, &PixFader::adjustment_changed));
+ signal_grab_broken_event ().connect (mem_fun (*this, &PixFader::on_grab_broken_event));
if (_orien == VERT) {
- DrawingArea::set_size_request(_girth, _span);
+ CairoWidget::set_size_request(_girth, _span);
} else {
- DrawingArea::set_size_request(_span, _girth);
+ CairoWidget::set_size_request(_span, _girth);
}
}
if (_layout) _layout.clear (); // drop reference to existing layout
}
+void
+PixFader::flush_pattern_cache () {
+ for (list<FaderImage*>::iterator f = _patterns.begin(); f != _patterns.end(); ++f) {
+ cairo_pattern_destroy ((*f)->pattern);
+ }
+ _patterns.clear();
+}
+
+
cairo_pattern_t*
PixFader::find_pattern (double afr, double afg, double afb,
double abr, double abg, double abb,
/* paint background + border */
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, get_width(), 0);
- cairo_pattern_add_color_stop_rgba (shade_pattern, 0, br*0.8,bg*0.8,bb*0.8, 1.0);
- cairo_pattern_add_color_stop_rgba (shade_pattern, 1, br*0.6,bg*0.6,bb*0.6, 1.0);
+ cairo_pattern_add_color_stop_rgba (shade_pattern, 0, br*0.4,bg*0.4,bb*0.4, 1.0);
+ cairo_pattern_add_color_stop_rgba (shade_pattern, 0.25, br*0.6,bg*0.6,bb*0.6, 1.0);
+ cairo_pattern_add_color_stop_rgba (shade_pattern, 1, br*0.8,bg*0.8,bb*0.8, 1.0);
cairo_set_source (tc, shade_pattern);
cairo_rectangle (tc, 0, 0, get_width(), get_height() * 2.0);
cairo_fill (tc);
/* paint right shade (background section)*/
cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height());
- cairo_pattern_add_color_stop_rgba (shade_pattern, 0, br*0.8,bg*0.8,bb*0.8, 1.0);
- cairo_pattern_add_color_stop_rgba (shade_pattern, 1, br*0.6,bg*0.6,bb*0.6, 1.0);
+ cairo_pattern_add_color_stop_rgba (shade_pattern, 0, br*0.4,bg*0.4,bb*0.4, 1.0);
+ cairo_pattern_add_color_stop_rgba (shade_pattern, 0.25, br*0.6,bg*0.6,bb*0.6, 1.0);
+ cairo_pattern_add_color_stop_rgba (shade_pattern, 1, br*0.8,bg*0.8,bb*0.8, 1.0);
cairo_set_source (tc, shade_pattern);
cairo_rectangle (tc, 0, 0, get_width() * 2.0, get_height());
cairo_fill (tc);
cairo_surface_destroy (surface);
}
-bool
-PixFader::on_expose_event (GdkEventExpose* ev)
+void
+PixFader::render (cairo_t *cr, cairo_rectangle_t* area)
{
- Cairo::RefPtr<Cairo::Context> context = get_window()->create_cairo_context();
- cairo_t* cr = context->cobj();
-
- // clip to expose area
- cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
- cairo_clip (cr);
-
if (!_pattern) {
create_patterns();
}
*/
CairoWidget::set_source_rgb_a (cr, get_style()->get_bg (get_state()), 1);
- cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+ cairo_rectangle (cr, area->x, area->y, area->width, area->height);
cairo_fill (cr);
- return true;
+ return;
}
OnExpose();
cairo_rectangle (cr, 0, 0, w, h);
cairo_fill(cr);
- cairo_set_line_width (cr, 1);
+ cairo_set_line_width (cr, 2);
cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
cairo_matrix_t matrix;
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET, w-CORNER_SIZE, h-CORNER_SIZE, CORNER_RADIUS);
- // we use a 'trick' here: The stoke is off by .5px but filling the interiour area
- // results in an outline of 0.5 px the actual outline is exatly 1px at 50% alpha.
+ // we use a 'trick' here: The stoke is off by .5px but filling the interior area
+ // after a stroke of 2px width results in an outline of 1px
cairo_stroke_preserve(cr);
if (_orien == VERT) {
cairo_fill (cr);
CairoWidget::set_source_rgb_a (cr, get_style()->get_fg (get_state()), 1);
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET,
- ds - CORNER_OFFSET, h - CORNER_SIZE, CORNER_RADIUS);
+ ds - CORNER_SIZE, h - CORNER_SIZE, CORNER_RADIUS);
}
cairo_fill (cr);
}
/* draw the unity-position line if it's not at either end*/
- if (_unity_loc > 0) {
- context->set_line_width (1);
- context->set_line_cap (Cairo::LINE_CAP_ROUND);
+ if (!(_tweaks & NoShowUnityLine) && _unity_loc > CORNER_RADIUS) {
+ cairo_set_line_width(cr, 1);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
Gdk::Color c = get_style()->get_fg (Gtk::STATE_ACTIVE);
- context->set_source_rgba (c.get_red_p()*1.5, c.get_green_p()*1.5, c.get_blue_p()*1.5, 0.85);
- if ( _orien == VERT) {
- if (_unity_loc < h ) {
- context->move_to (1.5, _unity_loc + CORNER_OFFSET + .5);
- context->line_to (_girth - 1.5, _unity_loc + CORNER_OFFSET + .5);
- context->stroke ();
+ cairo_set_source_rgba (cr, c.get_red_p() * 1.5, c.get_green_p() * 1.5, c.get_blue_p() * 1.5, 0.85);
+ if (_orien == VERT) {
+ if (_unity_loc < h - CORNER_RADIUS) {
+ cairo_move_to (cr, 1.5, _unity_loc + CORNER_OFFSET + .5);
+ cairo_line_to (cr, _girth - 1.5, _unity_loc + CORNER_OFFSET + .5);
+ cairo_stroke (cr);
}
} else {
- if ( _unity_loc < w ){
- context->move_to (_unity_loc - CORNER_OFFSET + .5, 1.5);
- context->line_to (_unity_loc - CORNER_OFFSET + .5, _girth - 1.5);
- context->stroke ();
+ if (_unity_loc < w - CORNER_RADIUS) {
+ cairo_move_to (cr, _unity_loc - CORNER_OFFSET + .5, 1.5);
+ cairo_line_to (cr, _unity_loc - CORNER_OFFSET + .5, _girth - 1.5);
+ cairo_stroke (cr);
}
}
}
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET, w-CORNER_SIZE, h-CORNER_SIZE, CORNER_RADIUS);
cairo_set_source_rgba (cr, 0.505, 0.517, 0.525, 0.4);
cairo_fill (cr);
- } else if (_hovering) {
+ } else if (_hovering && CairoWidget::widget_prelight()) {
Gtkmm2ext::rounded_rectangle (cr, CORNER_OFFSET, CORNER_OFFSET, w-CORNER_SIZE, h-CORNER_SIZE, CORNER_RADIUS);
cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.1);
cairo_fill (cr);
}
-
- _last_drawn = ds;
-
- return true;
}
void
PixFader::on_size_request (GtkRequisition* req)
{
if (_orien == VERT) {
- req->width = (_girth ? _girth : -1);
- req->height = (_span ? _span : -1);
+ req->width = (_min_girth ? _min_girth : -1);
+ req->height = (_min_span ? _min_span : -1);
} else {
- req->height = (_girth ? _girth : -1);
- req->width = (_span ? _span : -1);
+ req->height = (_min_girth ? _min_girth : -1);
+ req->width = (_min_span ? _min_span : -1);
}
}
void
PixFader::on_size_allocate (Gtk::Allocation& alloc)
{
- DrawingArea::on_size_allocate(alloc);
+ int old_girth = _girth;
+ int old_span = _span;
+
+ CairoWidget::on_size_allocate(alloc);
if (_orien == VERT) {
_girth = alloc.get_width ();
_span = alloc.get_width ();
}
- if (is_realized()) {
+ if (is_realized() && ((old_girth != _girth) || (old_span != _span))) {
/* recreate patterns in case we've changed size */
create_patterns ();
}
update_unity_position ();
}
+bool
+PixFader::on_grab_broken_event (GdkEventGrabBroken* ev)
+{
+ if (_dragging) {
+ remove_modal_grab();
+ _dragging = false;
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ StopGesture ();
+ }
+ return (_tweaks & NoButtonForward) ? true : false;
+}
+
bool
PixFader::on_button_press_event (GdkEventButton* ev)
{
gdk_pointer_ungrab (GDK_CURRENT_TIME);
StopGesture ();
}
- return false;
+ return (_tweaks & NoButtonForward) ? true : false;
}
if (ev->button != 1 && ev->button != 2) {
set_adjustment_from_event (ev);
}
- return true;
+ return (_tweaks & NoButtonForward) ? true : false;
}
bool
PixFader::on_button_release_event (GdkEventButton* ev)
{
- double const ev_pos = (_orien == VERT) ? ev->y : ev->x;
+ double ev_pos = (_orien == VERT) ? ev->y : ev->x;
switch (ev->button) {
case 1:
StopGesture ();
if (!_hovering) {
- Keyboard::magic_widget_drop_focus();
+ if (!(_tweaks & NoVerticalScroll)) {
+ Keyboard::magic_widget_drop_focus();
+ }
queue_draw ();
}
if (ev_pos == _grab_start) {
-
/* no motion - just a click */
+ ev_pos = rint(ev_pos);
if (ev->state & Keyboard::TertiaryModifier) {
- adjustment.set_value (_default_value);
+ _adjustment.set_value (_default_value);
} else if (ev->state & Keyboard::GainFineScaleModifier) {
- adjustment.set_value (adjustment.get_lower());
- } else if (ev_pos == display_span()) {
+ _adjustment.set_value (_adjustment.get_lower());
+#if 0 // ignore clicks
+ } else if (ev_pos == slider_pos) {
; // click on current position, no move.
- } else if ((_orien == VERT && ev_pos < display_span()) || (_orien == HORIZ && ev_pos > display_span())) {
+ } else if ((_orien == VERT && ev_pos < slider_pos) || (_orien == HORIZ && ev_pos > slider_pos)) {
/* above the current display height, remember X Window coords */
- adjustment.set_value (adjustment.get_value() + adjustment.get_step_increment());
+ _adjustment.set_value (_adjustment.get_value() + _adjustment.get_step_increment());
} else {
- adjustment.set_value (adjustment.get_value() - adjustment.get_step_increment());
+ _adjustment.set_value (_adjustment.get_value() - _adjustment.get_step_increment());
+#endif
}
}
return true;
if (ev->state & Keyboard::GainFineScaleModifier) {
if (ev->state & Keyboard::GainExtraFineScaleModifier) {
- scale = 0.01;
+ scale = 0.005;
} else {
- scale = 0.05;
+ scale = 0.1;
}
} else {
- scale = 0.25;
+ scale = 1.0;
}
if (_orien == VERT) {
-
- /* should left/right scroll affect vertical faders ? */
-
switch (ev->direction) {
-
- case GDK_SCROLL_UP:
- adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale));
- ret = true;
- break;
- case GDK_SCROLL_DOWN:
- adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale));
- ret = true;
- break;
- default:
- break;
+ case GDK_SCROLL_UP:
+ _adjustment.set_value (_adjustment.get_value() + (_adjustment.get_page_increment() * scale));
+ ret = true;
+ break;
+ case GDK_SCROLL_DOWN:
+ _adjustment.set_value (_adjustment.get_value() - (_adjustment.get_page_increment() * scale));
+ ret = true;
+ break;
+ default:
+ break;
}
} else {
+ int dir = ev->direction;
- /* up/down scrolls should definitely affect horizontal faders
- because they are so much easier to use
- */
-
- switch (ev->direction) {
+ if (ev->state & Keyboard::ScrollHorizontalModifier || !(_tweaks & NoVerticalScroll)) {
+ if (ev->direction == GDK_SCROLL_UP) dir = GDK_SCROLL_RIGHT;
+ if (ev->direction == GDK_SCROLL_DOWN) dir = GDK_SCROLL_LEFT;
+ }
- case GDK_SCROLL_RIGHT:
- case GDK_SCROLL_UP:
- adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale));
- ret = true;
- break;
- case GDK_SCROLL_LEFT:
- case GDK_SCROLL_DOWN:
- adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale));
- ret = true;
- break;
- default:
- break;
+ switch (dir) {
+ case GDK_SCROLL_RIGHT:
+ _adjustment.set_value (_adjustment.get_value() + (_adjustment.get_page_increment() * scale));
+ ret = true;
+ break;
+ case GDK_SCROLL_LEFT:
+ _adjustment.set_value (_adjustment.get_value() - (_adjustment.get_page_increment() * scale));
+ ret = true;
+ break;
+ default:
+ break;
}
}
return ret;
if (ev->state & Keyboard::GainFineScaleModifier) {
if (ev->state & Keyboard::GainExtraFineScaleModifier) {
- scale = 0.05;
+ scale = 0.005;
} else {
scale = 0.1;
}
double const delta = ev_pos - _grab_loc;
_grab_loc = ev_pos;
- double fract = (delta / _span);
+ const double off = FADER_RESERVE + ((_orien == VERT) ? CORNER_OFFSET : 0);
+ const double span = _span - off;
+ double fract = (delta / span);
fract = min (1.0, fract);
fract = max (-1.0, fract);
fract = -fract;
}
- adjustment.set_value (adjustment.get_value() + scale * fract * (adjustment.get_upper() - adjustment.get_lower()));
+ _adjustment.set_value (_adjustment.get_value() + scale * fract * (_adjustment.get_upper() - _adjustment.get_lower()));
}
return true;
void
PixFader::adjustment_changed ()
{
- if (display_span() != _last_drawn) {
- queue_draw ();
- }
+ queue_draw ();
}
/** @return pixel offset of the current value from the right or bottom of the fader */
int
PixFader::display_span ()
{
- float fract = (adjustment.get_value () - adjustment.get_lower()) / ((adjustment.get_upper() - adjustment.get_lower()));
+ float fract = (_adjustment.get_value () - _adjustment.get_lower()) / ((_adjustment.get_upper() - _adjustment.get_lower()));
int ds;
if (_orien == VERT) {
- ds = (int)floor (_span * (1.0 - fract));
+ const double off = FADER_RESERVE + CORNER_OFFSET;
+ const double span = _span - off;
+ ds = (int)rint (span * (1.0 - fract));
} else {
- ds = (int)floor (_span * fract);
+ const double off = FADER_RESERVE;
+ const double span = _span - off;
+ ds = (int)rint (span * fract + off);
}
return ds;
PixFader::update_unity_position ()
{
if (_orien == VERT) {
- _unity_loc = (int) rint (_span * (1 - ((_default_value - adjustment.get_lower()) / (adjustment.get_upper() - adjustment.get_lower())))) - 1;
+ const double span = _span - FADER_RESERVE - CORNER_OFFSET;
+ _unity_loc = (int) rint (span * (1 - ((_default_value - _adjustment.get_lower()) / (_adjustment.get_upper() - _adjustment.get_lower())))) - 1;
} else {
- _unity_loc = (int) rint ((_default_value - adjustment.get_lower()) * _span / (adjustment.get_upper() - adjustment.get_lower()));
+ const double span = _span - FADER_RESERVE;
+ _unity_loc = (int) rint (FADER_RESERVE + (_default_value - _adjustment.get_lower()) * span / (_adjustment.get_upper() - _adjustment.get_lower()));
}
queue_draw ();
PixFader::on_enter_notify_event (GdkEventCrossing*)
{
_hovering = true;
- Keyboard::magic_widget_grab_focus ();
+ if (!(_tweaks & NoVerticalScroll)) {
+ Keyboard::magic_widget_grab_focus ();
+ }
queue_draw ();
return false;
}
{
if (!_dragging) {
_hovering = false;
- Keyboard::magic_widget_drop_focus();
+ if (!(_tweaks & NoVerticalScroll)) {
+ Keyboard::magic_widget_drop_focus();
+ }
queue_draw ();
}
return false;
void
PixFader::set_adjustment_from_event (GdkEventButton* ev)
{
- double fract = (_orien == VERT) ? (1.0 - (ev->y / _span)) : (ev->x / _span);
+ const double off = FADER_RESERVE + ((_orien == VERT) ? CORNER_OFFSET : 0);
+ const double span = _span - off;
+ double fract = (_orien == VERT) ? (1.0 - ((ev->y - off) / span)) : ((ev->x - off) / span);
fract = min (1.0, fract);
fract = max (0.0, fract);
- adjustment.set_value (fract * (adjustment.get_upper () - adjustment.get_lower ()));
+ _adjustment.set_value (fract * (_adjustment.get_upper () - _adjustment.get_lower ()));
}
void
}
void
-PixFader::set_text (const std::string& str, bool centered = true)
+PixFader::set_tweaks (Tweaks t)
+{
+ bool need_redraw = false;
+ if ((_tweaks & NoShowUnityLine) ^ (t & NoShowUnityLine)) {
+ need_redraw = true;
+ }
+ _tweaks = t;
+ if (need_redraw) {
+ queue_draw();
+ }
+}
+
+void
+PixFader::set_text (const std::string& str, bool centered, bool expose)
{
if (_layout && _text == str) {
return;
if (_layout) {
_layout->set_text (str);
_layout->get_pixel_size (_text_width, _text_height);
- queue_resize ();
+ // queue_resize ();
+ if (expose) queue_draw ();
}
}
std::string txt = _layout->get_text();
_layout.clear (); // drop reference to existing layout
_text = "";
- set_text (txt, _centered_text);
+ set_text (txt, _centered_text, false);
}
- /* patterns are cached and re-created as needed
+ /* patterns are cached and re-created as needed
* during 'expose' in the GUI thread */
_pattern = 0;
queue_draw ();