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