Rotate a Widget with Mouse ( GTK3 and C language)

I am trying to make a Music Player which will be written in GTK3 and C language .
There are some skins available for users with different Interfaces.
One Skin uses a Circular Volume Control Button so that the User can turn it with its Mouse pointer.
volume-control

The problem I am facing is that I can not figure out how to create this Volume button so that the user can turn it with its Mouse pointer.

Here are a minimal working example which I came so far:
player.c:

#include <gtk/gtk.h>

void start_css ( void );
GtkWidget *createWindow ( const gint width, const int height );
GtkWidget *createButton ( GtkWidget *widget );

int main ( void )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init ( NULL, NULL );
    start_css();
    ///
    window = createWindow ( 250, 250 );
    gtk_window_set_title ( GTK_WINDOW ( window ), "Button" );
    g_signal_connect ( window, "destroy", gtk_main_quit, NULL );
    gtk_widget_show ( window );
    ///
    button = createButton ( window );
    gtk_widget_show ( button );
    gtk_main();
}

void start_css ( void )
{
    GtkCssProvider *provider;
    GdkDisplay *display;
    GdkScreen *screen;
    GFile *css_fp;
    const gchar *css_style_file;
    GError *error = 0;
    /// ---------------------------
    provider = gtk_css_provider_new ();
    display = gdk_display_get_default ();
    screen = gdk_display_get_default_screen ( display );
    /// ***
    gtk_style_context_add_provider_for_screen ( screen, GTK_STYLE_PROVIDER ( provider ), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION );
    css_style_file = "style.css";
    css_fp = g_file_new_for_path ( css_style_file );
    /// ***
    gtk_css_provider_load_from_file ( provider, css_fp, &error );
    g_object_unref ( provider );
}

GtkWidget *createWindow ( const gint width, const int height )
{
    GtkWidget *window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_container_set_border_width ( GTK_CONTAINER ( window ), 50 );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), width, height );
    return window;
}

GtkWidget *createButton ( GtkWidget *widget )
{
    GtkWidget *button = gtk_button_new_with_label ( "Rotate me" );
    gtk_style_context_add_class ( gtk_widget_get_style_context ( GTK_WIDGET ( button ) ), "circular" );
    gtk_container_add ( GTK_CONTAINER ( widget ), button );
    return button;
}

style.css:

window
{
    background-color: green;
}

button
{
    background-image: url( "volume.png" );
    background-repeat: no-repeat;
    background-position: 50% 50%;
    color: black;
    border: 5px solid gray;
    box-shadow: 0 0 10px red;

    font-family:Verdana;
    font-size:1.2em;
    font-weight:bold;
}

button,
button:hover,
button:active,
button:checked
{
    text-decoration:none;
}

Volume-Button image:
volume

I was thinking of Draw and Orientation, but I am not sure if this is the right way and if it is how should I implement it.

There are several parts to this. Firstly, can you use CSS-transforms in GTK at all? I don’t know, I’ve never tried but I would assume not.

CSS transforms would handle the rotation part but then you have the issue of the shadow, if you rotate that image, the shadow and the highlight will rotate too. So perhaps what really want to do is leave the background alone and rotate the little black dot around the inside of the dial? In that case, the black dot is going to need to be a separate moveable element in some way. My preferred solution would be to mostly drop the CSS and draw the control with cairo.

Next thing is that you will need some button-press and pointer-motion events to actually handle the interaction. Then you will need to decide what motion you want. These dial controls often ignore the actual rotation and use upward drag to turn the dial one way and downward to turn the other. You might also want to change the dial with mouse wheel, which is another input event that you need to handle.

I think the best course of action is to look at the code of an existing control and base your control on that. Perhaps the input spinner would be a good candidate?

@ Bowler
Thank you for your Answer. I do like GTK and I will probably use it in the next few years.
I will keep search for a way to do it.
If happens that you find a way or you already know one, please share it.

This has really nothing to do with CSS—mostly because CSS only deals with the drawing model, not with the input one.

The most naive implementation would be to write your own widget that includes an EventBox (to catch input events) and a DrawingArea (to render the control). You will need to connect to pointer events, compute the angle of rotation from the pointer motion, and then draw the button control to reflect that angle.

For instance, you can use the GtkWidget::button-press-event to detect when the user presses a button on the pointer while inside your widget. If the button press came from the primary button, then you can start the “dragging to rotate” operation, and save the coordinates of the event inside your state tracker data structure. You can use the GtkWidget::motion-notify-event while checking that the motion event state has the button pressed modifier with the same button that caused the button-press-event to be emitted. The motion event coordinates can be used to compute the angle of rotation by turning them from cartesian to polar, using the coordinates of the button press event as the origin to determine the direction of rotation. Your widget should have a method that allows you to draw it at a certain angle, by assigning the angle to a state variable and queueing a redraw. Finally, when the user releases the button, you’ll get a GtkWidget::button-release-event, and you can stop your state tracker.

As for drawing, you will need to have a look at Cairo.

For more complex functionality, like using touchpad/touchscreen gestures like two finger rotations you will need to look into GtkGesture; GtkGesture is already a state tracker for input, so you can get an angle out of it. Of course, it only works with two touch points, so if you want to use a single pointer, like a mouse, you’ll have to implement that kind of state tracker yourself.

1 Like

I’ll give it a try, but seems that this kind of stuff is beyond my knowledge.
One last thing,do you mean that I need to create a new Widget from scratch or derived from existing ones?

You can create a GtkBin subclass that has a GtkEventBox as a child, which has a GtkDrawingArea as a child.

1 Like

Hm, this Sounds not that complicated as it looks like. I am going to make a lot of research on it. This kind of behavior is what a lot of people are looking it on internet. Until now no one gave an explanation about how should one do it. I hope I’ll succeed.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.