Link styling (GtkLabel) is hard to read in a popover inside a selectable widget
Steps to reproduce
Use the sample code provided below, or construct your own where
- You have a container widget that can have a selection, such as ListBox, ListView, or ColumnView.
- A child widget in that container has a Popover.
- The popover contains a Label using markup with a link.
Then run the app and open the popover on the selected row.
Sample program in Rust:
$ sudo dnf install cargo gtk4-devel # for Fedora
$ cargo init gtk-issue && cd gtk-issue
$ cargo add gtk4 --features v4_12
edit src/main.rs
:
use gtk4 as gtk;
use gtk::prelude::*;
fn main() {
let app = gtk::Application::builder()
.application_id("example.bug")
.build();
app.connect_activate(|app| {
let window = gtk::ApplicationWindow::builder()
.application(app)
.build();
let list = gtk::ListBox::new();
list.append(&make_row());
list.append(&make_row());
window.set_child(Some(&list));
window.present();
});
app.run();
}
fn make_row() -> gtk::Box {
let label1 = gtk::Label::builder()
.use_markup(true)
.label("Here's a <a href='https://gnome.org'>link</a> and popover:")
.build();
let label2 = gtk::Label::builder()
.use_markup(true)
.label("Test <a href='https://gnome.org'>link</a> should be readable")
.build();
let button = gtk::MenuButton::new();
let popover = gtk::Popover::new();
popover.set_child(Some(&label2));
button.set_popover(Some(&popover));
let row = gtk::Box::new(gtk::Orientation::Horizontal, 12);
row.append(&label1);
row.append(&button);
row
}
$ cargo run
Sorry it's not in C. But you can instead use this equivalent Builder definition, if that's easier:
window.ui
<?xml version='1.0' encoding='UTF-8'?>
<interface>
<object class='GtkApplicationWindow' id='window'>
<child>
<object class='GtkListBox'>
<!-- first row -->
<child>
<object class='GtkBox'>
<child>
<object class='GtkLabel'>
<property mame='use-markup'>true</property>
<property name='label'>Here's a <a href='https://gnome.org'>link</a> and popover: </property>
</object>
</child>
<child>
<object class='GtkMenuButton'>
<property name='popover'>
<object class='GtkPopover'>
<child>
<object class='GtkLabel'>
<property mame='use-markup'>true</property>
<property name='label'>Test <a href='https://gnome.org'>link</a> should be readable</property>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
<!-- second row, copied from the first -->
<child>
<object class='GtkBox'>
<child>
<object class='GtkLabel'>
<property mame='use-markup'>true</property>
<property name='label'>Here's a <a href='https://gnome.org'>link</a> and popover: </property>
</object>
</child>
<child>
<object class='GtkMenuButton'>
<property name='popover'>
<object class='GtkPopover'>
<child>
<object class='GtkLabel'>
<property mame='use-markup'>true</property>
<property name='label'>Test <a href='https://gnome.org'>link</a> should be readable</property>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
Current behavior
When you open the popover for the selected row, the link text inside the popover is almost white and very hard to read. (In this sample you can see it is the same colour as the link that's directly on the blue selected background.) When you open the popover for the non-selected row, that link text is a normal readable blue colour.
Note that the behaviour is determined by the selected state of the list row when the popover is first opened: dismissing it, changing which row is selected, and opening it again will not change the link's colour.
Expected outcome
The link text inside the popover should be the regular blue colour.
Version information
GTK version: 4.16.5 (package gtk4-4.16.5-2.fc41.x86_64
)
OS: Fedora 41
Windowing system: Wayland
Additional information
At gtk/theme/Default/_common.scss:1005 the theme effectively sets CSS rules
link {
color: $link_color;
}
*:selected link {
color: mix($selected_fg_color, $link_color, 80%);
}
so that a link with any "selected" ancestor will be light-coloured, suitable for display against the blue selection background colour. However, a Popover is a child with its own background, so the default $link_color
should be used instead when it is a closer ancestor than the list view. It's always the nearest ancestor setting a background colour that matters, since popovers or other widgets with backgrounds and selectable widgets can be nested arbitrarily deeply in any order.
I think this can be described correctly using CSS variables (custom properties) like this:
.background {
--link-color: $link_color;
}
:selected {
--link-color: mix($selected_fg_color, $link_color, 80%);
}
link {
color: var(--link-color);
}
If that isn't acceptable (I suppose it could have some performance impact though I'm not sure how to measure it), at least one layer of nesting could be supported by adding an overriding rule such as
link {
color: $link_color;
@@ ... other states omitted ... @@
@at-root %link_selected,
&:selected,
*:selected & { color: mix($selected_fg_color, $link_color, 80%); }
+ /* Support one layer of nesting popovers: */
+ *:selected .background & { color: $link_color; }
}
(similarly for the hover, visited, and active states omitted in this snippet)