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