1

I have a bunch of GTK buttons in a GTK Flowbox, and am using the CSS border-radius property to make them round and background-image for the background. Unfortunately, they then appear as ovals, as in picrel below. What can I do so that the button would always have height equal to width, thus maintaining the shape of a perfect circle?

enter image description here

A MRE as requested: C file:

#include <gtk/gtk.h>

#define WINDOW_HEIGHT 600

GtkWidget * stop;
int num_stops; 

static void stop_toggle (GtkToggleButton *source, gpointer user_data) {
    //have to use set child here
    if (gtk_toggle_button_get_active(source)) {
        gtk_widget_set_name(GTK_WIDGET(source), "stop_button_pressed");
    }
    else {
        gtk_widget_set_name(GTK_WIDGET(source), "stop_button");
    }
}  

static void make_toggles (GtkWidget * window) {
    GtkWidget * stop_flow;
    short i; 

    stop_flow = gtk_flow_box_new();
    
    //initialise stops on this panel
    char label[7]; label[0] = '\0';
    for (i = 0; i < num_stops; i++) {
        sprintf(label, "%d", i);
        stop = gtk_toggle_button_new_with_label(label);
        gtk_widget_set_name(stop, "stop_button");
        
        gtk_flow_box_append(GTK_FLOW_BOX(stop_flow), stop);
        g_signal_connect(stop, "toggled", G_CALLBACK(stop_toggle), NULL);
    }
    gtk_flow_box_set_column_spacing(GTK_FLOW_BOX(stop_flow), 15);
    gtk_flow_box_set_row_spacing(GTK_FLOW_BOX(stop_flow), 10);
    gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(stop_flow), GTK_SELECTION_NONE);
    
    gtk_widget_set_name(stop_flow, "window_main");
    gtk_window_set_child (GTK_WINDOW (window), stop_flow);
}
static void app_startup(GtkApplication* app) {
    //use this to figure out number of stops and pistons from the LCS app
    num_stops = 16; 
}
static void activate (GtkApplication* app, gpointer user_data) {
    GtkWidget *window;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Left Panel");
    gtk_window_set_default_size (GTK_WINDOW (window), 800, WINDOW_HEIGHT);
    make_toggles(window);
    
    GtkCssProvider * cssProvider = gtk_css_provider_new();
    gtk_css_provider_load_from_path(cssProvider, "/your_location/GTK/styling2.css");
    gtk_style_context_add_provider_for_display(gtk_widget_get_display(window), GTK_STYLE_PROVIDER(cssProvider),
                                                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    gtk_widget_show (window);
}


int main(int argc, char **argv) {
    GtkApplication *app;
    int status;

    app = gtk_application_new("lcs.console", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "startup", G_CALLBACK(app_startup), NULL);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

With Css file:

w_main {
    background-color: grey;
}

#stop_button {
    background-color: white;
    background-size: 100% 100%;
    background-position: center center;
    border-radius: 50%;
    color: black;
    font-weight: bold;
    font-size: xx-large;
}
#stop_button_pressed {
    background-color: yellow;
    background-size: 100% 100%;
    background-position: center center;
    border-radius: 50%;
    color: black;
    font-weight: bold;
    font-size: xx-large;
}

And you may compile the CSS file on linux via gcc -Wall pkg-config --cflags gtk4 file.c pkg-config --libs gtk4 .

3
  • Please edit your question and produce an MRE. We'd like a single .c file that compiles cleanly and demonstrates the problem [rather than background images, just set solid colors]. We might like to download, compile, and run your code. Also, what is the window geometry? And, what is the screen geometry? (i.e.) We'd like to know the screen aspect ration and, possibly, the pixel aspect ratio. As a rule, whenever a circle comes out as an oval, it's related to aspect ratio. Commented Mar 27, 2023 at 21:59
  • Also, what pixel format are you using? 8 bit colormap, 16 bit true color, 24 bit RGB/BGR (8 bits/color), or 32 bit? Your ovals appear to be approx. 2x taller than wider. (e.g.) If the surface expected 8 bit pixels, but you gave it 16 bit pixels but didn't tell it, this might be the issue. Commented Mar 27, 2023 at 22:04
  • Alright I added an MRE. I do not know what you mean by pixel format I just used the css background-image command. Commented Mar 27, 2023 at 22:45

1 Answer 1

2

I tried out your code, and in testing out different quantities of buttons, the issue seemed to circle around the behavior of the gtk_flow_box widget. As more buttons were added, the size and distortion of the buttons grew. No matter what I tried, the program would not allow for the window to be reduced in size to make the oval buttons look like circular buttons.

I don't know if it is mandatory that you utilize the GTK flow box, but if not, you might try utilizing a GTK grid box instead. Following is a refactored version of your program with container references changed from a flow box to a grid box.

#include <gtk/gtk.h>

#define WINDOW_HEIGHT 300

GtkWidget * stop;
int num_stops;

static void stop_toggle (GtkToggleButton *source, gpointer user_data)
{
    //have to use set child here
    if (gtk_toggle_button_get_active(source))
    {
        gtk_widget_set_name(GTK_WIDGET(source), "stop_button_pressed");
    }
    else
    {
        gtk_widget_set_name(GTK_WIDGET(source), "stop_button");
    }
}

static void make_toggles (GtkWidget * window)
{
    GtkWidget * stop_grid;                                      /* Revised name to pertain to a grid box */
    short i;

    stop_grid = gtk_grid_new();
    gtk_grid_set_column_spacing(GTK_GRID(stop_grid), 10);
    gtk_grid_set_row_spacing(GTK_GRID(stop_grid), 10);

    //initialise stops on this panel
    char label[7];
    label[0] = '\0';

    /*gtk_flow_box_set_column_spacing(GTK_FLOW_BOX(stop_flow), 0);
    gtk_flow_box_set_row_spacing(GTK_FLOW_BOX(stop_flow), 0);
    gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(stop_flow), GTK_SELECTION_NONE);*/

    gtk_widget_set_name(stop_grid, "window_main");

    for (i = 0; i < num_stops; i++)
    {
        for (int j = 0; j < num_stops; j++)                 /* Create a 4 x 4 grid of buttons */
        {
            sprintf(label, "%d", i * num_stops + j + 1);
            stop = gtk_toggle_button_new_with_label(label);
            gtk_widget_set_name(stop, "stop_button");
            gtk_grid_attach(GTK_GRID(stop_grid), stop, j, i * num_stops, 1, 1);
            //gtk_flow_box_append(GTK_FLOW_BOX(stop_flow), stop);
            g_signal_connect(stop, "toggled", G_CALLBACK(stop_toggle), NULL);
        }
    }

    gtk_window_set_child (GTK_WINDOW (window), stop_grid);
}
static void app_startup(GtkApplication* app)
{
    //use this to figure out number of stops and pistons from the LCS app
    num_stops = 4;                                                          /* Revised for unit testing */
}
static void activate (GtkApplication* app, gpointer user_data)
{
    GtkWidget *window;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Left Panel");
    gtk_window_set_default_size (GTK_WINDOW (window), 300, WINDOW_HEIGHT);  /* Revised for unit testing */
    make_toggles(window);

    GtkCssProvider * cssProvider = gtk_css_provider_new();
    gtk_css_provider_load_from_path(cssProvider, "styling2.css");           /* Revised for unit testing */
    gtk_style_context_add_provider_for_display(gtk_widget_get_display(window), GTK_STYLE_PROVIDER(cssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    gtk_widget_show (window);
}

int main(int argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new("lcs.console", 0);                            /* Revised for unit testing */
    g_signal_connect(app, "startup", G_CALLBACK(app_startup), NULL);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

Along with the code refactoring, the style sheet was revised a bit to add in some padding.

#window_main {
    background-color: #c5dbe0;
    padding:10px;
}

#stop_button {
    background-color: white;
    background-size: 100% 100%;
    background-position: center center;
    border-radius: 50%;
    color: black;
    font-weight: bold;
    font-size: xx-large;
    min-width:40px;
    min-height:40px;
    padding:10px;
    border:20px;
}
#stop_button_pressed {
    background-color: yellow;
    background-size: 100% 100%;
    background-position: center center;
    border-radius: 50%;
    color: black;
    font-weight: bold;
    font-size: xx-large;
    min-width:40px;
    min-height:40px;
    padding:10px;
    border:20px;
}

With those bits of refactoring and refinements, a window with circular buttons was able to be produced.

Sample Button Grid

Again, if it is not mandatory that you utilize a GTK flow box, you might try out these tweaks and see if it meets the spirit of your project.

Sign up to request clarification or add additional context in comments.

2 Comments

Interesting, the padding property was the final touch needed to make them perfectly round. I suppose if I am not able to find a FlowBox solution I will go with this
My main concern with a GTK Grid approach is that it is not dynamic, i.e. in order to fill the whole screen I need to manually set the min-height and min-width property depending on the number of stops.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.