[libadwaita/wip/exalm/borderless] 123




commit e4d1671f385d91c2ffce60151815cca796a9b5ea
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Tue Jun 29 18:50:18 2021 +0500

    123

 examples/adw-view-switcher-demo-window.ui  | 273 ++++++---
 src/adw-split-button.c                     | 890 +++++++++++++++++++++++++++++
 src/adw-split-button.h                     |  74 +++
 src/adwaita.h                              |   1 +
 src/meson.build                            |   2 +
 src/stylesheet/widgets/_buttons.scss       |  82 ++-
 src/stylesheet/widgets/_header-bar.scss    |  57 +-
 src/stylesheet/widgets/_linked.scss        |   1 +
 src/stylesheet/widgets/_toolbars.scss      |  66 ++-
 src/stylesheet/widgets/_view-switcher.scss |   4 +-
 10 files changed, 1306 insertions(+), 144 deletions(-)
---
diff --git a/examples/adw-view-switcher-demo-window.ui b/examples/adw-view-switcher-demo-window.ui
index a56b2218..97e0a5cd 100644
--- a/examples/adw-view-switcher-demo-window.ui
+++ b/examples/adw-view-switcher-demo-window.ui
@@ -40,37 +40,20 @@
             <child type="end">
               <object class="GtkMenuButton">
                 <property name="icon-name">open-menu-symbolic</property>
-                <property name="popover">
-                  <object class="GtkPopover"/>
-                </property>
+                <property name="menu-model">menu</property>
               </object>
             </child>
             <child type="end">
               <object class="GtkMenuButton">
                 <property name="icon-name">document-open-symbolic</property>
                 <property name="always-show-arrow">True</property>
-                <property name="popover">
-                  <object class="GtkPopover"/>
-                </property>
+                <property name="menu-model">menu</property>
               </object>
             </child>
             <child type="end">
-              <object class="GtkBox">
-                <style>
-                  <class name="linked2"/>
-                </style>
-                <child>
-                  <object class="GtkButton">
-                    <property name="icon-name">media-playback-start-symbolic</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkMenuButton">
-                    <property name="popover">
-                      <object class="GtkPopover"/>
-                    </property>
-                  </object>
-                </child>
+              <object class="AdwSplitButton">
+                <property name="icon-name">media-playback-start-symbolic</property>
+                <property name="menu-model">menu</property>
               </object>
             </child>
           </object>
@@ -113,8 +96,9 @@
               </object>
             </child>
             <child>
-              <object class="GtkButton">
+              <object class="GtkMenuButton">
                 <property name="icon-name">view-more-symbolic</property>
+                <property name="menu-model">menu</property>
               </object>
             </child>
           </object>
@@ -135,80 +119,92 @@
             </style>
           </object>
         </child>
-        <child>
-          <object class="GtkSeparator">
-            <style>
-              <class name="sidebar"/>
-            </style>
-          </object>
-        </child>
         <child>
           <object class="GtkBox">
-            <style>
-              <class name="toolbar"/>
-            </style>
+            <property name="spacing">12</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="vexpand">True</property>
             <child>
               <object class="GtkBox">
-                <style>
-                  <class name="linked2"/>
-                </style>
+                <property name="spacing">12</property>
+                <property name="orientation">vertical</property>
                 <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-text-bold-symbolic</property>
+                  <object class="AdwSplitButton">
+                    <property name="label">Button</property>
+                    <property name="menu-model">menu</property>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-text-italic-symbolic</property>
+                  <object class="AdwSplitButton">
+                    <property name="label">Button</property>
+                    <property name="menu-model">menu</property>
+                    <style>
+                      <class name="flat"/>
+                    </style>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-text-underline-symbolic</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-text-strikethrough-symbolic</property>
+                  <object class="AdwSplitButton">
+                    <property name="label">Button</property>
+                    <property name="menu-model">menu</property>
+                    <style>
+                      <class name="outline"/>
+                    </style>
                   </object>
                 </child>
               </object>
             </child>
             <child>
               <object class="GtkBox">
-                <style>
-                  <class name="linked2"/>
-                </style>
+                <property name="spacing">12</property>
+                <property name="orientation">vertical</property>
                 <child>
-                  <object class="GtkToggleButton" id="bold">
-                    <property name="icon-name">format-justify-left-symbolic</property>
-                    <property name="active">True</property>
+                  <object class="AdwSplitButton">
+                    <property name="icon-name">tab-new-symbolic</property>
+                    <property name="menu-model">menu</property>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-justify-center-symbolic</property>
-                    <property name="group">bold</property>
+                  <object class="AdwSplitButton">
+                    <property name="icon-name">tab-new-symbolic</property>
+                    <property name="menu-model">menu</property>
+                    <style>
+                      <class name="flat"/>
+                    </style>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-justify-right-symbolic</property>
-                    <property name="group">bold</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkToggleButton">
-                    <property name="icon-name">format-justify-fill-symbolic</property>
-                    <property name="group">bold</property>
+                  <object class="AdwSplitButton">
+                    <property name="icon-name">tab-new-symbolic</property>
+                    <property name="menu-model">menu</property>
+                    <style>
+                      <class name="outline"/>
+                    </style>
                   </object>
                 </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <style>
+                  <class name="toolbar"/>
+                </style>
+                <property name="spacing">6</property>
+                <property name="orientation">vertical</property>
                 <child>
-                  <object class="GtkVolumeButton">
+                  <object class="AdwSplitButton">
+                    <property name="label">Button</property>
+                    <property name="menu-model">menu</property>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkVolumeButton">
+                  <object class="AdwSplitButton">
+                    <property name="label">Button</property>
+                    <property name="menu-model">menu</property>
+                    <style>
+                      <class name="flat"/>
+                    </style>
                   </object>
                 </child>
               </object>
@@ -216,20 +212,112 @@
             <child>
               <object class="GtkBox">
                 <style>
-                  <class name="linked2"/>
+                  <class name="toolbar"/>
                 </style>
+                <property name="spacing">6</property>
+                <property name="orientation">vertical</property>
                 <child>
-                  <object class="GtkButton">
-                    <property name="icon-name">format-indent-less-symbolic</property>
+                  <object class="AdwSplitButton">
+                    <property name="icon-name">tab-new-symbolic</property>
+                    <property name="menu-model">menu</property>
+                    <style>
+                      <class name="raised"/>
+                    </style>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkButton">
-                    <property name="icon-name">format-indent-more-symbolic</property>
+                  <object class="AdwSplitButton">
+                    <property name="icon-name">tab-new-symbolic</property>
+                    <property name="menu-model">menu</property>
                   </object>
                 </child>
               </object>
             </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSeparator">
+            <style>
+              <class name="sidebar"/>
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <style>
+              <class name="toolbar"/>
+            </style>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-text-bold-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-text-italic-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-text-underline-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-text-strikethrough-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkSeparator"/>
+            </child>
+            <child>
+              <object class="GtkToggleButton" id="bold">
+                <property name="icon-name">format-justify-left-symbolic</property>
+                <property name="active">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-justify-center-symbolic</property>
+                <property name="group">bold</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-justify-right-symbolic</property>
+                <property name="group">bold</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton">
+                <property name="icon-name">format-justify-fill-symbolic</property>
+                <property name="group">bold</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkVolumeButton">
+              </object>
+            </child>
+            <child>
+              <object class="GtkVolumeButton">
+              </object>
+            </child>
+            <child>
+              <object class="GtkSeparator"/>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="icon-name">format-indent-less-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="icon-name">format-indent-more-symbolic</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkSeparator"/>
+            </child>
             <child>
               <object class="GtkButton">
                 <property name="icon-name">insert-image-symbolic</property>
@@ -252,9 +340,7 @@
             <child type="start">
               <object class="GtkMenuButton">
                 <property name="label">Open</property>
-                <property name="popover">
-                  <object class="GtkPopover"/>
-                </property>
+                <property name="menu-model">menu</property>
               </object>
             </child>
             <child type="start">
@@ -271,23 +357,11 @@
               <object class="GtkButton">
                 <property name="label">Properties</property>
               </object>
-            </child>            <child type="end">
-              <object class="GtkBox">
-                <style>
-                  <class name="linked"/>
-                </style>
-                <child>
-                  <object class="GtkButton">
-                    <property name="label">Open</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkMenuButton">
-                    <property name="popover">
-                      <object class="GtkPopover"/>
-                    </property>
-                  </object>
-                </child>
+            </child>
+            <child type="end">
+              <object class="AdwSplitButton">
+                <property name="label">Open</property>
+                <property name="menu-model">menu</property>
               </object>
             </child>
           </object>
@@ -295,4 +369,19 @@
       </object>
     </property>
   </template>
+  <menu id="menu">
+    <section>
+      <item>
+        <attribute name="label">Something</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label">Preferences</attribute>
+      </item>
+      <item>
+        <attribute name="label">About Demo</attribute>
+      </item>
+    </section>
+  </menu>
 </interface>
diff --git a/src/adw-split-button.c b/src/adw-split-button.c
new file mode 100644
index 00000000..4d299362
--- /dev/null
+++ b/src/adw-split-button.c
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "config.h"
+
+#include "adw-split-button.h"
+
+/**
+ * AdwSplitButton:
+ *
+ * A combined button and dropdown widget.
+ *
+ * `AdwSplitButton` is typically used to present a set of actions in a menu,
+ * but allow access to one of them with a single click.
+ *
+ * The API is very similar to [class@Gtk.Button] and [class@Gtk.MenuButton], see
+ * their documentation for details.
+ *
+ * ## CSS nodes
+ *
+ * ```
+ * splitbutton[.image-button][.text-button]
+ * ├── button
+ * │   ╰── <content>
+ * ├── separator
+ * ╰── menubutton
+ *     ╰── button.toggle
+ *         ╰── arrow
+ * ```
+ *
+ * `AdwSplitButton`'s CSS node is called `splitbutton`. It contains the css
+ * nodes: `button`, `separator`, `menubutton`. See [class@Gtk.MenuButton]
+ * documentation for the `menubutton` contents.
+ *
+ * The main CSS node will contain the `.image-button` or `.text-button` style
+ * classes matching the button contents. The nested button nodes will never
+ * contain them.
+ *
+ * ## Accessibility
+ *
+ * `AdwSplitButton` uses the `GTK_ACCESSIBLE_ROLE_BUTTON` role.
+ *
+ * Since: 1.0
+ */
+
+enum {
+  PROP_0,
+  PROP_LABEL,
+  PROP_USE_UNDERLINE,
+  PROP_ICON_NAME,
+  PROP_CHILD,
+  PROP_MENU_MODEL,
+  PROP_POPOVER,
+  PROP_DIRECTION,
+
+  /* actionable properties */
+  PROP_ACTION_NAME,
+  PROP_ACTION_TARGET,
+  LAST_PROP = PROP_ACTION_NAME
+};
+
+static GParamSpec *props[LAST_PROP];
+
+enum {
+  SIGNAL_CLICKED,
+  SIGNAL_ACTIVATE,
+  SIGNAL_LAST_SIGNAL
+};
+
+static guint signals[SIGNAL_LAST_SIGNAL];
+
+struct _AdwSplitButton
+{
+  GtkWidget parent_instance;
+
+  GtkWidget *button;
+  GtkWidget *menu_button;
+  GtkWidget *arrow_button;
+  GtkWidget *separator;
+};
+
+static void adw_split_button_actionable_init (GtkActionableInterface *iface);
+static void adw_split_button_buildable_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (AdwSplitButton, adw_split_button, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, adw_split_button_actionable_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, adw_split_button_buildable_init))
+
+static GtkBuildableIface *parent_buildable_iface;
+
+static void
+update_state (AdwSplitButton *self)
+{
+  GtkStateFlags flags;
+  gboolean keyboard_activating;
+
+  flags = gtk_widget_get_state_flags (self->button) |
+          gtk_widget_get_state_flags (self->arrow_button);
+
+  keyboard_activating =
+    gtk_widget_has_css_class (self->button, "keyboard-activating") ||
+    gtk_widget_has_css_class (self->arrow_button, "keyboard-activating");
+
+  if (flags & GTK_STATE_FLAG_ACTIVE || keyboard_activating)
+    gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE, FALSE);
+  else
+    gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
+
+  if (flags & GTK_STATE_FLAG_CHECKED)
+    gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_CHECKED, FALSE);
+  else
+    gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_CHECKED);
+}
+
+static void
+update_style_classes (AdwSplitButton *self)
+{
+  const char *icon_name = gtk_button_get_icon_name (GTK_BUTTON (self->button));
+  const char *label = gtk_button_get_label (GTK_BUTTON (self->button));
+
+  if (icon_name && icon_name[0])
+    gtk_widget_add_css_class (GTK_WIDGET (self), "image-button");
+  else
+    gtk_widget_remove_css_class (GTK_WIDGET (self), "image-button");
+
+  if (label && label[0])
+    gtk_widget_add_css_class (GTK_WIDGET (self), "text-button");
+  else
+    gtk_widget_remove_css_class (GTK_WIDGET (self), "text-button");
+
+  gtk_widget_remove_css_class (self->button, "text-button");
+  gtk_widget_remove_css_class (self->button, "image-button");
+
+  gtk_widget_remove_css_class (self->arrow_button, "arrow-button");
+  gtk_widget_remove_css_class (self->arrow_button, "image-button");
+}
+
+static void
+clicked_cb (AdwSplitButton *self)
+{
+  g_signal_emit (self, signals[SIGNAL_CLICKED], 0);
+}
+
+static void
+activate_cb (AdwSplitButton *self)
+{
+  g_signal_emit_by_name (self->button, "activate");
+}
+
+static void
+adw_split_button_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (object);
+
+  switch (prop_id) {
+  case PROP_LABEL:
+    g_value_set_string (value, adw_split_button_get_label (self));
+    break;
+  case PROP_USE_UNDERLINE:
+    g_value_set_boolean (value, adw_split_button_get_use_underline (self));
+    break;
+  case PROP_ICON_NAME:
+    g_value_set_string (value, adw_split_button_get_icon_name (self));
+    break;
+  case PROP_CHILD:
+    g_value_set_object (value, adw_split_button_get_child (self));
+    break;
+  case PROP_MENU_MODEL:
+    g_value_set_object (value, adw_split_button_get_menu_model (self));
+    break;
+  case PROP_POPOVER:
+    g_value_set_object (value, adw_split_button_get_popover (self));
+    break;
+  case PROP_DIRECTION:
+    g_value_set_enum (value, adw_split_button_get_direction (self));
+    break;
+  case PROP_ACTION_NAME:
+    g_value_set_string (value, gtk_actionable_get_action_name (GTK_ACTIONABLE (self)));
+    break;
+  case PROP_ACTION_TARGET:
+    g_value_set_variant (value, gtk_actionable_get_action_target_value (GTK_ACTIONABLE (self)));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+adw_split_button_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (object);
+
+  switch (prop_id) {
+  case PROP_LABEL:
+    adw_split_button_set_label (self, g_value_get_string (value));
+    break;
+  case PROP_USE_UNDERLINE:
+    adw_split_button_set_use_underline (self, g_value_get_boolean (value));
+    break;
+  case PROP_ICON_NAME:
+    adw_split_button_set_icon_name (self, g_value_get_string (value));
+    break;
+  case PROP_CHILD:
+    adw_split_button_set_child (self, g_value_get_object (value));
+    break;
+  case PROP_MENU_MODEL:
+    adw_split_button_set_menu_model (self, g_value_get_object (value));
+    break;
+  case PROP_POPOVER:
+    adw_split_button_set_popover (self, g_value_get_object (value));
+    break;
+  case PROP_DIRECTION:
+    adw_split_button_set_direction (self, g_value_get_enum (value));
+    break;
+  case PROP_ACTION_NAME:
+    gtk_actionable_set_action_name (GTK_ACTIONABLE (self), g_value_get_string (value));
+    break;
+  case PROP_ACTION_TARGET:
+    gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self), g_value_get_variant (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+adw_split_button_dispose (GObject *object)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (object);
+
+  g_clear_pointer (&self->button, gtk_widget_unparent);
+  g_clear_pointer (&self->menu_button, gtk_widget_unparent);
+  g_clear_pointer (&self->separator, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (adw_split_button_parent_class)->dispose (object);
+}
+
+static void
+adw_split_button_class_init (AdwSplitButtonClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->get_property = adw_split_button_get_property;
+  object_class->set_property = adw_split_button_set_property;
+  object_class->dispose = adw_split_button_dispose;
+
+  /**
+   * AdwSplitButton:label: (attributes org.gtk.Property.get=adw_split_button_get_label 
org.gtk.Property.set=adw_split_button_set_label)
+   *
+   * The label for the button.
+   *
+   * Setting the label will set [property@Adw.SplitButton:icon-name] and
+   * [property@Adw.SplitButton:child] to `NULL`.
+   *
+   * Since: 1.0
+   */
+  props[PROP_LABEL] =
+    g_param_spec_string ("label",
+                         "Label",
+                         "The label for the button",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwSplitButton:use-underline: (attributes org.gtk.Property.get=adw_split_button_get_use_underline 
org.gtk.Property.set=adw_split_button_set_use_underline)
+   *
+   * Whether an underline in the text indicates a mnemonic.
+   *
+   * See [property@Adw.SplitButton:label].
+   *
+   * Since: 1.0
+   */
+  props[PROP_USE_UNDERLINE] =
+    g_param_spec_boolean ("use-underline",
+                          "Use underline",
+                          "Whether an underline in the text indicates a mnemonic",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwSplitButton:icon-name: (attributes org.gtk.Property.get=adw_split_button_get_icon_name 
org.gtk.Property.set=adw_split_button_set_icon_name)
+   *
+   * The name of the icon used to automatically populate the button.
+   *
+   * Setting the icon name will set [property@Adw.SplitButton:label] and
+   * [property@Adw.SplitButton:child] to `NULL`.
+   *
+   * Since: 1.0
+   */
+  props[PROP_ICON_NAME] =
+    g_param_spec_string ("icon-name",
+                         "Icon Name",
+                         "The name of the icon used to automatically populate the button",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwSplitButton:child: (attributes org.gtk.Property.get=adw_split_button_get_child 
org.gtk.Property.set=adw_split_button_set_child)
+   *
+   * The child widget.
+   *
+   * Setting the child widget will set [property@Adw.SplitButton:label] and
+   * [property@Adw.SplitButton:icon-name] to `NULL`.
+   *
+   * Since: 1.0
+   */
+  props[PROP_CHILD] =
+    g_param_spec_object ("child",
+                         "Child",
+                         "The child widget",
+                         GTK_TYPE_WIDGET,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwSplitButton:menu-model: (attributes org.gtk.Property.get=adw_split_button_get_menu_model 
org.gtk.Property.set=adw_split_button_set_menu_model)
+   *
+   * The `GMenuModel` from which the popup will be created.
+   *
+   * If the menu model is `NULL`, the dropdown is disabled.
+   *
+   * A [class@Gtk.Popover] will be created from the menu model with
+   * [ctor@Gtk.PopoverMenu.new_from_model]. Actions will be connected
+   * as documented for this function.
+   *
+   * If [property@Adw.SplitButton:popover] is already set, it will be
+   * dissociated from the button, and the property is set to `NULL`.
+   *
+   * Since: 1.0
+   */
+  props[PROP_MENU_MODEL] =
+    g_param_spec_object ("menu-model",
+                         "Menu model",
+                         "The model from which the popup is made",
+                         G_TYPE_MENU_MODEL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwSplitButton:popover: (attributes org.gtk.Property.get=adw_split_button_get_popover 
org.gtk.Property.set=adw_split_button_set_popover)
+   *
+   * The `GtkPopover` that will be popped up when the dropdown is clicked.
+   *
+   * If the popover is `NULL`, the dropdown is disabled.
+   *
+   * If [property@Adw.SplitButton:menu-model] is set, the menu model is
+   * dissociated from the button, and the property is set to `NULL`.
+   *
+   * Since: 1.0
+   */
+  props[PROP_POPOVER] =
+    g_param_spec_object ("popover",
+                         "Popover",
+                         "The popover that will be popped up when the dropdown is clicked",
+                         GTK_TYPE_POPOVER,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwSplitButton:direction: (attributes org.gtk.Property.get=adw_split_button_get_direction 
org.gtk.Property.set=adw_split_button_set_direction)
+   *
+   * The direction in which the popup will be popped up.
+   *
+   * The dropdown arrow icon will point at the same direction.
+   *
+   * If the does not fit in the available space in the given direction,
+   * GTK will its best to keep it inside the screen and fully visible.
+   *
+   * If you pass `GTK_ARROW_NONE`, it's equivalent to `GTK_ARROW_DOWN`.
+   *
+   * Since: 1.0
+   */
+  props[PROP_DIRECTION] =
+    g_param_spec_enum ("direction",
+                       "Direction",
+                       "The direction in which the popup will be popped up",
+                       GTK_TYPE_ARROW_TYPE,
+                       GTK_ARROW_DOWN,
+                       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+
+  g_object_class_override_property (object_class, PROP_ACTION_NAME, "action-name");
+  g_object_class_override_property (object_class, PROP_ACTION_TARGET, "action-target");
+
+  /**
+   * AdwSplitButton::clicked:
+   *
+   * Emitted when the button has been activated (pressed and released).
+   */
+  signals[SIGNAL_CLICKED] =
+    g_signal_new ("clicked",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  /**
+   * AdwSplitButton::activate:
+   *
+   * Emitted to animate press then release.
+   *
+   * This is an action signal. Applications should never connect
+   * to this signal, but use the [signal@Adw.SplitButton::clicked] signal.
+   */
+  signals[SIGNAL_ACTIVATE] =
+    g_signal_new ("activate",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  gtk_widget_class_set_activate_signal (widget_class, signals[SIGNAL_ACTIVATE]);
+
+  g_signal_override_class_handler ("activate",
+                                   G_TYPE_FROM_CLASS (klass),
+                                   G_CALLBACK (activate_cb));
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
+  gtk_widget_class_set_css_name (widget_class, "splitbutton");
+  gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP);
+}
+
+#define NOTIFY(func, prop) \
+static void \
+func (GObject *object) { \
+  g_object_notify_by_pspec (object, props[prop]); \
+}
+
+NOTIFY (notify_label_cb, PROP_LABEL);
+NOTIFY (notify_icon_name_cb, PROP_ICON_NAME);
+NOTIFY (notify_child_cb, PROP_CHILD);
+NOTIFY (notify_use_underline_cb, PROP_USE_UNDERLINE);
+NOTIFY (notify_menu_model_cb, PROP_MENU_MODEL);
+NOTIFY (notify_popover_cb, PROP_POPOVER);
+NOTIFY (notify_direction_cb, PROP_DIRECTION);
+
+static void
+notify_action_name_cb (GObject *object)
+{
+  g_object_notify (object, "action-name");
+}
+
+static void
+notify_action_target_cb (GObject *object)
+{
+  g_object_notify (object, "action-target");
+}
+
+static void
+adw_split_button_init (AdwSplitButton *self)
+{
+  gtk_widget_set_hexpand (GTK_WIDGET (self), FALSE);
+
+  self->button = gtk_button_new ();
+  gtk_widget_set_parent (self->button, GTK_WIDGET (self));
+  gtk_widget_set_hexpand (self->button, TRUE);
+
+  self->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+  gtk_widget_set_parent (self->separator, GTK_WIDGET (self));
+
+  self->menu_button = gtk_menu_button_new ();
+  gtk_widget_set_parent (self->menu_button, GTK_WIDGET (self));
+
+  /* FIXME: This is iffy, but we don't have any other way to do it */
+  self->arrow_button = gtk_widget_get_first_child (self->menu_button);
+
+  g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (clicked_cb), self);
+
+  g_signal_connect_swapped (self->button, "notify::css-classes", G_CALLBACK (update_state), self);
+  g_signal_connect_swapped (self->button, "state-flags-changed", G_CALLBACK (update_state), self);
+  g_signal_connect_swapped (self->arrow_button, "notify::css-classes", G_CALLBACK (update_state), self);
+  g_signal_connect_swapped (self->arrow_button, "state-flags-changed", G_CALLBACK (update_state), self);
+
+  g_signal_connect_swapped (self->button, "notify::label", G_CALLBACK (notify_label_cb), self);
+  g_signal_connect_swapped (self->button, "notify::icon-name", G_CALLBACK (notify_icon_name_cb), self);
+  g_signal_connect_swapped (self->button, "notify::child", G_CALLBACK (notify_child_cb), self);
+  g_signal_connect_swapped (self->button, "notify::use-underline", G_CALLBACK (notify_use_underline_cb), 
self);
+  g_signal_connect_swapped (self->button, "notify::action-name", G_CALLBACK (notify_action_name_cb), self);
+  g_signal_connect_swapped (self->button, "notify::action-target", G_CALLBACK (notify_action_target_cb), 
self);
+  g_signal_connect_swapped (self->menu_button, "notify::menu-model", G_CALLBACK (notify_menu_model_cb), 
self);
+  g_signal_connect_swapped (self->menu_button, "notify::popover", G_CALLBACK (notify_popover_cb), self);
+  g_signal_connect_swapped (self->menu_button, "notify::direction", G_CALLBACK (notify_direction_cb), self);
+
+  g_object_bind_property (self->button, "sensitive",
+                          self, "sensitive",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+  update_style_classes (self);
+}
+
+static const char *
+adw_split_button_get_action_name (GtkActionable *actionable)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (actionable);
+
+  return gtk_actionable_get_action_name (GTK_ACTIONABLE (self->button));
+}
+
+static void
+adw_split_button_set_action_name (GtkActionable *actionable,
+                                  const char    *action_name)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (actionable);
+
+  gtk_actionable_set_action_name (GTK_ACTIONABLE (self->button), action_name);
+}
+
+static GVariant *
+adw_split_button_get_action_target_value (GtkActionable *actionable)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (actionable);
+
+  return gtk_actionable_get_action_target_value (GTK_ACTIONABLE (self->button));
+}
+
+static void
+adw_split_button_set_action_target_value (GtkActionable *actionable,
+                                          GVariant      *action_target)
+{
+  AdwSplitButton *self = ADW_SPLIT_BUTTON (actionable);
+
+  gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->button), action_target);
+}
+
+static void
+adw_split_button_actionable_init (GtkActionableInterface *iface)
+{
+  iface->get_action_name = adw_split_button_get_action_name;
+  iface->set_action_name = adw_split_button_set_action_name;
+  iface->get_action_target_value = adw_split_button_get_action_target_value;
+  iface->set_action_target_value = adw_split_button_set_action_target_value;
+}
+
+static void
+adw_split_button_buildable_add_child (GtkBuildable *buildable,
+                                      GtkBuilder   *builder,
+                                      GObject      *child,
+                                      const char   *type)
+{
+  if (GTK_IS_POPOVER (child))
+    adw_split_button_set_popover (ADW_SPLIT_BUTTON (buildable), GTK_POPOVER (child));
+  else if (GTK_IS_WIDGET (child))
+    adw_split_button_set_child (ADW_SPLIT_BUTTON (buildable), GTK_WIDGET (child));
+  else
+    parent_buildable_iface->add_child (buildable, builder, child, type);
+}
+
+static void
+adw_split_button_buildable_init (GtkBuildableIface *iface)
+{
+  parent_buildable_iface = g_type_interface_peek_parent (iface);
+
+  iface->add_child = adw_split_button_buildable_add_child;
+}
+
+/**
+ * adw_split_button_new:
+ *
+ * Creates a new `AdwSplitButton`.
+ *
+ * Returns: the newly created `AdwSplitButton`
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+adw_split_button_new (void)
+{
+  return g_object_new (ADW_TYPE_SPLIT_BUTTON, NULL);
+}
+
+/**
+ * adw_split_button_get_label: (attributes org.gtk.Method.get_property=label)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets the label for @self.
+ *
+ * Returns: (nullable): the label for @self
+ *
+ * Since: 1.0
+ */
+const char *
+adw_split_button_get_label (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), NULL);
+
+  return gtk_button_get_label (GTK_BUTTON (self->button));
+}
+
+/**
+ * adw_split_button_set_label: (attributes org.gtk.Method.set_property=label)
+ * @self: a `AdwSplitButton`
+ * @label: (nullable): the label to set
+ *
+ * Sets the label for @self.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_label (AdwSplitButton *self,
+                            const char     *label)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  gtk_button_set_label (GTK_BUTTON (self->button), label);
+
+  update_style_classes (self);
+}
+
+/**
+ * adw_split_button_get_use_underline: (attributes org.gtk.Method.set_property=use-underline)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets whether an underline in the text indicates a mnemonic.
+ *
+ * Returns: whether an underline in the text indicates a mnemonic
+ *
+ * Since: 1.0
+ */
+gboolean
+adw_split_button_get_use_underline (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), FALSE);
+
+  return gtk_button_get_use_underline (GTK_BUTTON (self->button));
+}
+
+/**
+ * adw_split_button_set_use_underline: (attributes org.gtk.Method.set_property=use-underline)
+ * @self: a `AdwSplitButton`
+ * @use_underline: whether an underline in the text indicates a mnemonic
+ *
+ * Sets whether an underline in the text indicates a mnemonic.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_use_underline (AdwSplitButton *self,
+                                    gboolean        use_underline)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  use_underline = !!use_underline;
+
+  if (use_underline == adw_split_button_get_use_underline (self))
+    return;
+
+  gtk_button_set_use_underline (GTK_BUTTON (self->button), use_underline);
+}
+
+/**
+ * adw_split_button_get_icon_name: (attributes org.gtk.Method.get_property=icon-name)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets the name of the icon used to automatically populate the button.
+ *
+ * Returns: (nullable): the icon name
+ *
+ * Since: 1.0
+ */
+const char *
+adw_split_button_get_icon_name (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), NULL);
+
+  return gtk_button_get_icon_name (GTK_BUTTON (self->button));
+}
+
+/**
+ * adw_split_button_set_icon_name: (attributes org.gtk.Method.set_property=icon-name)
+ * @self: a `AdwSplitButton`
+ * @icon_name: (nullable): the icon name to set
+ *
+ * Sets the name of the icon used to automatically populate the button.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_icon_name (AdwSplitButton *self,
+                                const char     *icon_name)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  gtk_button_set_icon_name (GTK_BUTTON (self->button), icon_name);
+
+  update_style_classes (self);
+}
+
+/**
+ * adw_split_button_get_child: (attributes org.gtk.Method.get_property=child)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets the child widget.
+ *
+ * Returns: (transfer none) (nullable): the child widget
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+adw_split_button_get_child (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), NULL);
+
+  return gtk_button_get_child (GTK_BUTTON (self->button));
+}
+
+/**
+ * adw_split_button_set_child: (attributes org.gtk.Method.set_property=child)
+ * @self: a `AdwSplitButton`
+ * @child: (nullable): the new child widget
+ *
+ * Sets the child widget.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_child (AdwSplitButton *self,
+                            GtkWidget      *child)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  if (child == adw_split_button_get_child (self))
+    return;
+
+  gtk_button_set_child (GTK_BUTTON (self->button), child);
+
+  update_style_classes (self);
+}
+
+/**
+ * adw_split_button_get_menu_model: (attributes org.gtk.Method.get_property=menu-model)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets the menu model from which the popup will be created.
+ *
+ * Returns: (transfer none) (nullable): the menu model
+ *
+ * Since: 1.0
+ */
+GMenuModel *
+adw_split_button_get_menu_model (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), NULL);
+
+  return gtk_menu_button_get_menu_model (GTK_MENU_BUTTON (self->menu_button));
+}
+
+/**
+ * adw_split_button_set_menu_model: (attributes org.gtk.Method.set_property=menu-model)
+ * @self: a `AdwSplitButton`
+ * @menu_model: (nullable): the menu model
+ *
+ * Sets the menu model from which the popup will be created.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_menu_model (AdwSplitButton *self,
+                                 GMenuModel     *menu_model)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  if (menu_model == adw_split_button_get_menu_model (self))
+    return;
+
+  gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (self->menu_button), menu_model);
+}
+
+/**
+ * adw_split_button_get_popover: (attributes org.gtk.Method.get_property=popover)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets the popover that will be popped up when the dropdown is clicked.
+ *
+ * Returns: (transfer none) (nullable): the popover
+ *
+ * Since: 1.0
+ */
+GtkPopover *
+adw_split_button_get_popover (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), NULL);
+
+  return gtk_menu_button_get_popover (GTK_MENU_BUTTON (self->menu_button));
+}
+
+/**
+ * adw_split_button_set_popover: (attributes org.gtk.Method.set_property=popover)
+ * @self: a `AdwSplitButton`
+ * @popover: (nullable): the popover
+ *
+ * Sets the popover that will be popped up when the dropdown is clicked.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_popover (AdwSplitButton *self,
+                              GtkPopover     *popover)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  if (popover == adw_split_button_get_popover (self))
+    return;
+
+  gtk_menu_button_set_popover (GTK_MENU_BUTTON (self->menu_button), GTK_WIDGET (popover));
+}
+
+/**
+ * adw_split_button_get_direction: (attributes org.gtk.Method.get_property=direction)
+ * @self: a `AdwSplitButton`
+ *
+ * Gets the direction in which the popup will be popped up.
+ *
+ * Returns: the direction
+ *
+ * Since: 1.0
+ */
+GtkArrowType
+adw_split_button_get_direction (AdwSplitButton *self)
+{
+  g_return_val_if_fail (ADW_IS_SPLIT_BUTTON (self), GTK_ARROW_DOWN);
+
+  return gtk_menu_button_get_direction (GTK_MENU_BUTTON (self->menu_button));
+}
+
+/**
+ * adw_split_button_set_direction: (attributes org.gtk.Method.set_property=direction)
+ * @self: a `AdwSplitButton`
+ * @direction: the direction
+ *
+ * Sets the direction in which the popup will be popped up.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_set_direction (AdwSplitButton *self,
+                                GtkArrowType    direction)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  if (direction == adw_split_button_get_direction (self))
+    return;
+
+  gtk_menu_button_set_direction (GTK_MENU_BUTTON (self->menu_button), direction);
+
+  update_style_classes (self);
+}
+
+/**
+ * adw_split_button_popup:
+ * @self: a `AdwSplitButton`
+ *
+ * Pops up the menu.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_popup (AdwSplitButton *self)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  gtk_menu_button_popup (GTK_MENU_BUTTON (self->menu_button));
+}
+
+/**
+ * adw_split_button_popdown:
+ * @self: a `AdwSplitButton`
+ *
+ * Dismisses the menu.
+ *
+ * Since: 1.0
+ */
+void
+adw_split_button_popdown (AdwSplitButton *self)
+{
+  g_return_if_fail (ADW_IS_SPLIT_BUTTON (self));
+
+  gtk_menu_button_popdown (GTK_MENU_BUTTON (self->menu_button));
+}
diff --git a/src/adw-split-button.h b/src/adw-split-button.h
new file mode 100644
index 00000000..55686e2e
--- /dev/null
+++ b/src/adw-split-button.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-version.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_SPLIT_BUTTON (adw_split_button_get_type())
+
+ADW_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (AdwSplitButton, adw_split_button, ADW, SPLIT_BUTTON, GtkWidget)
+
+ADW_AVAILABLE_IN_ALL
+GtkWidget *adw_split_button_new (void) G_GNUC_WARN_UNUSED_RESULT;
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_split_button_get_label (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_split_button_set_label (AdwSplitButton *self,
+                                        const char     *label);
+
+ADW_AVAILABLE_IN_ALL
+gboolean adw_split_button_get_use_underline (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void     adw_split_button_set_use_underline (AdwSplitButton *self,
+                                             gboolean        use_underline);
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_split_button_get_icon_name (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_split_button_set_icon_name (AdwSplitButton *self,
+                                            const char     *icon_name);
+
+ADW_AVAILABLE_IN_ALL
+GtkWidget *adw_split_button_get_child (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void       adw_split_button_set_child (AdwSplitButton *self,
+                                       GtkWidget      *child);
+
+ADW_AVAILABLE_IN_ALL
+GMenuModel *adw_split_button_get_menu_model (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_split_button_set_menu_model (AdwSplitButton *self,
+                                             GMenuModel     *menu_model);
+
+ADW_AVAILABLE_IN_ALL
+GtkPopover *adw_split_button_get_popover (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_split_button_set_popover (AdwSplitButton *self,
+                                          GtkPopover     *popover);
+
+ADW_AVAILABLE_IN_ALL
+GtkArrowType adw_split_button_get_direction (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void         adw_split_button_set_direction (AdwSplitButton *self,
+                                             GtkArrowType    direction);
+
+ADW_AVAILABLE_IN_ALL
+void adw_split_button_popup (AdwSplitButton *self);
+ADW_AVAILABLE_IN_ALL
+void adw_split_button_popdown (AdwSplitButton *self);
+
+G_END_DECLS
diff --git a/src/adwaita.h b/src/adwaita.h
index ba9bf074..09fcda6a 100644
--- a/src/adwaita.h
+++ b/src/adwaita.h
@@ -47,6 +47,7 @@ G_BEGIN_DECLS
 #include "adw-preferences-page.h"
 #include "adw-preferences-row.h"
 #include "adw-preferences-window.h"
+#include "adw-split-button.h"
 #include "adw-squeezer.h"
 #include "adw-status-page.h"
 #include "adw-swipe-tracker.h"
diff --git a/src/meson.build b/src/meson.build
index 95ee4d63..39bc16da 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -92,6 +92,7 @@ src_headers = [
   'adw-preferences-page.h',
   'adw-preferences-row.h',
   'adw-preferences-window.h',
+  'adw-split-button.h',
   'adw-squeezer.h',
   'adw-status-page.h',
   'adw-swipe-tracker.h',
@@ -149,6 +150,7 @@ src_sources = [
   'adw-preferences-row.c',
   'adw-preferences-window.c',
   'adw-shadow-helper.c',
+  'adw-split-button.c',
   'adw-squeezer.c',
   'adw-status-page.c',
   'adw-swipe-tracker.c',
diff --git a/src/stylesheet/widgets/_buttons.scss b/src/stylesheet/widgets/_buttons.scss
index dd21e2c1..2e49af7b 100644
--- a/src/stylesheet/widgets/_buttons.scss
+++ b/src/stylesheet/widgets/_buttons.scss
@@ -286,10 +286,6 @@ button {
 
 %undecorated_button {
   background-color: transparent;
-  background-image: none;
-  border-color: transparent;
-  box-shadow: inset 0 1px transparentize(white, 1),
-              0 1px transparentize(white, 1);
 }
 
 button.color {
@@ -342,6 +338,10 @@ menubutton {
     border-spacing: 6px;
   }
 
+  > button.arrow-button > box {
+    border-spacing: 3px;
+  }
+
   &.osd {
     background: none;
     color: inherit;
@@ -373,3 +373,77 @@ menubutton {
     }
   }
 }
+
+splitbutton {
+  border-radius: $button_radius;
+
+  &, & > separator {
+    transition: $button_transition;
+    transition-property: background;
+  }
+
+  > separator {
+    margin-top: 7px;
+    margin-bottom: 7px;
+    background: none;
+  }
+
+  > menubutton > button {
+    padding-left: 4px;
+    padding-right: 4px;
+  }
+
+  // Reimplementing linked so we don't blow up css
+  > button:dir(ltr),
+  > menubutton > button:dir(rtl) {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+    margin-right: -1px;
+  }
+
+  > button:dir(rtl),
+  > menubutton > button:dir(ltr) {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+    margin-left: -1px;
+  }
+
+  @at-root %flat_split_button,
+  &.flat {
+    > separator {
+      background: gtkalpha(currentColor, .2);
+    }
+
+    &:hover,
+    &:active,
+    &:checked {
+      background: gtkalpha(currentColor, .05);
+
+      > separator {
+        background: none;
+      }
+    }
+
+    &:focus-within:focus-visible > separator {
+      background: none;
+    }
+
+    > button,
+    > menubutton > button {
+      @extend %button_basic_flat;
+
+      border-radius: $button_radius;
+    }
+  }
+
+  &.outline {
+    > button,
+    > menubutton > button {
+      @extend %outline_button;
+    }
+  }
+
+  > menubutton > button > arrow.none {
+    -gtk-icon-source: -gtk-icontheme('pan-down-symbolic');
+  }
+}
diff --git a/src/stylesheet/widgets/_header-bar.scss b/src/stylesheet/widgets/_header-bar.scss
index b19f9c2a..c960b91b 100644
--- a/src/stylesheet/widgets/_header-bar.scss
+++ b/src/stylesheet/widgets/_header-bar.scss
@@ -5,6 +5,8 @@ headerbar {
   border-color: $alt_borders_color;
   background-color: $headerbar_bg_color;
 
+  @extend %toolbar;
+
   > windowhandle > box {
     padding: 0 6px;
 
@@ -38,15 +40,7 @@ headerbar {
     }
 
     windowcontrols {
-      button,
-      menubutton {
-        min-height: 28px;
-        min-width: 28px;
-        margin: 0;
-        padding: 0;
-      }
-
-      menubutton button {
+      button {
         min-height: 22px;
         min-width: 22px;
         margin: 0;
@@ -64,27 +58,6 @@ headerbar {
       }
     }
   }
-
-  // add vertical margins to common widget on the headerbar to avoid them spanning the whole height
-  entry,
-  spinbutton,
-  separator:not(.sidebar),
-  button,
-  menubutton {
-    margin-top: 6px;
-    margin-bottom: 6px;
-  }
-
-  // Reset margins for buttons inside menubutton
-  menubutton > button {
-    margin-top: 0px;
-    margin-bottom: 0px;
-  }
-
-  switch {
-    margin-top: 10px;
-    margin-bottom: 10px;
-  }
 }
 
 .titlebar:not(headerbar) {
@@ -96,16 +69,26 @@ headerbar {
  *********************/
 
 windowcontrols {
-  border-spacing: 6px;
+  border-spacing: 3px;
 
   button {
-    @extend %button_basic_flat;
+    min-width: 24px;
+    padding: 5px;
+    margin: 0;
+
+    > image {
+      background-color: $button_color;
+      border-radius: 100%;
+      padding: 2px;
+      transition: $button_transition;
+    }
+
+    &, &:hover, &:active {
+      background: none;
+    }
 
-    border-radius: 9999px;
-    padding: 7px;
-    margin: 0 2px;
-    min-width: 0;
-    min-height: 0;
+    &:hover > image { background-color: $button_hover_color; }
+    &:hover:active > image { background-color: $button_active_color; }
   }
 }
 
diff --git a/src/stylesheet/widgets/_linked.scss b/src/stylesheet/widgets/_linked.scss
index a319feff..7bbd34c7 100644
--- a/src/stylesheet/widgets/_linked.scss
+++ b/src/stylesheet/widgets/_linked.scss
@@ -5,6 +5,7 @@ $_linked_widgets: ("%button",          ""),
                   ("dropdown",         "> button"),
                   ("colorbutton",      "> button"),
                   ("fontbutton",       "> button"),
+                  ("scalebutton",      "> button"),
                   ("combobox",         "> box > button.combo"),
                   ("appchooserbutton", "> combobox > box > button.combo"),
                   ("%entry",           ""),
diff --git a/src/stylesheet/widgets/_toolbars.scss b/src/stylesheet/widgets/_toolbars.scss
index cc7a2067..de4d6384 100644
--- a/src/stylesheet/widgets/_toolbars.scss
+++ b/src/stylesheet/widgets/_toolbars.scss
@@ -1,11 +1,62 @@
+%toolbar {
+  button.image-button:not(.raised),
+  button.arrow-button:not(.raised) {
+    @extend %button_basic_flat;
+  }
+
+  .linked button {
+    &.image-button:not(.raised),
+    &.arrow-button:not(.raised) {
+      @extend %button;
+    }
+  }
+
+  splitbutton {
+    // Specificity bump
+    > separator.vertical {
+      margin-top: 7px;
+      margin-bottom: 7px;
+    }
+
+    &.image-button:not(.raised) {
+      @extend %flat_split_button;
+    }
+  }
+
+  // add vertical margins to common widgets on toolbars to avoid them spanning
+  // the whole height
+  entry,
+  spinbutton,
+  splitbutton,
+  separator:not(.sidebar),
+  button,
+  .linked2 {
+    margin-top: 6px;
+    margin-bottom: 6px;
+  }
+
+  .linked2 button,
+  splitbutton button {
+    margin-top: 0px;
+    margin-bottom: 0px;
+  }
+
+  switch {
+    margin-top: 10px;
+    margin-bottom: 10px;
+  }
+}
+
 .toolbar {
-  padding: 5px;
-  border-spacing: 5px;
+  padding: 0 6px;
+  border-spacing: 6px;
   background-color: $bg_color;
 
   // on OSD
   .osd & { background-color: transparent; }
 
+  @extend %toolbar;
+
   // stand-alone OSD toolbars
   &.osd {
     padding: 14px;
@@ -20,12 +71,8 @@
   }
 
   // toolbar separators
-  &.horizontal > separator { margin: 5px 0; }
-  &.vertical > separator { margin: 0 5px; }
-
-  button {
-    @extend %button_basic_flat;
-  }
+  &.horizontal > separator { margin: 6px 0; }
+  &.vertical > separator { margin: 0 6px; }
 }
 
 /****************
@@ -56,8 +103,9 @@ searchbar > revealer > box {
  ****************/
 
 actionbar > revealer > box {
-  padding: 6px;
+  padding: 0 6px;
   border-top: 1px solid $borders_color;
+  @extend %toolbar;
 
   &, > box.start, > box.end {
     border-spacing: 6px;
diff --git a/src/stylesheet/widgets/_view-switcher.scss b/src/stylesheet/widgets/_view-switcher.scss
index c98c14e1..0d14b5c4 100644
--- a/src/stylesheet/widgets/_view-switcher.scss
+++ b/src/stylesheet/widgets/_view-switcher.scss
@@ -1,10 +1,10 @@
 viewswitcher {
-  &, & button {
+  &, & button.toggle {
     margin: 0;
     padding: 0;
   }
 
-  button {
+  button.toggle {
     border-radius: 0;
     border-top: none;
     border-bottom: none;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]