diff --git a/src/alsa-scarlett-gui.css b/src/alsa-scarlett-gui.css index 65eac99..bb2ffb9 100644 --- a/src/alsa-scarlett-gui.css +++ b/src/alsa-scarlett-gui.css @@ -23,3 +23,7 @@ button { padding: 0px 5px 0px 5px; } + +dial { + background: black; +} diff --git a/src/gtkdial.c b/src/gtkdial.c index bc65a43..503f624 100644 --- a/src/gtkdial.c +++ b/src/gtkdial.c @@ -18,6 +18,9 @@ #include "gtkdial.h" +#define DIAL_MIN_WIDTH 60 +#define DIAL_MAX_WIDTH 70 + static void set_value(GtkDial *dial, double newval); static void gtk_dial_set_property( @@ -98,13 +101,6 @@ enum { static guint signals[LAST_SIGNAL]; static GParamSpec *properties[LAST_PROP]; -struct DialColors { - GdkRGBA trough_border, - trough_bg, - trough_fill, - pointer; -}; - struct _GtkDial { GtkWidget parent_instance; GtkAdjustment *adj; @@ -114,8 +110,6 @@ struct _GtkDial { e_grab grab; double dvalp; - struct DialColors colors; - int round_digits; double zero_db; int taper; @@ -216,16 +210,28 @@ struct dial_properties { double w; double h; double radius; - double thickness; + double slider_thickness; double cx; double cy; double valp; double angle; - double slider_radius; double slider_cx; double slider_cy; }; +static int calculate_dial_height(int width) { + double radius = width / 2; + double angle = (360 - TOTAL_ROTATION_DEGREES) / 2 * M_PI / 180; + double height = radius + radius * cos(angle); + + return ceil(height); +} + +static double calculate_dial_radius_from_height(int height) { + double angle = (360 - TOTAL_ROTATION_DEGREES) / 2.0 * M_PI / 180.0; + return height / (1 + cos(angle)); +} + static void get_dial_properties( GtkDial *dial, struct dial_properties *props @@ -233,12 +239,29 @@ static void get_dial_properties( props->w = gtk_widget_get_width(GTK_WIDGET(dial)); props->h = gtk_widget_get_height(GTK_WIDGET(dial)); + double width = props->w; + if (width > DIAL_MAX_WIDTH) + width = DIAL_MAX_WIDTH; + + double max_height = calculate_dial_height(DIAL_MAX_WIDTH); + double height = props->h; + if (height > max_height) + height = max_height; + + double radius_from_width = width / 2; + + double radius_from_height = calculate_dial_radius_from_height(height); + + props->radius = radius_from_width < radius_from_height ? + radius_from_width : radius_from_height; + props->radius -= 0.5; + props->cx = props->w / 2; - props->cy = props->h / 2; - props->radius = props->h < props->w ? props->h / 2 - 2 : props->w / 2 - 2; - props->thickness = 10; - props->slider_radius = props->thickness * 1.5; - props->radius -= props->slider_radius / 2; + double angle = (360 - TOTAL_ROTATION_DEGREES) / 2.0 * M_PI / 180.0; + double y_offset = props->radius * cos(angle); + props->cy = (props->h / 2.0) + (props->radius - y_offset) / 2.0 - 0.5; + + props->slider_thickness = 16; double mn = dial->adj ? gtk_adjustment_get_lower(dial->adj) : 0; double mx = dial->adj ? gtk_adjustment_get_upper(dial->adj) : 1; @@ -246,9 +269,9 @@ static void get_dial_properties( props->valp = calc_taper(dial, calc_valp(value, mn, mx)); props->angle = calc_val(props->valp, ANGLE_START, ANGLE_END); - double radius = props->radius - props->thickness / 2; - props->slider_cx = cos(props->angle) * radius + props->cx; - props->slider_cy = sin(props->angle) * radius + props->cy; + double slider_radius = props->radius - props->slider_thickness / 2; + props->slider_cx = cos(props->angle) * slider_radius + props->cx; + props->slider_cy = sin(props->angle) * slider_radius + props->cy; } static double pdist2(double x1, double y1, double x2, double y2) { @@ -288,6 +311,8 @@ static void gtk_dial_class_init(GtkDialClass *klass) { klass->move_slider = >k_dial_move_slider; klass->value_changed = NULL; + gtk_widget_class_set_css_name(w_class, "dial"); + /** * GtkDial:adjustment: (attributes org.gtk.Method.get=gtk_dial_get_adjustment org.gtk.Method.set=gtk_dial_set_adjustment) * @@ -398,7 +423,6 @@ static void gtk_dial_focus_change_cb( } static void gtk_dial_init(GtkDial *dial) { - gtk_dial_set_style(dial, "#cdc7c2", "#f0f0f0", "#3584e4", "#808080"); gtk_widget_set_focusable(GTK_WIDGET(dial), TRUE); dial->adj = NULL; @@ -461,10 +485,15 @@ static void dial_measure( int *minimum_baseline, int *natural_baseline ) { - *minimum = 50; - *natural = 50; - *minimum_baseline = for_size; - *natural_baseline = for_size; + if (orientation == GTK_ORIENTATION_HORIZONTAL) { + *minimum = DIAL_MIN_WIDTH; + *natural = DIAL_MAX_WIDTH; + } else { + *minimum = calculate_dial_height(DIAL_MIN_WIDTH); + *natural = calculate_dial_height(DIAL_MAX_WIDTH); + } + *minimum_baseline = -1; + *natural_baseline = -1; } static void dial_snapshot(GtkWidget *widget, GtkSnapshot *snapshot) { @@ -472,50 +501,123 @@ static void dial_snapshot(GtkWidget *widget, GtkSnapshot *snapshot) { struct dial_properties p; get_dial_properties(dial, &p); - p.valp = CLAMP(p.valp, 0.0001, 1.0); cairo_t *cr = gtk_snapshot_append_cairo( snapshot, &GRAPHENE_RECT_INIT(0, 0, p.w, p.h) ); - // draw border - cairo_set_line_width(cr, gtk_widget_has_focus(widget) ? 5 : 2); - gdk_cairo_set_source_rgba(cr, &dial->colors.trough_border); - cairo_arc(cr, p.cx, p.cy, p.radius - p.thickness, ANGLE_START, ANGLE_END); - cairo_arc_negative(cr, p.cx, p.cy, p.radius, ANGLE_END, ANGLE_START); - cairo_close_path(cr); - cairo_stroke(cr); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - // bg trough - cairo_arc( - cr, - p.cx, p.cy, - (2 * p.radius - p.thickness) / 2.0, - ANGLE_START, ANGLE_END - ); - cairo_set_line_width(cr, p.thickness); - gdk_cairo_set_source_rgba(cr, &dial->colors.trough_bg); - cairo_stroke(cr); + double slider_radius = p.radius - p.slider_thickness / 2; + double background_radius = slider_radius + p.slider_thickness / 4; - // fill trough - cairo_arc( - cr, - p.cx, p.cy, - (2 * p.radius - p.thickness) / 2.0, - ANGLE_START, p.angle - ); - cairo_set_line_width(cr, p.thickness); - gdk_cairo_set_source_rgba(cr, &dial->colors.trough_fill); - cairo_stroke(cr); - - // pointer - gdk_cairo_set_source_rgba(cr, &dial->colors.pointer); + // background line + cairo_arc(cr, p.cx, p.cy, slider_radius, ANGLE_START, ANGLE_END); cairo_set_line_width(cr, 2); - cairo_move_to(cr, p.cx, p.cy); - cairo_line_to(cr, p.slider_cx, p.slider_cy); + cairo_set_source_rgba(cr, 1, 1, 1, 0.17); cairo_stroke(cr); + if (p.valp > 0.0) { + // outside value shadow + cairo_arc(cr, p.cx, p.cy, background_radius, ANGLE_START, p.angle); + cairo_set_line_width(cr, p.slider_thickness / 2); + cairo_set_source_rgba(cr, 1, 1, 1, 0.15); + cairo_stroke(cr); + + // value blur 2 + cairo_arc(cr, p.cx, p.cy, slider_radius, ANGLE_START, p.angle); + cairo_set_line_width(cr, 6); + cairo_set_source_rgba(cr, 1, 1, 1, 0.3); + cairo_stroke(cr); + } + + // draw line to zero db + double zero_db = gtk_dial_get_zero_db(dial); + if (zero_db != 0.0) { + double zero_db_valp = calc_taper( + dial, + calc_valp( + zero_db, + gtk_adjustment_get_lower(dial->adj), + gtk_adjustment_get_upper(dial->adj) + ) + ); + + double zero_db_angle = calc_val(zero_db_valp, ANGLE_START, ANGLE_END); + double zero_db_x = cos(zero_db_angle) * slider_radius + p.cx; + double zero_db_y = sin(zero_db_angle) * slider_radius + p.cy; + + cairo_move_to(cr, p.cx, p.cy); + cairo_line_to(cr, zero_db_x, zero_db_y); + cairo_set_line_width(cr, 2); + cairo_set_source_rgba(cr, 1, 1, 1, 0.17); + cairo_stroke(cr); + } + + // marker when at min or max + if (gtk_dial_get_value(dial) == gtk_adjustment_get_lower(dial->adj) || + gtk_dial_get_value(dial) == gtk_adjustment_get_upper(dial->adj)) { + cairo_move_to(cr, p.cx, p.cy); + cairo_line_to(cr, p.slider_cx, p.slider_cy); + cairo_set_line_width(cr, 2); + cairo_set_source_rgba(cr, 1, 1, 1, 0.5); + cairo_stroke(cr); + } + + if (p.valp > 0.0) { + // value blur 1 + cairo_arc(cr, p.cx, p.cy, slider_radius, ANGLE_START, p.angle); + cairo_set_line_width(cr, 4); + cairo_set_source_rgba(cr, 1, 1, 1, 0.5); + cairo_stroke(cr); + + // value + cairo_arc(cr, p.cx, p.cy, slider_radius, ANGLE_START, p.angle); + cairo_set_line_width(cr, 2); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_stroke(cr); + } + + // fill the circle + cairo_pattern_t *pat; + pat = cairo_pattern_create_radial( + p.cx + 5, p.cy + 5, 0, p.cx, p.cy, p.radius + ); + if (gtk_widget_has_focus(GTK_WIDGET(dial))) { + cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.30, 0.30, 0.33); + cairo_pattern_add_color_stop_rgb(pat, 0.4, 0.30, 0.30, 0.33); + cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.50, 0.50, 0.53); + } else { + cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.18, 0.18, 0.20); + cairo_pattern_add_color_stop_rgb(pat, 0.4, 0.18, 0.18, 0.20); + cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.40, 0.40, 0.42); + } + cairo_set_source(cr, pat); + + cairo_arc(cr, p.cx, p.cy, p.radius - p.slider_thickness, 0, 2 * M_PI); + cairo_fill(cr); + + // draw the circle + cairo_pattern_t *pat2; + pat2 = cairo_pattern_create_linear( + p.cx - p.radius / 2, + p.cy - p.radius / 2, + p.cx + p.radius / 2, + p.cy + p.radius / 2 + ); + cairo_pattern_add_color_stop_rgb(pat2, 0, 0.9, 0.9, 0.9); + cairo_pattern_add_color_stop_rgb(pat2, 1, 0.3, 0.3, 0.3); + cairo_set_source(cr, pat2); + + cairo_arc(cr, p.cx, p.cy, p.radius - p.slider_thickness, 0, 2 * M_PI); + cairo_set_line_width(cr, 2); + cairo_stroke(cr); + + cairo_pattern_destroy(pat); + cairo_pattern_destroy(pat2); + cairo_destroy(cr); } @@ -685,27 +787,6 @@ void gtk_dial_set_taper_linear_breakpoints( dial->taper_breakpoints_count = total_count; } -gboolean gtk_dial_set_style( - GtkDial *dial, - const char *trough_border, - const char *trough_bg, - const char *trough_fill, - const char *pointer -) { - gboolean out = TRUE; - - if (trough_border) - out = out && gdk_rgba_parse(&dial->colors.trough_border, trough_border); - if (trough_bg) - out = out && gdk_rgba_parse(&dial->colors.trough_bg, trough_bg); - if (trough_fill) - out = out && gdk_rgba_parse(&dial->colors.trough_fill, trough_fill); - if (pointer) - out = out && gdk_rgba_parse(&dial->colors.pointer, pointer); - - return out; -} - void gtk_dial_set_adjustment(GtkDial *dial, GtkAdjustment *adj) { if (!(adj == NULL || GTK_IS_ADJUSTMENT(adj))) return; @@ -950,7 +1031,7 @@ static void gtk_dial_click_gesture_pressed( struct dial_properties p; get_dial_properties(dial, &p); - if (circle_contains_point(p.slider_cx, p.slider_cy, p.slider_radius, x, y)) + if (circle_contains_point(p.slider_cx, p.slider_cy, p.radius, x, y)) dial->grab = GRAB_SLIDER; else dial->grab = GRAB_NONE; diff --git a/src/gtkdial.h b/src/gtkdial.h index bcaf53a..18a2d1f 100644 --- a/src/gtkdial.h +++ b/src/gtkdial.h @@ -81,26 +81,6 @@ void gtk_dial_set_taper_linear_breakpoints( int count ); -/** - * @brief Set the colors which this dial uses. String codes can be one of the following: - * A standard name (Taken from the X11 rgb.txt file) - * A hexadecimal value in the form “#rgb”, “#rrggbb”, “#rrrgggbbb” or ”#rrrrggggbbbb” - * A RGB color in the form “rgb(r,g,b)” (In this case the color will have full opacity) - * A RGBA color in the form “rgba(r,g,b,a)” - * NULL if the color is to remain unchanged - * - * @param dial: The dial - * @param trough_border: String code for trough border color - * @param trough_bg: String code for trough background color - * @param trough_fill: String code for trough fill color - * @return TRUE if all the colors were set successfully, FALSE otherwise - */ -gboolean gtk_dial_set_style(GtkDial *dial, - const char *trough_border, - const char *trough_bg, - const char *trough_fill, - const char *pointer); - G_END_DECLS #endif