1 /* VScale.cpp
2 * Copyright (C) 2018, 2019 Sven Jähnichen
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "VScale.hpp"
19 #include <string>
20
21 namespace BWidgets
22 {
VScale()23 VScale::VScale () : VScale (0.0, 0.0, BWIDGETS_DEFAULT_VSCALE_WIDTH, BWIDGETS_DEFAULT_VSCALE_HEIGHT, "vscale",
24 BWIDGETS_DEFAULT_VALUE, BWIDGETS_DEFAULT_RANGE_MIN, BWIDGETS_DEFAULT_RANGE_MAX, BWIDGETS_DEFAULT_RANGE_STEP) {}
25
VScale(const double x,const double y,const double width,const double height,const std::string & name,const double value,const double min,const double max,const double step)26 VScale::VScale (const double x, const double y, const double width, const double height, const std::string& name,
27 const double value, const double min, const double max, const double step) :
28 RangeWidget (x, y, width, height, name, value, min, max, step),
29 fgColors (BWIDGETS_DEFAULT_FGCOLORS), bgColors (BWIDGETS_DEFAULT_BGCOLORS),
30 scaleArea (), scaleYValue (0)
31 {
32 setClickable (true);
33 setDraggable (true);
34 setScrollable (true);
35 }
36
VScale(const VScale & that)37 VScale::VScale (const VScale& that) :
38 RangeWidget (that), fgColors (that.fgColors), bgColors (that.bgColors), scaleArea (that.scaleArea),
39 scaleYValue (that.scaleYValue) {}
40
operator =(const VScale & that)41 VScale& VScale::operator= (const VScale& that)
42 {
43 fgColors = that.fgColors;
44 bgColors = that.bgColors;
45 scaleArea = that.scaleArea;
46 scaleYValue = that.scaleYValue;
47 RangeWidget::operator= (that);
48
49 return *this;
50 }
51
clone() const52 Widget* VScale::clone () const {return new VScale (*this);}
53
update()54 void VScale::update ()
55 {
56 updateCoords ();
57 Widget::update();
58 }
59
applyTheme(BStyles::Theme & theme)60 void VScale::applyTheme (BStyles::Theme& theme) {applyTheme (theme, name_);}
61
applyTheme(BStyles::Theme & theme,const std::string & name)62 void VScale::applyTheme (BStyles::Theme& theme, const std::string& name)
63 {
64 Widget::applyTheme (theme, name);
65
66 // Foreground colors (scale)
67 void* fgPtr = theme.getStyle(name, BWIDGETS_KEYWORD_FGCOLORS);
68 if (fgPtr) fgColors = *((BColors::ColorSet*) fgPtr);
69
70 // Background colors (scale background, knob)
71 void* bgPtr = theme.getStyle(name, BWIDGETS_KEYWORD_BGCOLORS);
72 if (bgPtr) bgColors = *((BColors::ColorSet*) bgPtr);
73
74 if (fgPtr || bgPtr) update ();
75
76 }
77
onButtonPressed(BEvents::PointerEvent * event)78 void VScale::onButtonPressed (BEvents::PointerEvent* event)
79 {
80 if
81 (
82 main_ && isVisible () &&
83 (getHeight() >= 1) &&
84 (getWidth() >= 1) &&
85 (scaleArea.getHeight() > 0) &&
86 (event->getButton() == BDevices::LEFT_BUTTON)
87 )
88 {
89 double min = getMin ();
90 double max = getMax ();
91
92 // Use pointer coords directly if hardSetable , otherwise apply only
93 // Y movement (drag mode)
94 if (hardChangeable)
95 {
96 double frac = (scaleArea.getY() + scaleArea.getHeight() - event->getPosition().y) / scaleArea.getHeight();
97 if (getStep () < 0) frac = 1 - frac;
98 double hardValue = min + frac * (max - min);
99 softValue = 0;
100 setValue (hardValue);
101 }
102 else
103 {
104 if (min != max)
105 {
106 double deltaFrac = -event->getDelta().y / scaleArea.getHeight();
107 if (getStep () < 0) deltaFrac = -deltaFrac;
108 softValue += deltaFrac * (max - min);
109 setValue (getValue() + softValue);
110 }
111 }
112 }
113 }
114
onButtonReleased(BEvents::PointerEvent * event)115 void VScale::onButtonReleased (BEvents::PointerEvent* event) {softValue = 0.0;}
116
onPointerDragged(BEvents::PointerEvent * event)117 void VScale::onPointerDragged (BEvents::PointerEvent* event) {onButtonPressed (event);}
118
onWheelScrolled(BEvents::WheelEvent * event)119 void VScale::onWheelScrolled (BEvents::WheelEvent* event)
120 {
121 double min = getMin ();
122 double max = getMax ();
123
124 if (min != max)
125 {
126 double step = (getStep () != 0 ? getStep () : (max - min) / scaleArea.getHeight());
127 setValue (getValue() + event->getDelta().y * step);
128 }
129 }
130
updateCoords()131 void VScale::updateCoords ()
132 {
133 scaleArea = BUtilities::RectArea
134 (
135 getXOffset (),
136 getYOffset (),
137 getEffectiveWidth (),
138 getEffectiveHeight ()
139 );
140 scaleYValue = scaleArea.getY() + (1 - getRelativeValue ()) * scaleArea.getHeight();
141 }
142
draw(const BUtilities::RectArea & area)143 void VScale::draw (const BUtilities::RectArea& area)
144 {
145
146 if ((!widgetSurface_) || (cairo_surface_status (widgetSurface_) != CAIRO_STATUS_SUCCESS)) return;
147
148 // Draw super class widget elements first
149 Widget::draw (area);
150
151 // Draw scale only if it is not a null widget
152 if ((scaleArea.getHeight() >= 1) && (scaleArea.getWidth() >= 1))
153 {
154 cairo_surface_clear (widgetSurface_);
155 cairo_t* cr = cairo_create (widgetSurface_);
156
157 if (cairo_status (cr) == CAIRO_STATUS_SUCCESS)
158 {
159
160 cairo_pattern_t* pat = nullptr;
161
162 // Limit cairo-drawing area
163 cairo_rectangle (cr, area.getX (), area.getY (), area.getWidth (), area.getHeight ());
164 cairo_clip (cr);
165
166 // Calculate aspect ratios first
167 double h = scaleArea.getHeight();
168 double w = scaleArea.getWidth();
169 double x1 = scaleArea.getX(); double y1 = scaleArea.getY(); // Top left
170 double x4 = x1 + w; double y4 = y1 + h; // Bottom right
171 double x2 = x1 + w; double y2 = scaleYValue; // Value line right
172 double x3 = x1; double y3 = y2; // Value line left
173
174
175 // Colors uses within this method
176 BColors::Color fgHi = *fgColors.getColor (getState ()); fgHi.applyBrightness (BWIDGETS_DEFAULT_ILLUMINATED);
177 BColors::Color fgMid = *fgColors.getColor (getState ()); fgMid.applyBrightness ((BWIDGETS_DEFAULT_ILLUMINATED + BWIDGETS_DEFAULT_NORMALLIGHTED) / 2);
178 BColors::Color fgLo = *fgColors.getColor (getState ()); fgLo.applyBrightness (BWIDGETS_DEFAULT_NORMALLIGHTED);
179 BColors::Color bgLo = *bgColors.getColor (getState ()); bgLo.applyBrightness (BWIDGETS_DEFAULT_NORMALLIGHTED);
180 BColors::Color bgHi = *bgColors.getColor (getState ()); bgHi.applyBrightness (BWIDGETS_DEFAULT_ILLUMINATED);
181 BColors::Color bgMid = *bgColors.getColor (getState ()); bgMid.applyBrightness ((BWIDGETS_DEFAULT_ILLUMINATED + BWIDGETS_DEFAULT_NORMALLIGHTED) / 2);
182 BColors::Color bgSh = *bgColors.getColor (getState ()); bgSh.applyBrightness (BWIDGETS_DEFAULT_SHADOWED);
183
184 cairo_set_line_width (cr, 0.0);
185 cairo_rectangle_rounded (cr, x1, y1, x4 - x1, y4 - y1, (x4 - x1) / 2, 0b1111);
186 cairo_clip (cr);
187
188 // Frame background
189 pat = cairo_pattern_create_linear (x4, y4, x1, y1);
190 if (pat && (cairo_pattern_status (pat) == CAIRO_STATUS_SUCCESS))
191 {
192 cairo_pattern_add_color_stop_rgba (pat, 0, bgLo.getRed (), bgLo.getGreen (), bgLo.getBlue (), bgLo.getAlpha ());
193 cairo_pattern_add_color_stop_rgba (pat, 1, bgHi.getRed (), bgHi.getGreen (), bgHi.getBlue (), bgHi.getAlpha ());
194 cairo_rectangle_rounded (cr, x1, y1, x4 - x1, y4 - y1, (x4 - x1) / 2, 0b1111);
195 cairo_set_source (cr, pat);
196 cairo_fill (cr);
197 cairo_pattern_destroy (pat);
198 }
199 cairo_rectangle_rounded (cr, x1 + BWIDGETS_DEFAULT_VSCALE_DEPTH, y1 + BWIDGETS_DEFAULT_VSCALE_DEPTH, x4 - x1, y4 - y1, (x4 - x1) / 2, 0b1111);
200 cairo_set_source_rgba (cr, bgSh.getRed (), bgSh.getGreen (), bgSh.getBlue (), bgSh.getAlpha ());
201 cairo_fill (cr);
202
203 // Scale active
204 pat = cairo_pattern_create_linear (x3, y3, x2, y2);
205 if (pat && (cairo_pattern_status (pat) == CAIRO_STATUS_SUCCESS))
206 {
207 cairo_pattern_add_color_stop_rgba (pat, 1, fgLo.getRed (), fgLo.getGreen (), fgLo.getBlue (), fgLo.getAlpha ());
208 cairo_pattern_add_color_stop_rgba (pat, 0.75, fgHi.getRed (), fgHi.getGreen (), fgHi.getBlue (), fgHi.getAlpha ());
209 cairo_pattern_add_color_stop_rgba (pat, 0, fgLo.getRed (), fgLo.getGreen (), fgLo.getBlue (), fgLo.getAlpha ());
210 if (getStep () >= 0)
211 {
212 cairo_rectangle_rounded (cr, x3 + 0.5 * BWIDGETS_DEFAULT_VSCALE_DEPTH,
213 y3 + 0.5 * BWIDGETS_DEFAULT_VSCALE_DEPTH, x4 - x3, y4 - y3, (x4 - x3) / 2, 0b1100);
214 }
215 else
216 {
217 cairo_rectangle_rounded (cr, x1 + 0.5 * BWIDGETS_DEFAULT_VSCALE_DEPTH,
218 y1 + 0.5 * BWIDGETS_DEFAULT_VSCALE_DEPTH, x2 - x1, y2 - y1, (x2 - x1) / 2, 0b0011);
219 }
220 cairo_set_source (cr, pat);
221 cairo_fill (cr);
222 cairo_pattern_destroy (pat);
223 }
224
225 //Frame
226 pat = cairo_pattern_create_linear (x4, y4, x1, y1);
227 if (pat && (cairo_pattern_status (pat) == CAIRO_STATUS_SUCCESS))
228 {
229 cairo_pattern_add_color_stop_rgba (pat, 0, bgLo.getRed (), bgLo.getGreen (), bgLo.getBlue (), bgLo.getAlpha ());
230 cairo_pattern_add_color_stop_rgba (pat, 1, bgHi.getRed (), bgHi.getGreen (), bgHi.getBlue (), bgHi.getAlpha ());
231 cairo_set_source (cr, pat);
232 cairo_set_line_width (cr, 0.2 * BWIDGETS_DEFAULT_VSCALE_DEPTH);
233 cairo_rectangle_rounded (cr, x1, y1, x4 - x1, y4 - y1, (x4 - x1) / 2, 0b1111);
234 cairo_stroke (cr);
235 cairo_pattern_destroy (pat);
236 }
237 }
238
239 cairo_destroy (cr);
240
241 }
242
243 }
244
245 }
246