From a3def4737c267b649194fd8c2f37cc8fa0f2bc0d Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Tue, 21 May 2024 10:51:37 +0200 Subject: mate-screensaver-preferences: Improve and cleanup time scale formatting (#295) Formatting the value of a GtkScale is actually pretty tricky, because the lower and upper values representations are used to compute the size required do draw *all* values. This means the representations for the lower and upper bounds have to be at least as big as any other possible value, failing that leads to wrapping and overflowing of the value representation. This was previously partially done in `time_to_string_text()`, but not only wasn't it comprehensive (it only did so for whole minutes < 59), but it also meant that if one of the bounds wasn't a whole minute it didn't have the desired effect. Fix this by extracting the code for padding the string outside of the time formatting, and pad the resulting format string whatever it is. Also improve the padding to try and be less visible, by padding to the side where we don't want the value to align (e.g. pad on the right if we want the value left-aligned). This is still basically a sad hack, but there doesn't seem to be a better way to do this when neither the lower nor upper bound is necessarily the largest value. --- src/mate-screensaver-preferences.c | 138 +++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 76 deletions(-) diff --git a/src/mate-screensaver-preferences.c b/src/mate-screensaver-preferences.c index d20c364..1bcdcaf 100644 --- a/src/mate-screensaver-preferences.c +++ b/src/mate-screensaver-preferences.c @@ -969,13 +969,7 @@ static char * time_to_string_text (long time) { char *secs, *mins, *hours, *string; - char *chk_hour_str, *chk_minute_str, *chk_hour_minute_str; - char *chk_ascii_str; int sec, min, hour; - size_t chk_ascii_len; - int len_minutes; - int n, inc_len; - int diff; sec = time % 60; time = time - sec; @@ -992,52 +986,6 @@ time_to_string_text (long time) secs = g_strdup_printf (ngettext ("%d second", "%d seconds", sec), sec); - /* inc_len = it's the lenght of the string "1 hour 59 minutes" */ - chk_hour_str = g_strdup_printf (ngettext ("%d hour", - "%d hours", 1), 1); - chk_minute_str = g_strdup_printf (ngettext ("%d minute", - "%d minutes", 59), 59); - chk_hour_minute_str = g_strdup_printf (_("%s %s"), - chk_hour_str, chk_minute_str); - inc_len = strlen (chk_hour_minute_str) - 1; - g_free (chk_hour_str); - g_free (chk_minute_str); - g_free (chk_hour_minute_str); - - len_minutes = 0; - for (n = 2; n < 60; n++) - { - char *minute_str = g_strdup_printf (ngettext ("%d minute", - "%d minutes", n), n); - char *ascii_str = g_str_to_ascii (minute_str, NULL); - size_t ascii_str_len = strlen (ascii_str); - size_t extra_length = (n < 10) ? 2 : 3; - - diff = (int) (ascii_str_len - extra_length); - if (diff > len_minutes) - len_minutes = diff; - - g_free (minute_str); - g_free (ascii_str); - } - - /* check the lenght of the string "1 minute" */ - chk_minute_str = g_strdup_printf (ngettext ("%d minute", - "%d minutes", 1), 1); - chk_ascii_str = g_str_to_ascii (chk_minute_str, NULL); - chk_ascii_len = strlen (chk_ascii_str); - diff = (int) (chk_ascii_len - 2); - - if (diff > len_minutes) - len_minutes = diff; - - g_free (chk_minute_str); - g_free (chk_ascii_str); - - /* len_minutes = MAX (1, len_minutes) */ - if (len_minutes < 1) - len_minutes = 1; - if (hour > 0) { if (sec > 0) @@ -1060,28 +1008,7 @@ time_to_string_text (long time) else { /* minutes */ - size_t max_len; - string = g_strdup_printf (_("%s"), mins); - if (min == 1) - max_len = (size_t) (len_minutes + inc_len + 3); - else if (min < 10) - max_len = (size_t) (len_minutes + inc_len); - else - max_len = (size_t) (len_minutes + inc_len - 1); - - while (strlen (string) != max_len) - { - char *string_aux; - - if (strlen (string) % 2 == 0) - string_aux = g_strconcat (string, " ", NULL); - else - string_aux = g_strconcat (" " , string, NULL); - - g_free (string); - string = string_aux; - } } } else @@ -1101,11 +1028,70 @@ static char * format_value_callback_time (GtkScale *scale, gdouble value) { - /*You need to make up for 27 characters in length, otherwise the display will split into different lines*/ + gchar *time_str, *big_time_str; + GtkAdjustment *adj; + gdouble lower, range, delta; + gint pad_size; + + /* get the value representation as a string */ if (value == 0) - return g_strdup_printf (_("Never ")); + time_str = g_strdup (_("Never")); + else + time_str = time_to_string_text ((long) (value * 60.0)); + + /* Now, adjust the string so the representation for the bounds are the + * longest ones, and try and adjust the length as smoothly as possible. + * The issue here is that GTK is using the lower and upper value + * representations to compute the largest expected value's bounding box, + * so those need to be bigger than anything else we might represent, + * otherwise layout gets messed up (wraps and overflows). To achieve this, + * we pad the values near each bound so its length is at least the same as + * the biggest actual value. We cannot really do anything perfect here + * because what matters is the pango layout size for the largest value, but + * we don't have access to enough information to create one matching what + * GTK will actually use, and even so it'd be trial-and-error until the + * layout is big enough. So the silly assumptions below are probably good + * enough. */ + adj = gtk_range_get_adjustment (GTK_RANGE (scale)); + lower = gtk_adjustment_get_lower (adj); + range = gtk_adjustment_get_upper (adj) - lower; + delta = range / 2 - (value - lower); + /* the largest (character-wise) time string we expect */ + big_time_str = time_to_string_text (7199 /* 1:59:59 */); + pad_size = ((g_utf8_strlen (big_time_str, -1) * (ABS (delta) / range)) - + g_utf8_strlen (time_str, -1)); + g_free (big_time_str); + if (pad_size > 0) + { + /* pad string with EM SPACE (U+2003) */ + GString *padded = g_string_new (NULL); + + /* adjust pad side in RTL locales that aren't actually translated, as + * a properly translated one would have text drawn RTL already */ + if (gtk_widget_get_direction (GTK_WIDGET (scale)) == GTK_TEXT_DIR_RTL) + { + const gchar *msg_plural = "%d minutes"; + if (ngettext ("%d minute", msg_plural, 2) == msg_plural) + delta *= -1; + } + + if (delta < 0) + { + for (gint i = 0; i < pad_size; i++) + g_string_append_unichar (padded, 0x2003); + g_string_append (padded, time_str); + } + else + { + g_string_append (padded, time_str); + for (gint i = 0; i < pad_size; i++) + g_string_append_unichar (padded, 0x2003); + } + g_free (time_str); + time_str = g_string_free (padded, FALSE); + } - return time_to_string_text ((long) (value * 60.0)); + return time_str; } static void -- cgit v1.2.1