Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek GtkWidgets.cpp   Sprache: C

 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include <dlfcn.h>
#include <gtk/gtk.h>
#include "GtkWidgets.h"
#include "mozilla/Assertions.h"
#include "mozilla/PodOperations.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/WidgetUtilsGtk.h"

namespace mozilla::widget::GtkWidgets {

static EnumeratedArray<Type, GtkWidget*, kTypeCount> sWidgetStorage;
static EnumeratedArray<Type, GtkStyleContext*, kTypeCount> sStyleStorage;

static GtkStyleContext* CreateCSSNode(const char* aName,
                                      GtkStyleContext* aParentStyle,
                                      GType aType = G_TYPE_NONE) {
  static auto sGtkWidgetPathIterSetObjectName =
      reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(
          dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name"));

  GtkWidgetPath* path;
  if (aParentStyle) {
    path = gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle));
    // Copy classes from the parent style context to its corresponding node in
    // the path, because GTK will only match against ancestor classes if they
    // are on the path.
    GList* classes = gtk_style_context_list_classes(aParentStyle);
    for (GList* link = classes; link; link = link->next) {
      gtk_widget_path_iter_add_class(path, -1, static_cast<gchar*>(link->data));
    }
    g_list_free(classes);
  } else {
    path = gtk_widget_path_new();
  }

  gtk_widget_path_append_type(path, aType);

  if (sGtkWidgetPathIterSetObjectName) {
    sGtkWidgetPathIterSetObjectName(path, -1, aName);
  }

  GtkStyleContext* context = gtk_style_context_new();
  gtk_style_context_set_path(context, path);
  gtk_style_context_set_parent(context, aParentStyle);
  gtk_widget_path_unref(path);

  return context;
}

static GtkStyleContext* GetWidgetRootStyle(Type aType);
static GtkStyleContext* GetCssNodeStyleInternal(Type aType);

static GtkWidget* CreateWindowContainerWidget() {
  GtkWidget* widget = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(Get(Type::Window)), widget);
  return widget;
}

static void AddToWindowContainer(GtkWidget* widget) {
  gtk_container_add(GTK_CONTAINER(Get(Type::WindowContainer)), widget);
}

static GtkWidget* CreateScrollbarWidget() {
  GtkWidget* widget = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, nullptr);
  AddToWindowContainer(widget);
  return widget;
}

static GtkWidget* CreateMenuPopupWidget() {
  GtkWidget* widget = gtk_menu_new();
  GtkStyleContext* style = gtk_widget_get_style_context(widget);
  gtk_style_context_add_class(style, GTK_STYLE_CLASS_POPUP);
  gtk_menu_attach_to_widget(GTK_MENU(widget), Get(Type::Window), nullptr);
  return widget;
}

static GtkWidget* CreateMenuBarWidget() {
  GtkWidget* widget = gtk_menu_bar_new();
  AddToWindowContainer(widget);
  return widget;
}

static GtkWidget* CreateFrameWidget() {
  GtkWidget* widget = gtk_frame_new(nullptr);
  AddToWindowContainer(widget);
  return widget;
}

static GtkWidget* CreateButtonWidget() {
  GtkWidget* widget = gtk_button_new_with_label("M");
  AddToWindowContainer(widget);
  return widget;
}

static GtkWidget* CreateScrolledWindowWidget() {
  GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr);
  AddToWindowContainer(widget);
  return widget;
}

static GtkWidget* CreateTreeViewWidget() {
  GtkWidget* widget = gtk_tree_view_new();
  AddToWindowContainer(widget);
  return widget;
}

static GtkWidget* CreateTreeHeaderCellWidget() {
  /*
   * Some GTK engines paint the first and last cell
   * of a TreeView header with a highlight.
   * Since we do not know where our widget will be relative
   * to the other buttons in the TreeView header, we must
   * paint it as a button that is between two others,
   * thus ensuring it is neither the first or last button
   * in the header.
   * GTK doesn't give us a way to do this explicitly,
   * so we must paint with a button that is between two
   * others.
   */

  GtkTreeViewColumn* firstTreeViewColumn;
  GtkTreeViewColumn* middleTreeViewColumn;
  GtkTreeViewColumn* lastTreeViewColumn;

  GtkWidget* treeView = Get(Type::TreeView);

  /* Create and append our three columns */
  firstTreeViewColumn = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), firstTreeViewColumn);

  middleTreeViewColumn = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(middleTreeViewColumn, "M");
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), middleTreeViewColumn);

  lastTreeViewColumn = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), lastTreeViewColumn);

  /* Use the middle column's header for our button */
  return gtk_tree_view_column_get_button(middleTreeViewColumn);
}

static void CreateWindowAndHeaderBar() {
  GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_name(window, "MozillaGtkWidget");
  GtkStyleContext* windowStyle = gtk_widget_get_style_context(window);

  // Headerbar has to be placed into a window with csd or solid-csd style
  // to properly draw the decorations.
  gtk_style_context_add_class(windowStyle, "csd");

  GtkWidget* fixed = gtk_fixed_new();
  GtkStyleContext* fixedStyle = gtk_widget_get_style_context(fixed);
  gtk_style_context_add_class(fixedStyle, "titlebar");

  GtkWidget* headerBar = gtk_header_bar_new();
  // From create_headerbar in gtkwindow.c
  g_object_set(headerBar, "title", "Title", "has-subtitle", FALSE,
               "show-close-button", TRUE, NULL);

  // Emulate what create_titlebar() at gtkwindow.c does.
  GtkStyleContext* headerBarStyle = gtk_widget_get_style_context(headerBar);
  gtk_style_context_add_class(headerBarStyle, GTK_STYLE_CLASS_TITLEBAR);

  // TODO: Define default-decoration titlebar style as workaround
  // to ensure the titlebar buttons does not overflow outside. Recently the
  // titlebar size is calculated as tab size + titlebar border/padding
  // (default-decoration has 6px padding at default Adwaita theme).
  // We need to fix titlebar size calculation to also include titlebar button
  // sizes. (Bug 1419442)
  gtk_style_context_add_class(headerBarStyle, "default-decoration");

  MOZ_ASSERT(!sWidgetStorage[Type::HeaderBar],
             "Headerbar widget is already created!");
  MOZ_ASSERT(!sWidgetStorage[Type::Window],
             "Window widget is already created!");
  MOZ_ASSERT(!sWidgetStorage[Type::HeaderBarFixed],
             "Fixed widget is already created!");

  sWidgetStorage[Type::HeaderBar] = headerBar;
  sWidgetStorage[Type::Window] = window;
  sWidgetStorage[Type::HeaderBarFixed] = fixed;

  gtk_container_add(GTK_CONTAINER(fixed), headerBar);
  gtk_window_set_titlebar(GTK_WINDOW(window), fixed);

  gtk_widget_show_all(headerBar);
}

static GtkWidget* CreateWidget(Type aType) {
  switch (aType) {
    case Type::Window:
    case Type::HeaderBarFixed:
    case Type::HeaderBar:
      // Create header bar widgets once and fill with child elements as we need
      // the header bar fully configured to get a correct style.
      CreateWindowAndHeaderBar();
      return sWidgetStorage[aType];
    case Type::WindowContainer:
      return CreateWindowContainerWidget();
    case Type::Scrollbar:
      return CreateScrollbarWidget();
    case Type::Menupopup:
      return CreateMenuPopupWidget();
    case Type::Menubar:
      return CreateMenuBarWidget();
    case Type::Frame:
      return CreateFrameWidget();
    case Type::Button:
      return CreateButtonWidget();
    case Type::ScrolledWindow:
      return CreateScrolledWindowWidget();
    case Type::TreeView:
      return CreateTreeViewWidget();
    case Type::TreeHeaderCell:
      return CreateTreeHeaderCellWidget();
    case Type::ScrollbarContents:
    case Type::ScrollbarTrough:
    case Type::ScrollbarThumb:
    case Type::TextView:
    case Type::TextViewText:
    case Type::TextViewTextSelection:
    case Type::Tooltip:
    case Type::TooltipBox:
    case Type::TooltipBoxLabel:
    case Type::FrameBorder:
    case Type::Menuitem:
    case Type::MenubarItem:
    case Type::WindowDecoration:
      break;
  }
  // Not implemented
  return nullptr;
}

GtkWidget* Get(Type aType) {
  GtkWidget* widget = sWidgetStorage[aType];
  if (!widget) {
    widget = CreateWidget(aType);
    sWidgetStorage[aType] = widget;
  }
  return widget;
}

static void AddStyleClassesFromStyle(GtkStyleContext* aDest,
                                     GtkStyleContext* aSrc) {
  GList* classes = gtk_style_context_list_classes(aSrc);
  for (GList* link = classes; link; link = link->next) {
    gtk_style_context_add_class(aDest, static_cast<gchar*>(link->data));
  }
  g_list_free(classes);
}

GtkStyleContext* CreateStyleForWidget(GtkWidget* aWidget,
                                      GtkStyleContext* aParentStyle) {
  static auto sGtkWidgetClassGetCSSName =
      reinterpret_cast<const char* (*)(GtkWidgetClass*)>(
          dlsym(RTLD_DEFAULT, "gtk_widget_class_get_css_name"));

  GtkWidgetClass* widgetClass = GTK_WIDGET_GET_CLASS(aWidget);
  const gchar* name = sGtkWidgetClassGetCSSName
                          ? sGtkWidgetClassGetCSSName(widgetClass)
                          : nullptr;

  GtkStyleContext* context =
      CreateCSSNode(name, aParentStyle, G_TYPE_FROM_CLASS(widgetClass));

  // Classes are stored on the style context instead of the path so that any
  // future gtk_style_context_save() will inherit classes on the head CSS
  // node, in the same way as happens when called on a style context owned by
  // a widget.
  //
  // Classes can be stored on a GtkCssNodeDeclaration and/or the path.
  // gtk_style_context_save() reuses the GtkCssNodeDeclaration, and appends a
  // new object to the path, without copying the classes from the old path
  // head.  The new head picks up classes from the GtkCssNodeDeclaration, but
  // not the path.  GtkWidgets store their classes on the
  // GtkCssNodeDeclaration, so make sure to add classes there.
  //
  // Picking up classes from the style context also means that
  // https://bugzilla.gnome.org/show_bug.cgi?id=767312, which can stop
  // gtk_widget_path_append_for_widget() from finding classes in GTK 3.20,
  // is not a problem.
  GtkStyleContext* widgetStyle = gtk_widget_get_style_context(aWidget);
  AddStyleClassesFromStyle(context, widgetStyle);

  // Release any floating reference on aWidget.
  g_object_ref_sink(aWidget);
  g_object_unref(aWidget);

  return context;
}

static GtkStyleContext* CreateStyleForWidget(GtkWidget* aWidget,
                                             Type aParentType) {
  return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType));
}

// Return a style context matching that of the root CSS node of a widget.
// This is used by all GTK versions.
static GtkStyleContext* GetWidgetRootStyle(Type aType) {
  GtkStyleContext* style = sStyleStorage[aType];
  if (style) {
    return style;
  }

  switch (aType) {
    case Type::Menuitem:
      style = CreateStyleForWidget(gtk_menu_item_new(), Type::Menupopup);
      break;
    case Type::MenubarItem:
      style = CreateStyleForWidget(gtk_menu_item_new(), Type::Menubar);
      break;
    case Type::TextView:
      style = CreateStyleForWidget(gtk_text_view_new(), Type::ScrolledWindow);
      break;
    case Type::Tooltip:
      if (gtk_check_version(3, 20, 0) != nullptr) {
        GtkWidget* tooltipWindow = gtk_window_new(GTK_WINDOW_POPUP);
        GtkStyleContext* style = gtk_widget_get_style_context(tooltipWindow);
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
        style = CreateStyleForWidget(tooltipWindow, nullptr);
        gtk_widget_destroy(tooltipWindow);  // Release GtkWindow self-reference.
      } else {
        // We create this from the path because GtkTooltipWindow is not public.
        style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
      }
      break;
    case Type::TooltipBox:
      style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
                                   Type::Tooltip);
      break;
    case Type::TooltipBoxLabel:
      style = CreateStyleForWidget(gtk_label_new(nullptr), Type::TooltipBox);
      break;
    default:
      GtkWidget* widget = Get(aType);
      MOZ_ASSERT(widget);
      return gtk_widget_get_style_context(widget);
  }

  MOZ_ASSERT(style);
  sStyleStorage[aType] = style;
  return style;
}

static GtkStyleContext* CreateChildCSSNode(const char* aName,
                                           Type aParentType) {
  return CreateCSSNode(aName, GetCssNodeStyleInternal(aParentType));
}

// Create a style context equivalent to a saved root style context of
// |aType| with |aStyleClass| as an additional class.  This is used to
// produce a context equivalent to what GTK versions < 3.20 use for many
// internal parts of widgets.
static GtkStyleContext* CreateSubStyleWithClass(Type aType,
                                                const gchar* aStyleClass) {
  static auto sGtkWidgetPathIterGetObjectName =
      reinterpret_cast<const char* (*)(const GtkWidgetPath*, gint)>(
          dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_object_name"));

  GtkStyleContext* parentStyle = GetWidgetRootStyle(aType);

  // Create a new context that behaves like |parentStyle| would after
  // gtk_style_context_save(parentStyle).
  //
  // Avoiding gtk_style_context_save() avoids the need to manage the
  // restore, and a new context permits caching style resolution.
  //
  // gtk_style_context_save(context) changes the node hierarchy of |context|
  // to add a new GtkCssNodeDeclaration that is a copy of its original node.
  // The new node is a child of the original node, and so the new heirarchy is
  // one level deeper.  The new node receives the same classes as the
  // original, but any changes to the classes on |context| will change only
  // the new node.  The new node inherits properties from the original node
  // (which retains the original heirarchy and classes) and matches CSS rules
  // with the new heirarchy and any changes to the classes.
  //
  // The change in hierarchy can produce some surprises in matching theme CSS
  // rules (e.g. https://bugzilla.gnome.org/show_bug.cgi?id=761870#c2), but it
  // is important here to produce the same behavior so that rules match the
  // same widget parts in Gecko as they do in GTK.
  //
  // When using public GTK API to construct style contexts, a widget path is
  // required.  CSS rules are not matched against the style context heirarchy
  // but according to the heirarchy in the widget path.  The path that matches
  // the same CSS rules as a saved context is like the path of |parentStyle|
  // but with an extra copy of the head (last) object appended.  Setting
  // |parentStyle| as the parent context provides the same inheritance of
  // properties from the widget root node.
  const GtkWidgetPath* parentPath = gtk_style_context_get_path(parentStyle);
  const gchar* name = sGtkWidgetPathIterGetObjectName
                          ? sGtkWidgetPathIterGetObjectName(parentPath, -1)
                          : nullptr;
  GType objectType = gtk_widget_path_get_object_type(parentPath);

  GtkStyleContext* style = CreateCSSNode(name, parentStyle, objectType);

  // Start with the same classes on the new node as were on |parentStyle|.
  // GTK puts no regions or junction_sides on widget root nodes, and so there
  // is no need to copy these.
  AddStyleClassesFromStyle(style, parentStyle);

  gtk_style_context_add_class(style, aStyleClass);
  return style;
}

// GetCssNodeStyleInternal is used by Gtk >= 3.20
static GtkStyleContext* GetCssNodeStyleInternal(Type aType) {
  GtkStyleContext* style = sStyleStorage[aType];
  if (style) {
    return style;
  }

  switch (aType) {
    case Type::ScrollbarContents:
      style = CreateChildCSSNode("contents", Type::Scrollbar);
      break;
    case Type::ScrollbarTrough:
      style =
          CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH, Type::ScrollbarContents);
      break;
    case Type::ScrollbarThumb:
      style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER, Type::ScrollbarTrough);
      break;
    case Type::ScrolledWindow:
      // TODO - create from CSS node
      style =
          CreateSubStyleWithClass(Type::ScrolledWindow, GTK_STYLE_CLASS_FRAME);
      break;
    case Type::TextViewTextSelection:
      style = CreateChildCSSNode("selection", Type::TextViewText);
      break;
    case Type::TextViewText:
      style = CreateChildCSSNode("text", Type::TextView);
      break;
    case Type::FrameBorder:
      style = CreateChildCSSNode("border", Type::Frame);
      break;
    case Type::WindowDecoration: {
      GtkStyleContext* parentStyle =
          CreateSubStyleWithClass(Type::Window, "csd");
      style = CreateCSSNode("decoration", parentStyle);
      g_object_unref(parentStyle);
      break;
    }
    default:
      return GetWidgetRootStyle(aType);
  }

  MOZ_ASSERT(style, "missing style context for node type");
  sStyleStorage[aType] = style;
  return style;
}

// GetWidgetStyleInternal is used by Gtk < 3.20
static GtkStyleContext* GetWidgetStyleInternal(Type aType) {
  GtkStyleContext* style = sStyleStorage[aType];
  if (style) {
    return style;
  }

  switch (aType) {
    case Type::ScrollbarTrough:
      style = CreateSubStyleWithClass(Type::Scrollbar, GTK_STYLE_CLASS_TROUGH);
      break;
    case Type::ScrollbarThumb:
      style = CreateSubStyleWithClass(Type::Scrollbar, GTK_STYLE_CLASS_SLIDER);
      break;
    case Type::ScrolledWindow:
      style =
          CreateSubStyleWithClass(Type::ScrolledWindow, GTK_STYLE_CLASS_FRAME);
      break;
    case Type::TextViewText:
      // GTK versions prior to 3.20 do not have the view class on the root
      // node, but add this to determine the background for the text window.
      style = CreateSubStyleWithClass(Type::TextView, GTK_STYLE_CLASS_VIEW);
      break;
    case Type::FrameBorder:
      return GetWidgetRootStyle(Type::Frame);
    default:
      return GetWidgetRootStyle(aType);
  }

  MOZ_ASSERT(style);
  sStyleStorage[aType] = style;
  return style;
}

static void ResetWidgetCache() {
  for (auto& style : sStyleStorage) {
    if (style) {
      g_object_unref(style);
    }
  }
  mozilla::PodZero(sStyleStorage.begin(), sStyleStorage.size());

  // This will destroy all of our widgets
  if (sWidgetStorage[Type::Window]) {
    gtk_widget_destroy(sWidgetStorage[Type::Window]);
  }

  // Clear already freed arrays
  mozilla::PodZero(sWidgetStorage.begin(), sWidgetStorage.size());
}

static void StyleContextSetScale(GtkStyleContext* style, gint aScaleFactor) {
  // Support HiDPI styles on Gtk 3.20+
  static auto sGtkStyleContextSetScalePtr =
      (void (*)(GtkStyleContext*, gint))dlsym(RTLD_DEFAULT,
                                              "gtk_style_context_set_scale");
  if (sGtkStyleContextSetScalePtr && style) {
    sGtkStyleContextSetScalePtr(style, aScaleFactor);
  }
}

GtkStyleContext* GetStyle(Type aType, int aScale, GtkStateFlags aState) {
  GtkStyleContext* style;
  if (gtk_check_version(3, 20, 0) != nullptr) {
    style = GetWidgetStyleInternal(aType);
  } else {
    style = GetCssNodeStyleInternal(aType);
    StyleContextSetScale(style, aScale);
  }
  if (gtk_style_context_get_state(style) != aState) {
    gtk_style_context_set_state(style, aState);
  }
  return style;
}

#if 0
// It's used for debugging only to compare Gecko widget style with
// the ones used by Gtk+ applications.
static void
style_path_print(GtkStyleContext *context)
{
    const GtkWidgetPath* path = gtk_style_context_get_path(context);

    static auto sGtkWidgetPathToStringPtr =
        (char * (*)(const GtkWidgetPath *))
        dlsym(RTLD_DEFAULT, "gtk_widget_path_to_string");

    fprintf(stderr, "Style path:\n%s\n\n", sGtkWidgetPathToStringPtr(path));
}
#endif

void Refresh() { ResetWidgetCache(); }

static void DrawWindowDecoration(cairo_t* cr, const DrawingParams& aParams) {
  GtkStyleContext* decorationStyle =
      GetStyle(Type::WindowDecoration, aParams.image_scale, aParams.state);

  const auto& rect = aParams.rect;
  gtk_render_background(decorationStyle, cr, rect.x, rect.y, rect.width,
                        rect.height);
  gtk_render_frame(decorationStyle, cr, rect.x, rect.y, rect.width,
                   rect.height);
}

/* cairo_t *cr argument has to be a system-cairo. */
void Draw(cairo_t* cr, const DrawingParams* aParams) {
  switch (aParams->widget) {
    case Type::WindowDecoration:
      return DrawWindowDecoration(cr, *aParams);
    default:
      g_warning("Unknown widget type: %u", uint32_t(aParams->widget));
      return;
  }
}

void Shutdown() {
  /* This will destroy all of our widgets */
  ResetWidgetCache();
}

}  // namespace mozilla::widget::GtkWidgets

Messung V0.5 in Prozent
C=85 H=98 G=91

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.24Bemerkung:  (vorverarbeitet am  2026-09-29) ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1867298