1 /* B.Harvestr
2  * LV2 Plugin
3  *
4  * Copyright (C) 2018, 2019  Sven Jähnichen
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef DIAL_HPP_
21 #define DIAL_HPP_
22 
23 #include "BWidgets/RangeWidget.hpp"
24 #include "BUtilities/to_string.hpp"
25 
26 class Dial : public BWidgets::RangeWidget
27 {
28 public:
Dial()29 	Dial () : Dial (0, 0, 0, 0, "", 0, 0, 0, 0) {}
Dial(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,std::string format="",std::string unit="",std::function<double (double x)> func=[](double x){},std::function<double (double x)> revfunc=[](double x){})30 	Dial (const double x, const double y, const double width, const double height, const std::string& name,
31 		 const double value, const double min, const double max, const double step, std::string format = "",
32 		 std::string unit = "",
33  	 	 std::function<double (double x)> func = [] (double x) {return x;},
__anon605c47790202(double x) 34  		 std::function<double (double x)> revfunc = [] (double x) {return x;}) :
35 			RangeWidget (x, y, width, height, name, value, min, max, step),
36 			format_ (format),
37 			unit_ (unit),
38 			transform_ (func),
39 			reverse_ (revfunc),
40 			fgColors_ (BWIDGETS_DEFAULT_FGCOLORS),
41 			bgColors_ (BWIDGETS_DEFAULT_BGCOLORS)
42 	{
43 		setDraggable (true);
44 	}
45 
clone() const46 	virtual Widget* clone () const override {return new Dial (*this);}
47 
applyTheme(BStyles::Theme & theme)48 	virtual void applyTheme (BStyles::Theme& theme) override {applyTheme (theme, name_);}
49 
applyTheme(BStyles::Theme & theme,const std::string & name)50 	virtual void applyTheme (BStyles::Theme& theme, const std::string& name) override
51 	{
52 		Widget::applyTheme (theme, name);
53 
54 		// Foreground colors (scale)
55 		void* fgPtr = theme.getStyle(name, BWIDGETS_KEYWORD_FGCOLORS);
56 		if (fgPtr) fgColors_ = *((BColors::ColorSet*) fgPtr);
57 
58 		// Background colors (scale background, knob)
59 		void* bgPtr = theme.getStyle(name, BWIDGETS_KEYWORD_BGCOLORS);
60 		if (bgPtr) bgColors_ = *((BColors::ColorSet*) bgPtr);
61 
62 		if (fgPtr || bgPtr) update ();
63 
64 	}
65 
onButtonPressed(BEvents::PointerEvent * event)66 	virtual void onButtonPressed (BEvents::PointerEvent* event) override {}
67 
onButtonReleased(BEvents::PointerEvent * event)68 	virtual void onButtonReleased (BEvents::PointerEvent* event) override {}
69 
onPointerDragged(BEvents::PointerEvent * event)70 	virtual void onPointerDragged (BEvents::PointerEvent* event) override
71 	{
72 		if (!event) return;
73 
74 		double w = getEffectiveWidth();
75 		double h = getEffectiveHeight();
76 		double d = (w < h ? w : h);
77 
78 		if ((d == 0) || (getMin() == getMax())) return;
79 
80 		double dist = getMax() - getMin();
81 		double valueTransformed = transform_ ((getValue() - getMin()) / dist);
82 		double nval = LIMIT (valueTransformed - event->getDelta ().y / 2.0 / w, 0.0, 1.0);
83 		setValue (getMin() + reverse_ (nval) * dist);
84 	}
85 
onWheelScrolled(BEvents::WheelEvent * event)86 	virtual void onWheelScrolled (BEvents::WheelEvent* event) override
87 	{
88 		if (!event) return;
89 
90 		double w = getEffectiveWidth();
91 		double h = getEffectiveHeight();
92 		double d = (w < h ? w : h);
93 
94 		if ((d == 0) || (getMin() == getMax())) return;
95 
96 		double dist = getMax() - getMin();
97 		double valueTransformed = transform_ ((getValue() - getMin()) / dist);
98 		double nval = LIMIT (valueTransformed + event->getDelta ().y / 2.0 / w, 0.0, 1.0);
99 		setValue (getMin() + reverse_ (nval) * dist);
100 	}
101 
102 protected:
103 	std::string format_;
104 	std::string unit_;
105 	std::function<double(double)> transform_;
106 	std::function<double(double)> reverse_;
107 	BColors::ColorSet fgColors_;
108 	BColors::ColorSet bgColors_;
109 
draw(const BUtilities::RectArea & area)110 	virtual void draw (const BUtilities::RectArea& area) override
111 	{
112 		if ((!widgetSurface_) || (cairo_surface_status (widgetSurface_) != CAIRO_STATUS_SUCCESS)) return;
113 
114 		// Draw super class widget elements first
115 		Widget::draw (area);
116 
117 		const double x0 = getXOffset ();
118 		const double y0 = getYOffset ();
119 		const double h = getEffectiveHeight ();
120 		const double w = getEffectiveWidth ();
121 		const double d = (w < h ? w : h);
122 		const double xc = x0 + 0.5 * w;
123 		const double yc = y0 + 0.5 * h;
124 		const double alpha = transform_ ((value - getMin()) / (getMax() - getMin()));
125 		const double x1 = xc + 0.5 * d * cos ((0.5 + 2 * alpha) * M_PI);
126 		const double y1 = yc + 0.5 * d * sin ((0.5 + 2 * alpha) * M_PI);
127 
128 		// Draw scale only if it is not a null widget
129 		if (d > 0)
130 		{
131 			cairo_surface_clear (widgetSurface_);
132 			cairo_t* cr = cairo_create (widgetSurface_);
133 
134 			if (cairo_status (cr) == CAIRO_STATUS_SUCCESS)
135 			{
136 				// Limit cairo-drawing area
137 				cairo_rectangle (cr, area.getX (), area.getY (), area.getWidth (), area.getHeight ());
138 				cairo_clip (cr);
139 
140 				BColors::Color slColor = *fgColors_.getColor (getState ()); slColor.applyBrightness (BWIDGETS_DEFAULT_NORMALLIGHTED);
141 				BColors::Color txColor = *fgColors_.getColor (getState ()); txColor.applyBrightness (BWIDGETS_DEFAULT_NORMALLIGHTED);
142 
143 				// Circle
144 				cairo_set_line_width (cr, 1.0);
145 				cairo_pattern_t* pat = cairo_pattern_create_linear (x1, y1, xc, yc);
146 				cairo_pattern_add_color_stop_rgba (pat, 0, slColor.getRed (), slColor.getGreen (), slColor.getBlue (), slColor.getAlpha ());
147 				cairo_pattern_add_color_stop_rgba (pat, 1, slColor.getRed (), slColor.getGreen (), slColor.getBlue (), 0.1 * slColor.getAlpha ());
148 				cairo_set_source (cr, pat);
149 				cairo_arc (cr,xc, yc, 0.49 * d, M_PI * (0.54 + 2.0 * alpha) , M_PI * (2.5 + 2.00 * alpha));
150 				cairo_stroke (cr);
151 				cairo_pattern_destroy (pat);
152 
153 				// Arc
154 				cairo_set_line_width (cr, 0.0);
155 				cairo_set_source_rgba (cr, CAIRO_RGBA (slColor));
156 				cairo_arc (cr,xc, yc, 0.42 * d, M_PI * 0.5, M_PI * (0.5 + alpha * 2.0));
157 				cairo_arc_negative (cr, xc, yc , 0.32 * d, M_PI * (0.5 + alpha * 2.0), M_PI * 0.5);
158 				cairo_close_path (cr);
159 				cairo_fill (cr);
160 
161 				// Text
162 				double txtlines = (unit_ == "" ? 1.0 : 2.0);
163 				cairo_text_extents_t ext;
164 				cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
165 				cairo_set_font_size (cr, 0.18 * d);
166 
167 				const std::string valstr = BUtilities::to_string (getValue(), format_);
168 				cairo_text_extents (cr, valstr.c_str(), &ext);
169 				cairo_move_to (cr, w / 2 - ext.width / 2 - ext.x_bearing, h / 2 - txtlines * ext.height / 2 - ext.y_bearing);
170 				cairo_set_source_rgba (cr, CAIRO_RGBA (txColor));
171 				cairo_show_text (cr, valstr.c_str ());
172 
173 				if (txtlines > 1.0)
174 				{
175 					cairo_text_extents (cr, unit_.c_str(), &ext);
176 					cairo_move_to (cr, w / 2 - ext.width / 2 - ext.x_bearing, h / 2 + ext.height / 2 - ext.y_bearing);
177 					cairo_set_source_rgba (cr, CAIRO_RGBA (txColor));
178 					cairo_show_text (cr, unit_.c_str ());
179 				}
180 
181 			}
182 			cairo_destroy (cr);
183 		}
184 	}
185 };
186 
187 #endif /* DIAL_HPP_ */
188