mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-01 16:30:15 +00:00
Merge branch 'css-focus-changes' into 'master'
Add a focus-within state See merge request GNOME/gtk!1700
This commit is contained in:
commit
1d1e1c5e83
@ -273,8 +273,9 @@ notebook tab:first-child label {
|
||||
:disabled, :selected, :focus, :indeterminate, :checked and :backdrop.
|
||||
In addition, the following pseudo-classes don't have a direct equivalent
|
||||
as a widget state: :dir(ltr) and :dir(rtl) (for text direction), :link and
|
||||
:visited (for links) and :drop(active) (for highlighting drop targets).
|
||||
Widget state pseudo-classes may only apply to the last element in a selector.
|
||||
:visited (for links), :drop(active) (for highlighting drop targets) and
|
||||
:focus-visible (for visible focus indication). Widget state pseudo-classes
|
||||
may only apply to the last element in a selector.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
|
@ -1282,6 +1282,8 @@ gtk_css_selector_parse_selector_pseudo_class (GtkCssParser *parser,
|
||||
{ "link", GTK_STATE_FLAG_LINK, },
|
||||
{ "visited", GTK_STATE_FLAG_VISITED, },
|
||||
{ "checked", GTK_STATE_FLAG_CHECKED, },
|
||||
{ "focus-visible", GTK_STATE_FLAG_FOCUS_VISIBLE, },
|
||||
{ "focus-within", GTK_STATE_FLAG_FOCUS_WITHIN, },
|
||||
};
|
||||
guint i;
|
||||
|
||||
@ -1448,19 +1450,6 @@ gtk_css_selector_parse_selector_pseudo_class (GtkCssParser *parser,
|
||||
selector);
|
||||
selector->state.state = GTK_STATE_FLAG_DROP_ACTIVE;
|
||||
}
|
||||
else if (gtk_css_token_is_function (token, "focus"))
|
||||
{
|
||||
if (!gtk_css_parser_consume_function (parser, 1, 1, parse_identifier_arg, (gpointer) "visible"))
|
||||
{
|
||||
if (selector)
|
||||
_gtk_css_selector_free (selector);
|
||||
return NULL;
|
||||
}
|
||||
selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE
|
||||
: >K_CSS_SELECTOR_PSEUDOCLASS_STATE,
|
||||
selector);
|
||||
selector->state.state = GTK_STATE_FLAG_FOCUS_VISIBLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_css_parser_error (parser,
|
||||
|
@ -232,7 +232,8 @@ gtk_css_pseudoclass_name (GtkStateFlags state)
|
||||
"visited",
|
||||
"checked",
|
||||
"drop(active)",
|
||||
"focus(visible)"
|
||||
"focus-visible",
|
||||
"focus-within"
|
||||
};
|
||||
guint i;
|
||||
|
||||
|
@ -736,6 +736,7 @@ typedef enum
|
||||
* @GTK_STATE_FLAG_CHECKED: Widget is checked
|
||||
* @GTK_STATE_FLAG_DROP_ACTIVE: Widget is highlighted as a drop target for DND
|
||||
* @GTK_STATE_FLAG_FOCUS_VISIBLE: Widget has the visible focus
|
||||
* @GTK_STATE_FLAG_FOCUS_WITHIN: Widget contains the keyboard focus
|
||||
*
|
||||
* Describes a widget state. Widget states are used to match the widget
|
||||
* against CSS pseudo-classes. Note that GTK extends the regular CSS
|
||||
@ -757,7 +758,8 @@ typedef enum
|
||||
GTK_STATE_FLAG_VISITED = 1 << 10,
|
||||
GTK_STATE_FLAG_CHECKED = 1 << 11,
|
||||
GTK_STATE_FLAG_DROP_ACTIVE = 1 << 12,
|
||||
GTK_STATE_FLAG_FOCUS_VISIBLE = 1 << 13
|
||||
GTK_STATE_FLAG_FOCUS_VISIBLE = 1 << 13,
|
||||
GTK_STATE_FLAG_FOCUS_WITHIN = 1 << 14
|
||||
} GtkStateFlags;
|
||||
|
||||
/**
|
||||
|
@ -45,7 +45,7 @@ typedef gboolean (*GtkSurfaceTransformChangedCallback) (GtkWidget
|
||||
const graphene_matrix_t *surface_transform,
|
||||
gpointer user_data);
|
||||
|
||||
#define GTK_STATE_FLAGS_BITS 14
|
||||
#define GTK_STATE_FLAGS_BITS 15
|
||||
|
||||
typedef struct _GtkWidgetSurfaceTransformData
|
||||
{
|
||||
|
@ -5568,7 +5568,7 @@ synthesize_focus_change_events (GtkWindow *window,
|
||||
else
|
||||
ancestor = NULL;
|
||||
|
||||
flags = GTK_STATE_FLAG_FOCUSED;
|
||||
flags = GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_FOCUS_WITHIN;
|
||||
if (gtk_window_get_focus_visible (GTK_WINDOW (window)))
|
||||
flags |= GTK_STATE_FLAG_FOCUS_VISIBLE;
|
||||
|
||||
@ -5612,8 +5612,14 @@ synthesize_focus_change_events (GtkWindow *window,
|
||||
gtk_widget_set_focus_child (widget, NULL);
|
||||
prev = widget;
|
||||
widget = gtk_widget_get_parent (widget);
|
||||
|
||||
flags = flags & ~GTK_STATE_FLAG_FOCUSED;
|
||||
}
|
||||
|
||||
flags = GTK_STATE_FLAG_FOCUS_WITHIN;
|
||||
if (gtk_window_get_focus_visible (GTK_WINDOW (window)))
|
||||
flags |= GTK_STATE_FLAG_FOCUS_VISIBLE;
|
||||
|
||||
list = NULL;
|
||||
for (widget = new_focus; widget; widget = gtk_widget_get_parent (widget))
|
||||
list = g_list_prepend (list, widget);
|
||||
@ -5652,6 +5658,10 @@ synthesize_focus_change_events (GtkWindow *window,
|
||||
}
|
||||
check_crossing_invariants (widget, &crossing);
|
||||
gtk_widget_handle_crossing (widget, &crossing, 0, 0);
|
||||
|
||||
if (l->next == NULL)
|
||||
flags = flags | GTK_STATE_FLAG_FOCUSED;
|
||||
|
||||
gtk_widget_set_state_flags (widget, flags, FALSE);
|
||||
gtk_widget_set_focus_child (widget, focus_child);
|
||||
}
|
||||
|
@ -42,10 +42,10 @@ dnd {
|
||||
* Outlines *
|
||||
********/
|
||||
|
||||
:focus(visible) link,
|
||||
label:focus(visible):not(.link),
|
||||
row:focus(visible),
|
||||
flowboxchild:focus(visible) {
|
||||
:focus-visible link,
|
||||
label:focus-visible:not(.link),
|
||||
row:focus-visible,
|
||||
flowboxchild:focus-visible {
|
||||
// We use the outline properties to signal the focus properties
|
||||
// to the adwaita engine: using real CSS properties is faster,
|
||||
// and we don't use any outlines for now.
|
||||
@ -58,15 +58,15 @@ flowboxchild:focus(visible) {
|
||||
}
|
||||
|
||||
// Widgets that draw their focus indicator outset and not inset
|
||||
plane:focus(visible),
|
||||
scale:focus(visible) > trough {
|
||||
plane:focus-visible,
|
||||
scale:focus-visible > trough {
|
||||
outline-color: $focus_border_color;
|
||||
outline-style: solid;
|
||||
outline-offset: 10px;
|
||||
outline-width: 2px;
|
||||
}
|
||||
|
||||
button:focus(visible), modelbutton:focus(visible) {
|
||||
button:focus-visible, modelbutton:focus-visible {
|
||||
outline-color: $focus_border_color;
|
||||
outline-style: solid;
|
||||
outline-offset: -2px;
|
||||
@ -78,7 +78,7 @@ button:focus(visible), modelbutton:focus(visible) {
|
||||
}
|
||||
|
||||
// Draw the "outline" around the whole switch not the slider
|
||||
switch:focus(visible) {
|
||||
switch:focus-visible {
|
||||
&, &:hover { slider { outline-color: transparent; } }
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 3px if($variant=='light', lighten(opacify($focus_border_color, 1), 20%), $focus_border_color);
|
||||
@ -86,8 +86,8 @@ switch:focus(visible) {
|
||||
row:selected & { outline-color: $alt_focus_border_color; }
|
||||
}
|
||||
|
||||
checkbutton:focus(visible),
|
||||
radiobutton:focus(visible) {
|
||||
checkbutton:focus-visible,
|
||||
radiobutton:focus-visible {
|
||||
outline-color: $focus_border_color;
|
||||
outline-style: solid;
|
||||
outline-offset: 2px;
|
||||
@ -96,7 +96,7 @@ radiobutton:focus(visible) {
|
||||
row:selected & , treeview:selected & { outline-color: $alt_focus_border_color; }
|
||||
}
|
||||
|
||||
row:focus(visible) {
|
||||
row:focus-visible {
|
||||
outline-color: $focus_border_color;
|
||||
outline-offset: -2px;
|
||||
outline-style: solid;
|
||||
@ -105,7 +105,7 @@ row:focus(visible) {
|
||||
}
|
||||
}
|
||||
|
||||
treeview:focus(visible) {
|
||||
treeview:focus-visible {
|
||||
outline-color: $focus_border_color;
|
||||
outline-style: solid;
|
||||
outline-width: 2px;
|
||||
@ -114,7 +114,7 @@ treeview:focus(visible) {
|
||||
}
|
||||
}
|
||||
|
||||
notebook:focus(visible) {
|
||||
notebook:focus:focus-visible {
|
||||
outline-color: $focus_border_color;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
@ -383,7 +383,7 @@ entry {
|
||||
}
|
||||
|
||||
&.flat {
|
||||
&:focus, &:backdrop, &:disabled, &:backdrop:disabled, & {
|
||||
&:focus-within, &:backdrop, &:disabled, &:backdrop:disabled, & {
|
||||
min-height: 0;
|
||||
padding: 2px;
|
||||
background-color: transparent;
|
||||
@ -392,7 +392,7 @@ entry {
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
&:focus-within {
|
||||
@include entry(focus);
|
||||
> placeholder {
|
||||
opacity: 0; /* We hide placeholders on focus */
|
||||
@ -418,7 +418,7 @@ entry {
|
||||
color: $e_color;
|
||||
border-color: entry_focus_border($e_color);
|
||||
|
||||
&:focus { @include entry(focus, $e_color); }
|
||||
&:focus-within { @include entry(focus, $e_color); }
|
||||
|
||||
> selection { background-color: $e_color; }
|
||||
}
|
||||
@ -439,7 +439,7 @@ entry {
|
||||
}
|
||||
|
||||
&:drop(active) {
|
||||
&:focus, & {
|
||||
&:focus-within, & {
|
||||
border-color: $drop_target_color;
|
||||
box-shadow: inset 0 0 0 1px $drop_target_color;
|
||||
}
|
||||
@ -448,7 +448,7 @@ entry {
|
||||
.osd & {
|
||||
@include entry(osd);
|
||||
|
||||
&:focus { @include entry(osd-focus); }
|
||||
&:focus-within { @include entry(osd-focus); }
|
||||
|
||||
&:backdrop { @include entry(osd-backdrop); }
|
||||
|
||||
@ -474,13 +474,13 @@ entry {
|
||||
|
||||
// linked entries
|
||||
.linked:not(.vertical) > & { @extend %linked; }
|
||||
.linked:not(.vertical) > &:focus + &,
|
||||
.linked:not(.vertical) > &:focus + button,
|
||||
.linked:not(.vertical) > &:focus + combobox > box > button.combo { border-left-color: entry_focus_border(); }
|
||||
.linked:not(.vertical) > &:focus-within + &,
|
||||
.linked:not(.vertical) > &:focus-within + button,
|
||||
.linked:not(.vertical) > &:focus-within + combobox > box > button.combo { border-left-color: entry_focus_border(); }
|
||||
|
||||
.linked:not(.vertical) > &:focus.error + &,
|
||||
.linked:not(.vertical) > &:focus.error + button,
|
||||
.linked:not(.vertical) > &:focus.error + combobox > box > button.combo { border-left-color: entry_focus_border($error_color); }
|
||||
.linked:not(.vertical) > &:focus-within.error + &,
|
||||
.linked:not(.vertical) > &:focus-within.error + button,
|
||||
.linked:not(.vertical) > &:focus-within.error + combobox > box > button.combo { border-left-color: entry_focus_border($error_color); }
|
||||
|
||||
.linked:not(.vertical) > &:drop(active) + &,
|
||||
.linked:not(.vertical) > &:drop(active) + button,
|
||||
@ -505,25 +505,25 @@ entry {
|
||||
|
||||
// color back the top border of a linked focused entry following another entry.
|
||||
// :not(:only-child) is a specificity bump hack.
|
||||
+ %entry:focus:not(:only-child),
|
||||
+ entry:focus:not(:only-child) { border-top-color: entry_focus_border(); }
|
||||
+ %entry:focus-within:not(:only-child),
|
||||
+ entry:focus-within:not(:only-child) { border-top-color: entry_focus_border(); }
|
||||
|
||||
+ %entry:focus.error:not(:only-child),
|
||||
+ entry:focus.error:not(:only-child) { border-top-color: entry_focus_border($error_color); }
|
||||
+ %entry:focus-within.error:not(:only-child),
|
||||
+ entry:focus-within.error:not(:only-child) { border-top-color: entry_focus_border($error_color); }
|
||||
|
||||
+ %entry:drop(active):not(:only-child),
|
||||
+ entry:drop(active):not(:only-child) { border-top-color: $drop_target_color; }
|
||||
|
||||
// this takes care of coloring the top border of the focused entry subsequent widget.
|
||||
// :not(:only-child) is a specificity bump hack.
|
||||
&:focus:not(:only-child) {
|
||||
&:focus-within:not(:only-child) {
|
||||
+ %entry,
|
||||
+ entry,
|
||||
+ button,
|
||||
+ combobox > box > button.combo { border-top-color: entry_focus_border(); }
|
||||
}
|
||||
|
||||
&:focus.error:not(:only-child) {
|
||||
&:focus-within.error:not(:only-child) {
|
||||
+ %entry,
|
||||
+ entry,
|
||||
+ button,
|
||||
@ -540,7 +540,7 @@ entry {
|
||||
}
|
||||
|
||||
treeview entry {
|
||||
&:focus {
|
||||
&:focus-within {
|
||||
&:dir(rtl), &:dir(ltr) { // specificity bump hack
|
||||
background-color: $base_color;
|
||||
transition-property: color, background;
|
||||
@ -552,7 +552,7 @@ treeview entry {
|
||||
background-image: none;
|
||||
background-color: $base_color;
|
||||
|
||||
&:focus { border-color: $selected_bg_color; }
|
||||
&:focus-within { border-color: $selected_bg_color; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -3979,7 +3979,7 @@ colorswatch {
|
||||
}
|
||||
}
|
||||
|
||||
&:focus(visible) {
|
||||
&:focus-visible {
|
||||
outline-offset: -2px;
|
||||
outline-width: 2px;
|
||||
outline-style: solid;
|
||||
|
@ -47,7 +47,7 @@ const char *css =
|
||||
" background-color: white;"
|
||||
" box-shadow: none;"
|
||||
"}"
|
||||
"focuswidget button:focus(visible) {"
|
||||
"focuswidget button:focus-visible {"
|
||||
" outline-width: 4px;"
|
||||
" outline-color: yellow;"
|
||||
"}"
|
||||
|
@ -86,6 +86,34 @@ done:
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void
|
||||
check_focus_states (GtkWidget *focus_widget)
|
||||
{
|
||||
GtkStateFlags state;
|
||||
GtkWidget *parent;
|
||||
|
||||
if (focus_widget == NULL)
|
||||
return;
|
||||
|
||||
/* Check that we set :focus and :focus-within on the focus_widget,
|
||||
* and :focus-within on its ancestors
|
||||
*/
|
||||
|
||||
state = gtk_widget_get_state_flags (focus_widget);
|
||||
g_assert_true ((state & (GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_FOCUS_WITHIN)) ==
|
||||
(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_FOCUS_WITHIN));
|
||||
|
||||
parent = gtk_widget_get_parent (focus_widget);
|
||||
while (parent)
|
||||
{
|
||||
state = gtk_widget_get_state_flags (parent);
|
||||
g_assert_true ((state & GTK_STATE_FLAG_FOCUS_WITHIN) == GTK_STATE_FLAG_FOCUS_WITHIN);
|
||||
g_assert_true ((state & GTK_STATE_FLAG_FOCUSED) == 0);
|
||||
|
||||
parent = gtk_widget_get_parent (parent);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
generate_focus_chain (GtkWidget *window,
|
||||
GtkDirectionType dir)
|
||||
@ -109,6 +137,8 @@ generate_focus_chain (GtkWidget *window,
|
||||
|
||||
focus = gtk_window_get_focus (GTK_WINDOW (window));
|
||||
|
||||
check_focus_states (focus);
|
||||
|
||||
if (focus)
|
||||
{
|
||||
/* ui files can't put a name on the embedded text,
|
||||
|
Loading…
Reference in New Issue
Block a user