From 10cd539104af54f5d1d1e49618f5aa9d8ac42074 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 22 May 2020 21:56:39 -0400 Subject: [PATCH 01/18] wip: Include markdown content, via pandoc Use pandoc to convert freestanding markdown files to docbook for inclusion in the generated docs, and use bits and pieces of gtk-doc code to continue expanding typical gtk-doc abbreviations. The new tool for markdown -> docbook is a python script called gtk-markdown-to-docbook. The markdown dialect is specified via a list of pandoc extension in gtk-markdown-to-docbook. It includes header annocations, definition lists and tables, among other things. This commit converts the 3 overview chapters (drawing, input handling and actions) and the migration guide to markdown syntax. Other files that are still listed in content_files can be converted later. This commit adds a pandoc dependency. --- docs/reference/gtk/actions.md | 222 ++++ docs/reference/gtk/actions.xml | 394 ------ docs/reference/gtk/drawing-model.md | 157 +++ docs/reference/gtk/drawing-model.xml | 232 ---- docs/reference/gtk/gtk-markdown-to-docbook | 184 +++ docs/reference/gtk/gtk4-docs.xml | 12 +- docs/reference/gtk/input-handling.md | 206 ++++ docs/reference/gtk/input-handling.xml | 332 ----- docs/reference/gtk/lists-overview.md | 101 ++ docs/reference/gtk/meson.build | 31 +- docs/reference/gtk/migrating-2to4.md | 5 + docs/reference/gtk/migrating-2to4.xml | 15 - docs/reference/gtk/migrating-3to4.md | 928 ++++++++++++++ docs/reference/gtk/migrating-3to4.xml | 1265 -------------------- 14 files changed, 1829 insertions(+), 2255 deletions(-) create mode 100644 docs/reference/gtk/actions.md delete mode 100644 docs/reference/gtk/actions.xml create mode 100644 docs/reference/gtk/drawing-model.md delete mode 100644 docs/reference/gtk/drawing-model.xml create mode 100755 docs/reference/gtk/gtk-markdown-to-docbook create mode 100644 docs/reference/gtk/input-handling.md delete mode 100644 docs/reference/gtk/input-handling.xml create mode 100644 docs/reference/gtk/lists-overview.md create mode 100644 docs/reference/gtk/migrating-2to4.md delete mode 100644 docs/reference/gtk/migrating-2to4.xml create mode 100644 docs/reference/gtk/migrating-3to4.md delete mode 100644 docs/reference/gtk/migrating-3to4.xml diff --git a/docs/reference/gtk/actions.md b/docs/reference/gtk/actions.md new file mode 100644 index 0000000000..c88006e6d4 --- /dev/null +++ b/docs/reference/gtk/actions.md @@ -0,0 +1,222 @@ +# Overview of actions in GTK {#actions-overview} + +This chapter describes in detail how GTK uses actions to connect +activatable UI elements to callbacks. GTK inherits the underlying +architecture of GAction and GMe:u for describing abstract actions +and menus from the GIO library. + +## Basics about actions + +A GAction is essentially a way to tell the toolkit about a piece of +functionality in your program, and to give it a name. + +Actions are purely functional. They do not contain any presentational +information. + +An action has four pieces of information associated with it: + +- a name as an identifier (usually all-lowercase, untranslated + English string) +- an enabled flag indicating if the action can be activated or not + (like the "sensitive" property on widgets) +- an optional state value, for stateful actions (like a boolean for + toggles) +- an optional parameter type, used when activating the action + +An action supports two operations. You can activate it, which requires +passing a parameter of the correct type And you can request to change +the actions state (for stateful actions) to a new state value of the +correct type. + +Here are some rules about an action: + +- the name is immutable (in the sense that it will never change) and + it is never %NULL +- the enabled flag can change +- the parameter type is immutable +- the parameter type is optional: it can be %NULL +- if the parameter type is %NULL then action activation must be done + without a parameter (ie: a %NULL GVariant pointer) +- if the parameter type is non-%NULL then the parameter must have this + type +- the state can change, but it cannot change type +- if the action was stateful when it was created, it will always have a + state and it will always have exactly the same type (such as boolean + or string) +- if the action was stateless when it was created, it can never have a + state +- you can only request state changes on stateful actions and it is only + possible to request that the state change to a value of the same type + as the existing state + +An action does not have any sort of presentational information such as +a label, an icon or a way of creating a widget from it. + +## Action state and parameters + +Most actions in your application will be stateless actions with no +parameters. These typically appear as menu items with no special +decoration. An example is "quit". + +Stateful actions are used to represent an action which has a +closely-associated state of some kind. A good example is a "fullscreen" +action. For this case, you would expect to see a checkmark next to the +menu item when the fullscreen option is active. This is usually called +a toggle action, and it has a boolean state. By convention, toggle actions +have no parameter type for activation: activating the action always toggles +the state. + +Another common case is to have an action representing a enumeration of +possible values of a given type (typically string). This is often called +a radio action and is usually represented in the user interface with radio +buttons or radio menu items, or sometimes a combobox. A good example is +"text-justify" with possible values "left", "center", and "right". By +convention, these types of actions have a parameter type equal to their +state type, and activating them with a particular parameter value is +equivalent to changing their state to that value. + +This approach to handling radio buttons is different than many other +action systems such as GtkAction. With GAction, there is only one action +for "text-justify" and "left", "center" and "right" are possible states on +that action. There are not three separate "justify-left", "justify-center" +and "justify-right" actions. + +The final common type of action is a stateless action with a parameter. +This is typically used for actions like "open-bookmark" where the parameter +to the action would be the identifier of the bookmark to open. + +Because some types of actions cannot be invoked without a parameter, it is +often important to specify a parameter when referring to the action from +a place where it will be invoked (such as from a radio button that sets +the state to a particular value or from a menu item that opens a specific +bookmark). In these contexts, the value used for the action parameter is +typically called the target of the action. + +Even though toggle actions have a state, they do not have a parameter. +Therefore, a target value is not needed when referring to them — they +will always be toggled on activation. + +Most APIs that allow using a GAction (such as GMenuModel and GtkActionable) +allow use of detailed action names. This is a convenient way of specifying +an action name and an action target with a single string. + +In the case that the action target is a string with no unusual characters +(ie: only alpha-numeric, plus '-' and '.') then you can use a detailed +action name of the form "justify::left" to specify the justify action with +a target of left. + +In the case that the action target is not a string, or contains unusual +characters, you can use the more general format "action-name(5)", where the +"5" here is any valid text-format GVariant (ie: a string that can be parsed +by g_variant_parse()). Another example is "open-bookmark('http://gnome.org/')". + +You can convert between detailed action names and split-out action names +and target values using g_action_parse_detailed_name() and +g_action_print_detailed_name() but usually you will not need to. Most APIs +will provide both ways of specifying actions with targets. + +## Action scopes + +Actions are always scoped to a particular object on which they operate. + +In GTK, actions are typically scoped to either an application or a window, +but any widget can have actions associated with it. + +Actions scoped to windows should be the actions that specifically impact +that window. These are actions like "fullscreen" and "close", or in the +case that a window contains a document, "save" and "print". + +Actions that impact the application as a whole rather than one specific +window are scoped to the application. These are actions like "about" and +"preferences". + +If a particular action is scoped to a window then it is scoped to a +specific window. Another way of saying this: if your application has a +"fullscreen" action that applies to windows and it has three windows, +then it will have three fullscreen actions: one for each window. + +Having a separate action per-window allows for each window to have a +separate state for each instance of the action as well as being able to +control the enabled state of the action on a per-window basis. + +Actions are added to their relevant scope (application, window or widget) +either using the GActionMap interface, or by using +gtk_widget_insert_action_group(). Actions that will be the same for all +instances of a widget class can be added globally using +gtk_widget_class_install_action(). + +## Action groups and action maps + +Actions rarely occurs in isolation. It is common to have groups +of related actions, which are represented by instances of the +GActionGroup interface. + +Action maps are a variant of action groups that allow to change +the name of the action as it is looked up. In GTK, the convention +is to add a prefix to the action name to indicate the scope of +the actions, such as "app." for the actions with application scope +or "win." for those with window scope. + +When referring to actions on a GActionMap only the name of the +action itself is used (ie: "quit", not "app.quit"). The +"app.quit" form is only used when referring to actions from +places like a GMenu or GtkActionable widget where the scope +of the action is not already known. + +GtkApplication and GtkApplicationWindow implement the GActionMap +interface, so you can just add actions directly to them. For +other widgets, use gtk_widget_insert_action_group() to add +actions to it. + +If you want to insert several actions at the same time, it is +typically faster and easier to use GActionEntry. + +## Connecting actions to widgets + +Any widget that implements the GtkActionable interface can +be connected to an action just by setting the ::action-name +property. If the action has a parameter, you will also need +to set the ::action-target property. +Widgets that implement GtkActionable include GtkSwitch, GtkButton, +and their respective subclasses. + +Another way of obtaining widgets that are connected to actions +is to create a menu using a GMenu menu model. GMenu provides an +abstract way to describe typical menus: nested groups of items +where each item can have a label, and icon, and an action. + +Typical uses of GMenu inside GTK are to set up an application +menu or menubar with gtk_application_set_app_menu() or +gtk_application_set_menubar(). Another, maybe more common use +is to create a popover for a menubutton, using +gtk_menu_button_set_menu_model(). + +Unlike traditional menus, those created from menu models don't +have keyboard accelerators associated with menu items. Instead, +GtkApplication offers the gtk_application_set_accels_for_action() +API to associate keyboard shortcuts with actions. + +## Activation + +When a widget with a connected action is activated, GTK finds +the action to activate by walking up the widget hierarchy, +looking for a matching action, ending up at the GtkApplication. + +## Built-in Actions + +GTK uses actions for its own purposes in a number places. These +built-in actions can sometimes be activated by applications, and +you should avoid naming conflicts with them when creating your +own actions. + +default.activate + : Activates the default widget in a context (typically a GtkWindow, + GtkDialog or GtkPopover) +clipboard.cut, clipboard.copy, clipboard.paste + : Clipboard operations on entries, text view and labels, typically + used in the context menu +selection.delete, selection.select-all + : Selection operations on entries, text view and labels +color.select, color.customize: + : Operate on colors in a #GtkColorChooserWidget. These actions are + unusual in that they have the non-trivial parameter type (dddd): diff --git a/docs/reference/gtk/actions.xml b/docs/reference/gtk/actions.xml deleted file mode 100644 index 1deabcb118..0000000000 --- a/docs/reference/gtk/actions.xml +++ /dev/null @@ -1,394 +0,0 @@ - - - - -The GTK Action Model -3 -GTK Library - - - -The GTK Action Model - - How actions are used in GTK - - - - - - Overview of actions in GTK - - - This chapter describes in detail how GTK uses actions to connect - activatable UI elements to callbacks. GTK inherits the underlying - architecture of GAction and GMenu for describing abstract actions - and menus from the GIO library. - - - - Basics about actions - - - A GAction is essentially a way to tell the toolkit about a - piece of functionality in your program, and to give it a name. - - - - Actions are purely functional. They do not contain any - presentational information. - - - - An action has four pieces of information associated with it: - - - a name as an identifier (usually all-lowercase, untranslated - English string) - - - an enabled flag indicating if the action can be activated or - not (like the "sensitive" property on widgets) - - - an optional state value, for stateful actions (like a boolean - for toggles) - - - an optional parameter type, used when activating the action - - - - - - An action supports two operations. You can activate it, which - requires passing a parameter of the correct type - And you can request to change the actions state (for stateful - actions) to a new state value of the correct type. - - - - Here are some rules about an action: - - - the name is immutable (in the sense that it will never - change) and it is never %NULL - - - the enabled flag can change - - - the parameter type is immutable - - - the parameter type is optional: it can be %NULL - - - if the parameter type is %NULL then action activation must - be done without a parameter (ie: a %NULL GVariant pointer) - - - if the parameter type is non-%NULL then the parameter must - have this type - - - the state can change, but it cannot change type - - - if the action was stateful when it was created, it will - always have a state and it will always have exactly the same - type (such as boolean or string) - - - if the action was stateless when it was created, it can never - have a state - - - you can only request state changes on stateful actions and it - is only possible to request that the state change to a value - of the same type as the existing state - - - - - - An action does not have any sort of presentational information - such as a label, an icon or a way of creating a widget from it. - - - - - - Action state and parameters - - - Most actions in your application will be stateless actions with - no parameters. These typically appear as menu items with no - special decoration. An example is "quit". - - - - Stateful actions are used to represent an action which has a - closely-associated state of some kind. A good example is a - "fullscreen" action. For this case, you'd expect to see a - checkmark next to the menu item when the fullscreen option - is active. This is usually called a toggle action, and it has - a boolean state. By convention, toggle actions have no parameter - type for activation: activating the action always toggles the - state. - - - - Another common case is to have an action representing a - enumeration of possible values of a given type (typically - string). This is often called a radio action and is usually - represented in the user interface with radio buttons or radio - menu items, or sometimes a combobox. A good example is - "text-justify" with possible values "left", "center", and - "right". By convention, these types of actions have a parameter - type equal to their state type, and activating them with a - particular parameter value is equivalent to changing their - state to that value. - - - - This approach to handling radio buttons is different than many - other action systems such as GtkAction. With GAction, there is - only one action for "text-justify" and "left", "center" and - "right" are possible states on that action. There are not three - separate "justify-left", "justify-center" and "justify-right" - actions. - - - - The final common type of action is a stateless action with a - parameter. This is typically used for actions like - "open-bookmark" where the parameter to the action would be - the identifier of the bookmark to open. - - - - Because some types of actions cannot be invoked without a - parameter, it is often important to specify a parameter when - referring to the action from a place where it will be invoked - (such as from a radio button that sets the state to a particular - value or from a menu item that opens a specific bookmark). In - these contexts, the value used for the action parameter is - typically called the target of the action. - - - - Even though toggle actions have a state, they do not have a - parameter. Therefore, a target value is not needed when - referring to them — they will always be toggled on activation. - - - - Most APIs that allow using a GAction (such as GMenuModel and - GtkActionable) allow use of detailed action names. This is a - convenient way of specifying an action name and an action target - with a single string. - - - - In the case that the action target is a string with no unusual - characters (ie: only alpha-numeric, plus '-' and '.') then you - can use a detailed action name of the form "justify::left" to - specify the justify action with a target of left. - - - - In the case that the action target is not a string, or contains - unusual characters, you can use the more general format - "action-name(5)", where the "5" here is any valid text-format - GVariant (ie: a string that can be parsed by g_variant_parse()). - Another example is "open-bookmark('http://gnome.org/')". - - - - You can convert between detailed action names and split-out - action names and target values using g_action_parse_detailed_name() - and g_action_print_detailed_name() but usually you will - not need to. Most APIs will provide both ways of specifying - actions with targets. - - - - - - Action scopes - - - Actions are always scoped to a particular object on which they - operate. - - - - In GTK, actions are typically scoped to either an application - or a window, but any widget can have actions associated with it. - - - - Actions scoped to windows should be the actions that - specifically impact that window. These are actions like - "fullscreen" and "close", or in the case that a window contains - a document, "save" and "print". - - - - Actions that impact the application as a whole rather than one - specific window are scoped to the application. These are actions - like "about" and "preferences". - - - - If a particular action is scoped to a window then it is scoped - to a specific window. Another way of saying this: if your - application has a "fullscreen" action that applies to windows - and it has three windows, then it will have three fullscreen - actions: one for each window. - - - - Having a separate action per-window allows for each window to - have a separate state for each instance of the action as well - as being able to control the enabled state of the action on a - per-window basis. - - - - Actions are added to their relevant scope (application, - window or widget) either using the GActionMap interface, - or by using gtk_widget_insert_action_group(). Actions that - will be the same for all instances of a widget class can - be added globally using gtk_widget_class_install_action(). - - - - - - Action groups and action maps - - - Actions rarely occurs in isolation. It is common to have groups - of related actions, which are represented by instances of the - GActionGroup interface. - - - - Action maps are a variant of action groups that allow to change - the name of the action as it is looked up. In GTK, the convention - is to add a prefix to the action name to indicate the scope of - the actions, such as "app." for the actions with application scope - or "win." for those with window scope. - - - - When referring to actions on a GActionMap only the name of the - action itself is used (ie: "quit", not "app.quit"). The - "app.quit" form is only used when referring to actions from - places like a GMenu or GtkActionable widget where the scope - of the action is not already known. - - - - GtkApplication and GtkApplicationWindow implement the GActionMap - interface, so you can just add actions directly to them. For - other widgets, use gtk_widget_insert_action_group() to add - actions to it. - - - - If you want to insert several actions at the same time, it is - typically faster and easier to use GActionEntry. - - - - - - Connecting actions to widgets - - - Any widget that implements the GtkActionable interface can - be connected to an action just by setting the ::action-name - property. If the action has a parameter, you will also need - to set the ::action-target property. - Widgets that implement GtkActionable include GtkSwitch, GtkButton, - and their respective subclasses. - - - - Another way of obtaining widgets that are connected to actions - is to create a menu using a GMenu menu model. GMenu provides an - abstract way to describe typical menus: nested groups of items - where each item can have a label, and icon, and an action. - - - - Typical uses of GMenu inside GTK are to set up an application - menu or menubar with gtk_application_set_app_menu() or - gtk_application_set_menubar(). Another, maybe more common use - is to create a popover for a menubutton, using - gtk_menu_button_set_menu_model(). - - - - Unlike traditional menus, those created from menu models don't - have keyboard accelerators associated with menu items. Instead, - GtkApplication offers the gtk_application_set_accels_for_action() - API to associate keyboard shortcuts with actions. - - - - - Activation - - - When a widget with a connected action is activated, GTK finds - the action to activate by walking up the widget hierarchy, - looking for a matching action, ending up at the GtkApplication. - - - - - Built-in Actions - - - GTK uses actions for its own purposes in a number places. These - built-in actions can sometimes be activated by applications, and - you should avoid naming conflicts with them when creating your - own actions. - - - default.activate - Activates the default widget in a context - (typically a GtkWindow, GtkDialog or GtkPopover) - - - - clipboard.cut, clipboard.copy, clipboard.paste - Clipboard operations on entries, text view - and labels, typically used in the context menu - - - - selection.delete, selection.select-all - Selection operations on entries, text view - and labels - - - - color.select, color.customize - Operations on colors in GtkColorChooserWidget. - These actions are unusual in that they have the non-trivial - parameter type (dddd). - - - - - - - - - diff --git a/docs/reference/gtk/drawing-model.md b/docs/reference/gtk/drawing-model.md new file mode 100644 index 0000000000..fb3ad74690 --- /dev/null +++ b/docs/reference/gtk/drawing-model.md @@ -0,0 +1,157 @@ +# Overview of the drawing model {#drawing-overview} + +This chapter describes the GTK drawing model in detail. If you +are interested in the procedure which GTK follows to draw its +widgets and windows, you should read this chapter; this will be +useful to know if you decide to implement your own widgets. This +chapter will also clarify the reasons behind the ways certain +things are done in GTK. + +## Windows and events {#drawing-windows} + +Applications that use a windowing system generally create +rectangular regions in the screen called _surfaces_ (GTK is +following the Wayland terminology, other windowing systems +such as X11 may call these _windows_). Traditional windowing +systems do not automatically save the graphical content of +surfaces, and instead ask applications to provide new content +whenever it is needed. For example, if a window that is stacked +below other windows gets raised to the top, then the application +has to repaint it, so the previously obscured area can be shown. +When the windowing system asks an application to redraw a window, +it sends a _frame event_ (_expose event_ in X11 terminology) +for that window. + +Each GTK toplevel window or dialog is associated with a +windowing system surface. Child widgets such as buttons or +entries don't have their own surface; they use the surface +of their toplevel. + +Generally, the drawing cycle begins when GTK receives +a frame event from the underlying windowing system: if the +user drags a window over another one, the windowing system will +tell the underlying surface that it needs to repaint itself. The +drawing cycle can also be initiated when a widget itself decides +that it needs to update its display. For example, when the user +types a character in an entry widget, the entry asks GTK to queue +a redraw operation for itself. + +The windowing system generates frame events for surfaces. The GDK +interface to the windowing system translates such events into +emissions of the ::render signal on the affected surfaces. The GTK +toplevel window connects to that signal, and reacts appropriately. + +The following sections describe how GTK decides which widgets +need to be repainted in response to such events, and how widgets +work internally in terms of the resources they use from the +windowing system. + +## The frame clock {#frameclock} + +All GTK applications are mainloop-driven, which means that most +of the time the app is idle inside a loop that just waits for +something to happen and then calls out to the right place when +it does. On top of this GTK has a frame clock that gives a +“pulse” to the application. This clock beats at a steady rate, +which is tied to the framerate of the output (this is synced to +the monitor via the window manager/compositor). A typical +refresh rate is 60 frames per second, so a new “pulse” happens +roughly every 16 milliseconds. + +The clock has several phases: + +- Events +- Update +- Layout +- Paint + + The phases happens in this order and we will always run each + phase through before going back to the start. + +The Events phase is a stretch of time between each redraw where +GTK processes input events from the user and other events +(like e.g. network I/O). Some events, like mouse motion are +compressed so that only a single mouse motion event per clock +cycle needs to be handled. + +Once the Events phase is over, external events are paused and +the redraw loop is run. First is the Update phase, where all +animations are run to calculate the new state based on the +estimated time the next frame will be visible (available via +the frame clock). This often involves geometry changes which +drive the next phase, Layout. If there are any changes in +widget size requirements the new layout is calculated for the +widget hierarchy (i.e. sizes and positions for all widgets are +determined). Then comes the Paint phase, where we redraw the +regions of the window that need redrawing. + +If nothing requires the Update/Layout/Paint phases we will +stay in the Events phase forever, as we don’t want to redraw +if nothing changes. Each phase can request further processing +in the following phases (e.g. the Update phase will cause there +to be layout work, and layout changes cause repaints). + +There are multiple ways to drive the clock, at the lowest level you +can request a particular phase with gdk_frame_clock_request_phase() +which will schedule a clock beat as needed so that it eventually +reaches the requested phase. However, in practice most things +happen at higher levels: + +- If you are doing an animation, you can use + gtk_widget_add_tick_callback() which will cause a regular + beating of the clock with a callback in the Update phase + until you stop the tick. +- If some state changes that causes the size of your widget to + change you call gtk_widget_queue_resize() which will request + a Layout phase and mark your widget as needing relayout. +- If some state changes so you need to redraw some area of + your widget you use the normal gtk_widget_queue_draw() + set of functions. These will request a Paint phase and + mark the region as needing redraw. + +There are also a lot of implicit triggers of these from the +CSS layer (which does animations, resizes and repaints as needed). + +## The scene graph {#scene-graph} + +The first step in “drawing” a window is that GTK creates +_render nodes_ for all the widgets in the window. The render +nodes are combined into a tree that you can think of as a +_scene graph_ describing your window contents. + +Render nodes belong to the GSK layer, and there are various kinds +of them, for the various kinds of drawing primitives you are likely +to need when translating widget content and CSS styling. Typical +examples are text nodes, gradient nodes, texture nodes or clip nodes. + +In the past, all drawing in GTK happened via cairo. It is still possible +to use cairo for drawing your custom widget contents, by using a cairo +render node. + +A GSK _renderer_ takes these render nodes, transforms them into +rendering commands for the drawing API it targets, and arranges +for the resulting drawing to be associated with the right surface. +GSK has renderers for OpenGL, Vulkan and cairo. + +## Hierarchical drawing {#hierarchical-drawing} + +During the Paint phase GTK receives a single #GdkSurface::render +signal on the toplevel surface. The signal handler will create a +snapshot object (which is a helper for creating a scene graph) and +call the #GtkWidget::snapshot() vfunc, which will propagate down +the widget hierarchy. This lets each widget snapshot its content +at the right place and time, correctly handling things like partial +transparencies and overlapping widgets. + +During the snapshotting of each widget, GTK automatically handles +the CSS rendering according to the CSS box model. It snapshots first +the background, then the border, then the widget content itself, and +finally the outline. + +To avoid excessive work when generating scene graphs, GTK caches render +nodes. Each widget keeps a reference to its render node (which in turn, +will refer to the render nodes of children, and grandchildren, and so +on), and will reuse that node during the Paint phase. Invalidating a +widget (by calling gtk_widget_queue_draw()) discards the cached render +node, forcing the widget to regenerate it the next time it needs to +produce a snapshot. diff --git a/docs/reference/gtk/drawing-model.xml b/docs/reference/gtk/drawing-model.xml deleted file mode 100644 index 88b10df78a..0000000000 --- a/docs/reference/gtk/drawing-model.xml +++ /dev/null @@ -1,232 +0,0 @@ - - - - -The GTK Drawing Model -3 -GTK Library - - - -The GTK Drawing Model - - How widgets draw - - - - - - Overview of the drawing model - - - This chapter describes the GTK drawing model in detail. If you - are interested in the procedure which GTK follows to draw its - widgets and windows, you should read this chapter; this will be - useful to know if you decide to implement your own widgets. This - chapter will also clarify the reasons behind the ways certain - things are done in GTK. - - - - - Windows and events - - - Applications that use a windowing system generally create - rectangular regions in the screen called surfaces - (GTK is following the Wayland terminology, other windowing systems - such as X11 may call these windows). - Traditional windowing systems do not automatically save the - graphical content of surfaces, and instead ask applications to - provide new content whenever it is needed. - For example, if a window that is stacked below other - windows gets raised to the top, then the application has to - repaint it, so the previously obscured area can be shown. - When the windowing system asks an application to redraw - a window, it sends a frame event - (expose event in X11 terminology) - for that window. - - - - Each GTK toplevel window or dialog is associated with a - windowing system surface. Child widgets such as buttons or - entries don't have their own surface; they use the surface - of their toplevel. - - - - Generally, the drawing cycle begins when GTK receives - a frame event from the underlying windowing system: if the - user drags a window over another one, the windowing system will - tell the underlying surface that it needs to repaint itself. The - drawing cycle can also be initiated when a widget itself decides - that it needs to update its display. For example, when the user - types a character in an entry widget, the entry asks GTK to queue - a redraw operation for itself. - - - - The windowing system generates frame events for surfaces. The GDK - interface to the windowing system translates such events into - emissions of the ::render signal on the affected surfaces. - The GTK toplevel window connects to that signal, and reacts appropriately. - - - - The following sections describe how GTK decides which widgets - need to be repainted in response to such events, and how widgets - work internally in terms of the resources they use from the - windowing system. - - - - - The frame clock - - - All GTK applications are mainloop-driven, which means that most - of the time the app is idle inside a loop that just waits for - something to happen and then calls out to the right place when - it does. On top of this GTK has a frame clock that gives a - “pulse” to the application. This clock beats at a steady rate, - which is tied to the framerate of the output (this is synced to - the monitor via the window manager/compositor). A typical - refresh rate is 60 frames per second, so a new “pulse” happens - roughly every 16 milliseconds. - - - - The clock has several phases: - - Events - Update - Layout - Paint - - The phases happens in this order and we will always run each - phase through before going back to the start. - - - - The Events phase is a stretch of time between each redraw where - GTK processes input events from the user and other events - (like e.g. network I/O). Some events, like mouse motion are - compressed so that only a single mouse motion event per clock - cycle needs to be handled. - - - - Once the Events phase is over, external events are paused and - the redraw loop is run. First is the Update phase, where all - animations are run to calculate the new state based on the - estimated time the next frame will be visible (available via - the frame clock). This often involves geometry changes which - drive the next phase, Layout. If there are any changes in - widget size requirements the new layout is calculated for the - widget hierarchy (i.e. sizes and positions for all widgets are - determined). Then comes the Paint phase, where we redraw the - regions of the window that need redrawing. - - - - If nothing requires the Update/Layout/Paint phases we will - stay in the Events phase forever, as we don’t want to redraw - if nothing changes. Each phase can request further processing - in the following phases (e.g. the Update phase will cause there - to be layout work, and layout changes cause repaints). - - - - There are multiple ways to drive the clock, at the lowest level - you can request a particular phase with - gdk_frame_clock_request_phase() which will schedule a clock beat - as needed so that it eventually reaches the requested phase. - However, in practice most things happen at higher levels: - - - If you are doing an animation, you can use - gtk_widget_add_tick_callback() which will cause a regular - beating of the clock with a callback in the Update phase - until you stop the tick. - - - If some state changes that causes the size of your widget - to change you call gtk_widget_queue_resize() which will - request a Layout phase and mark your widget as needing - relayout. - - - If some state changes so you need to redraw some area of - your widget you use the normal gtk_widget_queue_draw() - set of functions. These will request a Paint phase and - mark the region as needing redraw. - - - There are also a lot of implicit triggers of these from the - CSS layer (which does animations, resizes and repaints as needed). - - - - - The scene graph - - - The first step in “drawing” a window is that GTK creates - render nodes for all the widgets - in the window. The render nodes are combined into a tree - that you can think of as a scene graph - describing your window contents. - - - Render nodes belong to the GSK layer, and there are various kinds - of them, for the various kinds of drawing primitives you are likely - to need when translating widget content and CSS styling. Typical - examples are text nodes, gradient nodes, texture nodes or clip nodes. - - - In the past, all drawing in GTK happened via cairo. It is still possible - to use cairo for drawing your custom widget contents, by using a cairo - render node. - - - A GSK renderer takes these render nodes, transforms - them into rendering commands for the drawing API it targets, and arranges - for the resulting drawing to be associated with the right surface. GSK has - renderers for OpenGL, Vulkan and cairo. - - - - - Hierarchical drawing - - - During the Paint phase GTK receives a single #GdkSurface::render signal on - the toplevel surface. The signal handler will create a snapshot object - (which is a helper for creating a scene graph) and call the - #GtkWidget::snapshot() vfunc, which will propagate down the widget hierarchy. - This lets each widget snapshot its content at the right place and time, - correctly handling things like partial transparencies and overlapping widgets. - - - - During the snapshotting of each widget, GTK automatically handles the CSS - rendering according to the CSS box model. It snapshots first the background, - then the border, then the widget content itself, and finally the outline. - - - - To avoid excessive work when generating scene graphs, GTK caches render nodes. - Each widget keeps a reference to its render node (which in turn, will refer to - the render nodes of children, and grandchildren, and so on), and will reuse - that node during the Paint phase. Invalidating a widget (by calling - gtk_widget_queue_draw()) discards the cached render node, forcing the widget - to regenerate it the next time it needs to produce a snapshot. - - - - - - diff --git a/docs/reference/gtk/gtk-markdown-to-docbook b/docs/reference/gtk/gtk-markdown-to-docbook new file mode 100755 index 0000000000..b461a44230 --- /dev/null +++ b/docs/reference/gtk/gtk-markdown-to-docbook @@ -0,0 +1,184 @@ +#!/usr/bin/python +# +# Call pandoc to convert markdown to docbook, then expand gtk-doc +# abbreviations (|[ ]|, function(), #object, %constant, etc) + +import sys +import re +import tempfile +import os.path +import subprocess + +# The following code is taken from gtk-doc + +def ExpandAbbreviations(symbol, text): + # Convert '@param()' + text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1\2()', text) + + # Convert 'function()' or 'macro()'. + # if there is abc_*_def() we don't want to make a link to _def() + # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/ + def f1(m): + return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function")) + text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text) + # handle #Object.func() + text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text) + + # Convert '@param', but not '\@param'. + text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1\2', text) + text = re.sub(r'/\\\@', r'\@', text) + + # Convert '%constant', but not '\%constant'. + # Also allow negative numbers, e.g. %-1. + def f2(m): + return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2), "literal")) + + text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text) + text = re.sub(r'\\\%', r'\%', text) + + # Convert '#symbol', but not '\#symbol'. + def f3(m): + return m.group(1) + MakeHashXRef(m.group(2), "type") + + text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text) + text = re.sub(r'\\#', '#', text) + + return text + +# Standard C preprocessor directives, which we ignore for '#' abbreviations. +PreProcessorDirectives = { + 'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', + 'include', 'line', 'pragma', 'unassert', 'undef', 'warning' +} + +def MakeHashXRef(symbol, tag): + text = symbol + + # Check for things like '#include', '#define', and skip them. + if symbol in PreProcessorDirectives: + return "#%s" % symbol + + # Get rid of special suffixes ('-struct','-enum'). + text = re.sub(r'-struct$', '', text) + text = re.sub(r'-enum$', '', text) + + # If the symbol is in the form "Object::signal", then change the symbol to + # "Object-signal" and use "signal" as the text. + if '::' in symbol: + o, s = symbol.split('::', 1) + symbol = '%s-%s' % (o, s) + text = u'“' + s + u'”' + + # If the symbol is in the form "Object:property", then change the symbol to + # "Object--property" and use "property" as the text. + if ':' in symbol: + o, p = symbol.split(':', 1) + symbol = '%s--%s' % (o, p) + text = u'“' + p + u'”' + + if tag != '': + text = tagify(text, tag) + + return MakeXRef(symbol, text) + +def MakeXRef(symbol, text=None): + """This returns a cross-reference link to the given symbol. + + Though it doesn't try to do this for a few standard C types that it knows + won't be in the documentation. + + Args: + symbol (str): the symbol to try to create a XRef to. + text (str): text to put inside the XRef, defaults to symbol + + Returns: + str: a docbook link + """ + symbol = symbol.strip() + if not text: + text = symbol + + # Get rid of special suffixes ('-struct','-enum'). + text = re.sub(r'-struct$', '', text) + text = re.sub(r'-enum$', '', text) + + if ' ' in symbol: + return text + + symbol_id = CreateValidSGMLID(symbol) + return "%s" % (symbol_id, text) + +def CreateValidSGMLID(xml_id): + """Creates a valid SGML 'id' from the given string. + + According to http://www.w3.org/TR/html4/types.html#type-id "ID and NAME + tokens must begin with a letter ([A-Za-z]) and may be followed by any number + of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), + and periods (".")." + + When creating SGML IDS, we append ":CAPS" to all all-caps identifiers to + prevent name clashes (SGML ids are case-insensitive). (It basically never is + the case that mixed-case identifiers would collide.) + + Args: + id (str): The text to be converted into a valid SGML id. + + Returns: + str: The converted id. + """ + + # Special case, '_' would end up as '' so we use 'gettext-macro' instead. + if xml_id == '_': + return "gettext-macro" + + xml_id = re.sub(r'[,;]', '', xml_id) + xml_id = re.sub(r'[_ ]', '-', xml_id) + xml_id = re.sub(r'^-+', '', xml_id) + xml_id = xml_id.replace('::', '-') + xml_id = xml_id.replace(':', '--') + + # Append ":CAPS" to all all-caps identifiers + # FIXME: there are some inconsistencies here, we have index files containing e.g. TRUE--CAPS + if xml_id.isupper() and not xml_id.endswith('-CAPS'): + xml_id += ':CAPS' + + return xml_id + +def tagify(text, elem): + # Adds a tag around some text. + # e.g tagify("Text", "literal") => "Text". + return '<' + elem + '>' + text + '' + +# End of gtk-doc excerpts + +MarkdownExtensions = { + '-auto_identifiers', # we use explicit identifiers where needed + '+header_attributes', # for explicit identifiers + '+blank_before_header', # helps with gtk-doc #Object abbreviations + '+compact_definition_lists', # to replace + '+pipe_tables', + '+backtick_code_blocks', # to replace |[ ]| + '+fenced_code_attributes', # to add language annotations + '-raw_html', # to escape literal tags like in input +} + +def ConvertToDocbook(infile, outfile): + basename = os.path.basename(infile) + input_format = "markdown" + "".join(MarkdownExtensions) + output_format = "docbook" + subprocess.check_call(["pandoc", infile, "-o", outfile, + "--from=" + input_format, + "--to=" + output_format, + "--top-level-division=chapter"]) + +def ExpandGtkDocAbbreviations(infile, outfile): + contents = open(infile, 'r', encoding='utf-8').read() + with open(outfile, 'w', encoding='utf-8') as out: + out.write(ExpandAbbreviations("file", contents)) + + +if __name__ == '__main__': + tmp = tempfile.mktemp() + ConvertToDocbook(sys.argv[1], tmp) + ExpandGtkDocAbbreviations(tmp, sys.argv[2]) + os.remove(tmp) diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 05e2a341ff..6905a4ea42 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -6,7 +6,7 @@ ]> - + GTK 4 Reference Manual @@ -28,9 +28,9 @@ GTK Concepts - - - + + + @@ -397,8 +397,8 @@ - - + + diff --git a/docs/reference/gtk/input-handling.md b/docs/reference/gtk/input-handling.md new file mode 100644 index 0000000000..20998b396a --- /dev/null +++ b/docs/reference/gtk/input-handling.md @@ -0,0 +1,206 @@ +# Overview of GTK input and event handling {#input-overview} + +This chapter describes in detail how GTK handles input. If you are interested +in what happens to translate a key press or mouse motion of the users into a +change of a GTK widget, you should read this chapter. This knowledge will also +be useful if you decide to implement your own widgets. + +Devices and events + +The most basic input devices that every computer user has interacted with are +keyboards and mice; beyond these, GTK supports touchpads, touchscreens and +more exotic input devices such as graphics tablets. Inside GTK, every such +input device is represented by a #GdkDevice object. + +To simplify dealing with the variability between these input devices, GTK +has a concept of master and slave devices. The concrete physical devices that +have many different characteristics (mice may have 2 or 3 or 8 buttons, +keyboards have different layouts and may or may not have a separate number +block, etc) are represented as slave devices. Each slave device is +associated with a virtual master device. Master devices always come in +pointer/keyboard pairs - you can think of such a pair as a 'seat'. + +GTK widgets generally deal with the master devices, and thus can be used +with any pointing device or keyboard. + +When a user interacts with an input device (e.g. moves a mouse or presses +a key on the keyboard), GTK receives events from the windowing system. +These are typically directed at a specific surface - for pointer events, +the surface under the pointer (grabs complicate this), for keyboard events, +the surface with the keyboard focus. + +GDK translates these raw windowing system events into #GdkEvents. +Typical input events are button clicks, pointer motion, key presses +or touch events. These are all represented as #GdkEvents, but you can +differentiate between different events by looking at their type, using +gdk_event_get_event_type(). + +Some events, such as touch events or button press-release pairs, +are connected in to each other in an “event sequence” that +univocally identifies events that are related to the same +interaction. + +When GTK creates a GdkSurface, it connects to the #GdkSurface::event +signal on it, which receives all of these input events. Surfaces have +have signals and properties, e.g. to deal with window management +related events. + +## Event propagation {#event-propagation} + +The function which initially receives input events on the GTK +side is responsible for a number of tasks. + +1. Find the widget which got the event. +2. Generate crossing (i.e. enter and leave) events when the focus or + hover location change from one widget to another. +3. Send the event to widgets. + +An event is propagated down and up the widget hierarchy in three phases +(see #GtkPropagationPhase) towards a target widget. + +![Event propagation phases](capture-bubble.png) + +For key events, the top-level window gets a first shot at activating +mnemonics and accelerators. If that does not consume the events, +the target widget for event propagation is window's current focus +widget (see gtk_window_get_focus()). + +For pointer events, the target widget is determined by picking +the widget at the events coordinates (see gtk_window_pick()). + +In the first phase (the “capture” phase) the event is delivered to +each widget from the top-most (the top-level #GtkWindow or grab widget) +down to the target #GtkWidget. +[Event controllers](event-controllers-and-gestures) that are attached +with %GTK_PHASE_CAPTURE get a chance to react to the event. + +After the “capture” phase, the widget that was intended to be the +destination of the event will run event controllers attached to +it with %GTK_PHASE_TARGET. This is known as the “target” phase, +and only happens on that widget. + +In the last phase (the “bubble” phase), the event is delivered +to each widget from the target to the top-most, and event +controllers attached with %GTK_PHASE_BUBBLE are run. + +Events are not delivered to a widget which is insensitive or unmapped. + +Any time during the propagation phase, a controller may indicate +that a received event was consumed and propagation should +therefore be stopped. If gestures are used, this may happen +when the gesture claims the event touch sequence (or the +pointer events) for its own. See the “gesture states” section +below to learn more about gestures and sequences. + +## Keyboard input + +Every #GtkWindow maintains a single focus location (in +the #GtkWindow:focus-widget property). The focus widget is the +target widget for key events sent to the window. Only widgets which +have #GtkWidget:can-focus set to %TRUE can become the focus. Typically +these are input controls such as entries or text fields, but e.g. +buttons can take the focus too. + +Input widgets can be given the focus by clicking on them, but focus +can also be moved around with certain key events (this is known as +“keyboard navigation”). GTK reserves the Tab key to move the focus +to the next location, and Shift-Tab to move it back to the previous +one. In addition many containers allow “directional navigation” with +the arrow keys. + +Many widgets can be “activated” to trigger and action. E.g., you can +activate a button or switch by clicking on them, but you can also +activate them with the keyboard, by using the Enter or Space keys. + +Apart from keyboard navigation, activation and directly typing into +entries or text views, GTK widgets can use key events for activating +“shortcuts”. Shortcuts generally act as a quick way to move the focus +around or to activate a widget that does not currently have the focus. + +GTK has traditionally supported different kinds of shortcuts: + +Accelerators + : Accelerators are any other shortcuts that can be activated regardless + of where the focus is, and typically trigger global actions, such as + Ctrl-Q to quit an application. +Mnmemonics + : Mnemonics are usually triggered using Alt as a modifier for a letter. + They are used in places where a label is associated with a control, + and are indicated by underlining the letter in the label. As a special + case, inside menus (i.e. inside #GtkPopoverMenu), mnemonics can be + trigered without the modifier. +Key bindings + : Key bindings are specific to individual widgets, such as Ctrl-C or + Ctrl-V in an entry copy to or paste from the clipboard. They are only + triggered when the widget has focus. + +GTK handles accelerators and mnemonics in a global scope, during the +capture phase, and key bindings locally, during the target phase. + +Under the hood, all shortcuts are represented as instances of #GtkShortcut, +and they are managed by #GtkShortcutController. + +## Event controllers and gestures {#event-controllers-and-gestures} + +Event controllers are standalone objects that can perform +specific actions upon received #GdkEvents. These are tied +to a #GtkWidget, and can be told of the event propagation +phase at which they will manage the events. + +Gestures are a set of specific controllers that are prepared +to handle pointer and/or touch events, each gesture +implementation attempts to recognize specific actions out the +received events, notifying of the state/progress accordingly to +let the widget react to those. On multi-touch gestures, every +interacting touch sequence will be tracked independently. + +Since gestures are “simple” units, it is not uncommon to tie +several together to perform higher level actions, grouped +gestures handle the same event sequences simultaneously, and +those sequences share a same state across all grouped +gestures. Some examples of grouping may be: + +- A “drag” and a “swipe” gestures may want grouping. + The former will report events as the dragging happens, + the latter will tell the swipe X/Y velocities only after + recognition has finished. +- Grouping a “drag” gesture with a “pan” gesture will only + effectively allow dragging in the panning orientation, as + both gestures share state. +- If “press” and “long press” are wanted simultaneously, + those would need grouping. + +Shortcuts are handled by #GtkShortcutController, which is +a complex event handler that can either activate shortcuts +itself, or propagate them to another controller, depending +on its #GtkShortcutController:scope. + +## Gesture states + +Gestures have a notion of “state” for each individual touch +sequence. When events from a touch sequence are first received, +the touch sequence will have “none” state, this means the touch +sequence is being handled by the gesture to possibly trigger +actions, but the event propagation will not be stopped. + +When the gesture enters recognition, or at a later point in time, +the widget may choose to claim the touch sequences (individually +or as a group), hence stopping event propagation after the event +is run through every gesture in that widget and propagation phase. +Anytime this happens, the touch sequences are cancelled downwards +the propagation chain, to let these know that no further events +will be sent. + +Alternatively, or at a later point in time, the widget may choose +to deny the touch sequences, thus letting those go through again +in event propagation. When this happens in the capture phase, and +if there are no other claiming gestures in the widget, +a %GDK_TOUCH_BEGIN/%GDK_BUTTON_PRESS event will be emulated and +propagated downwards, in order to preserve consistency. + +Grouped gestures always share the same state for a given touch +sequence, so setting the state on one does transfer the state to +the others. They also are mutually exclusive, within a widget +where may be only one gesture group claiming a given sequence. +If another gesture group claims later that same sequence, the +first group will deny the sequence: diff --git a/docs/reference/gtk/input-handling.xml b/docs/reference/gtk/input-handling.xml deleted file mode 100644 index 8748148a45..0000000000 --- a/docs/reference/gtk/input-handling.xml +++ /dev/null @@ -1,332 +0,0 @@ - - - - -The GTK Input Model -3 -GTK Library - - - -The GTK Input Model - - input and event handling in detail - - - - - - Overview of GTK input and event handling - - - This chapter describes in detail how GTK handles input. If you are interested - in what happens to translate a key press or mouse motion of the users into a - change of a GTK widget, you should read this chapter. This knowledge will also - be useful if you decide to implement your own widgets. - - - - Devices and events - - - - The most basic input devices that every computer user has interacted with are - keyboards and mice; beyond these, GTK supports touchpads, touchscreens and - more exotic input devices such as graphics tablets. Inside GTK, every such - input device is represented by a #GdkDevice object. - - - - To simplify dealing with the variability between these input devices, GTK - has a concept of master and slave devices. The concrete physical devices that - have many different characteristics (mice may have 2 or 3 or 8 buttons, - keyboards have different layouts and may or may not have a separate number - block, etc) are represented as slave devices. Each slave device is - associated with a virtual master device. Master devices always come in - pointer/keyboard pairs - you can think of such a pair as a 'seat'. - - - GTK widgets generally deal with the master devices, and thus can be used - with any pointing device or keyboard. - - - - When a user interacts with an input device (e.g. moves a mouse or presses - a key on the keyboard), GTK receives events from the windowing system. - These are typically directed at a specific surface - for pointer events, - the surface under the pointer (grabs complicate this), for keyboard events, - the surface with the keyboard focus. - - - GDK translates these raw windowing system events into #GdkEvents. - Typical input events are button clicks, pointer motion, key presses - or touch events. These are all represented as #GdkEvents, but you can - differentiate between different events by looking at their type, using - gdk_event_get_event_type(). - - - Some events, such as touch events or button press-release pairs, - are connected in to each other in an “event sequence” that - univocally identifies events that are related to the same - interaction. - - - When GTK creates a GdkSurface, it connects to the #GdkSurface::event - signal on it, which receives all of these input events. Surfaces have - have signals and properties, e.g. to deal with window management - related events. - - - - - Event propagation - - - The function which initially receives input events on the GTK - side is responsible for a number of tasks. - - - - Find the widget which got the event. - - - Generate crossing (i.e. enter and leave) events when the focus or hover - location change from one widget to another. - - - The event is sent to widgets. - - - - - An event is propagated down and up the widget hierarchy in three phases - (see #GtkPropagationPhase) towards a target widget. - - - - - - - - - - - - For key events, the top-level window gets a first shot at activating - mnemonics and accelerators. If that does not consume the events, - the target widget for event propagation is window's current focus - widget (see gtk_window_get_focus()). - - - - For pointer events, the target widget is determined by picking - the widget at the events coordinates (see gtk_window_pick()). - - - In the first phase (the “capture” phase) the event is - delivered to each widget from the top-most (the top-level - #GtkWindow or grab widget) down to the target #GtkWidget. - Event - controllers that are attached with %GTK_PHASE_CAPTURE - get a chance to react to the event. - - - - After the “capture” phase, the widget that was intended to be the - destination of the event will run event controllers attached to - it with %GTK_PHASE_TARGET. This is known as the “target” phase, - and only happens on that widget. - - - - In the last phase (the “bubble” phase), the event is delivered - to each widget from the target to the top-most, and event - controllers attached with %GTK_PHASE_BUBBLE are run. - - - - Events are not delivered to a widget which is insensitive or - unmapped. - - - - Any time during the propagation phase, a controller may indicate - that a received event was consumed and propagation should - therefore be stopped. If gestures are used, this may happen - when the gesture claims the event touch sequence (or the - pointer events) for its own. See the “gesture states” section - below to learn more about gestures and sequences. - - - - - Keyboard input - - - Every #GtkWindow maintains a single focus location (in the - #GtkWindow:focus-widget property). The focus widget is the target - widget for key events sent to the window. Only widgets which have - #GtkWidget:can-focus set to %TRUE can become the focus. Typically - these are input controls such as entries or text fields, but e.g. - buttons can take the focus too. - - - - Input widgets can be given the focus by clicking on them, but focus - can also be moved around with certain key events (this is known as - “keyboard navigation”). GTK reserves the Tab key to move the focus - to the next location, and Shift-Tab to move it back to the previous - one. In addition many containers allow “directional navigation” with - the arrow keys. - - - - Many widgets can be “activated” to trigger and action. E.g., you can - activate a button or switch by clicking on them, but you can also - activate them with the keyboard, by using the Enter or Space keys. - - - - Apart from keyboard navigation, activation and directly typing into - entries or text views, GTK widgets can use key events for activating - “shortcuts”. Shortcuts generally act as a quick way to move the focus - around or to activate a widget that does not currently have the focus. - - - - GTK has traditionally supported different kinds of shortcuts: - - - Accelerators - - Accelerators are any other shortcuts that can be activated regardless - of where the focus is, and typically trigger global actions, such as - Ctrl-Q to quit an application. - - - - Mnmemonics - - Mnemonics are usually triggered using Alt as a modifier for a letter. - They are used in places where a label is associated with a control, - and are indicated by underlining the letter in the label. As a special - case, inside menus (i.e. inside #GtkPopoverMenu), mnemonics can be - trigered without the modifier. - - - - Key bindings - - Key bindings are specific to individual widgets, such as Ctrl-C or - Ctrl-V in an entry copy to or paste from the clipboard. They are only - triggered when the widget has focus. - - - - - - GTK traditionally handles accelerators and mnemonics in a global scope, - during the capture phase, and key bindings locally, during the target phase. - - - Under the hood, all shortcuts are represented as instances of #GtkShortcut, - and they are managed by #GtkShortcutController. - - - - - Event controllers and gestures - - - Event controllers are standalone objects that can perform - specific actions upon received #GdkEvents. These are tied - to a #GtkWidget, and can be told of the event propagation - phase at which they will manage the events. - - - - Gestures are a set of specific controllers that are prepared - to handle pointer and/or touch events, each gesture - implementation attempts to recognize specific actions out the - received events, notifying of the state/progress accordingly to - let the widget react to those. On multi-touch gestures, every - interacting touch sequence will be tracked independently. - - - - Since gestures are “simple” units, it is not uncommon to tie - several together to perform higher level actions, grouped - gestures handle the same event sequences simultaneously, and - those sequences share a same state across all grouped - gestures. Some examples of grouping may be: - - - - A “drag” and a “swipe” gestures may want grouping. - The former will report events as the dragging happens, - the latter will tell the swipe X/Y velocities only after - recognition has finished. - - - Grouping a “drag” gesture with a “pan” gesture will only - effectively allow dragging in the panning orientation, as - both gestures share state. - - - If “press” and “long press” are wanted simultaneously, - those would need grouping. - - - - - - Shortcuts are handled by #GtkShortcutController, which is - a complex event handler that can either activate shortcuts - itself, or propagate them to another controller, depending - on its #GtkShortcutController:scope. - - - - - Gesture states - - Gestures have a notion of “state” for each individual touch - sequence. When events from a touch sequence are first received, - the touch sequence will have “none” state, this means the touch - sequence is being handled by the gesture to possibly trigger - actions, but the event propagation will not be stopped. - - - - When the gesture enters recognition, or at a later point in time, - the widget may choose to claim the touch sequences (individually - or as a group), hence stopping event propagation after the event - is run through every gesture in that widget and propagation phase. - Anytime this happens, the touch sequences are cancelled downwards - the propagation chain, to let these know that no further events - will be sent. - - - - Alternatively, or at a later point in time, the widget may choose - to deny the touch sequences, thus letting those go through again - in event propagation. When this happens in the capture phase, and - if there are no other claiming gestures in the widget, - a %GDK_TOUCH_BEGIN/%GDK_BUTTON_PRESS event will be emulated and - propagated downwards, in order to preserve consistency. - - - - Grouped gestures always share the same state for a given touch - sequence, so setting the state on one does transfer the state to - the others. They also are mutually exclusive, within a widget - there may be only one gesture group claiming a given sequence. - If another gesture group claims later that same sequence, the - first group will deny the sequence. - - - - - diff --git a/docs/reference/gtk/lists-overview.md b/docs/reference/gtk/lists-overview.md new file mode 100644 index 0000000000..0fc533c341 --- /dev/null +++ b/docs/reference/gtk/lists-overview.md @@ -0,0 +1,101 @@ +# List widgets + +GTK provides powerful widgets to display and edit lists of data. This document gives an overview over the concepts and how they work together to allow developers to implement lists. + +Lists are intended to be used whenever developers want to display lists of objects in roughly the same way. + +Lists are perfectly fine to be used for very short list of only 2 or 3 elements, but generally scale fine to millions of items. Of course, the larger the list grows, the more care needs to be taken to choose the right data structures to keep things running well. + +Lists are meant to be used with changing data, both with the items itself changing as well as the list adding and removing items. Of course, they work just as well with static data. + +## Terminology + +These terms are used throughout the documentation when talking about lists and you should be aware of what they refer to. These are often generic terms that have a specific meaning in this context. + +**_Views_** or **_list widgets_** are the widgets that hold and manage the lists. Examples of thse widgets would be #GtkListView or #GtkGridView. + +Views display data from a **_model_**. A model is a #GListModel and models can be provided in 3 ways or combinations thereof: + + * Many list models implementations already exist. There are models that provide specific data, like #GtkDirectoryList. And there are models like #GListStore that allow building lists manually. + + * Wrapping list models exists like #GtkFilterListModel or #GtkSortListModel that modify or adapt or combine other models. + + * Last but not least, developers are encouraged to create their own #GListModel implementations. The interface is kept deliberately small to make this easy. + +The same model can be used in multiple different views and wrapped with multiple different models at once. + +The elements in a model are called **_items_**. All items are #GObjects. + +Every item in a model has a **_position_** which is the unsigned integer that describes where in the model the item is located. This position can of course change as items are added or removed from the model. + +It is important to be aware of the difference between items and positions because the mapping from position to item is not permanent, so developers should think about whether they want to track items or positions when working with models. Oftentimes some things are really hard to do one way but very easy the other way. + +The other important part of a view is a **_factory_**. Each factory is a #GtkListItemFactory implementation that takes care of mapping the items of the model to widgets that can be shown in the view. + +The way factories do this is by creating a **_listitem_** for each item that is currently in use. Listitems are always #GtkListItem objects. They are only ever created by GTK and provide information about what item they are meant to display. + +Different factory implementations use various different methods to allow developers to add the right widgets to listitems and to link those widgets with the item managed by the listitem. Finding a suitable factory implementation for the data displayed, the programming language and development environment is an important task that can simplify setting up the view tremendously. + +Views support selections via a **_selection model_**. A selection model is an implementation of the #GtkSelectionModel interface on top of the #GListModel interface that allows marking each item in a model as either selected or not selected. Just like regular models, this can be implemented either by implementing #GtkSelectionModel directly or by wrapping a model with one of the GTK models provided for this purposes, such as #GtkNoSelection or #GtkSingleSelection. The behavior of selection models - ie which items they allow selecting and what effect this has on other items - is completely up to the selection model. As such, single-selections, multi-selections or sharing selection state between different selection models and/or views is possible. The selection state of an item is exposed in the listitem via the GtkListItem:selected property. + +Views and listitems also support activation. Activation means that double clicking or pressing enter while inside a focused row will cause the view to emit and activation signal such as GtkListView::activate. This provides an easy way to set up lists, but can also be turned off on listitems if undesired. + +Both selections and activation are supported among other things via widget actions (FIXME: Link docs). This allows developers to add widgets to their lists that cause selections to change or to trigger activation via the #GtkActionable interface. For a list of all supported actions see the relevant documentation. (FIXME: where do we document actions and how to I link that?) + +## Behind the scenes + +While for short lists it is not a problem to instantiate widgets for every item in the model, once lists grow to thousands or millions of elements, this gets less feasible. Because of this, the views only create a limited amount of listitems and recycle them by binding them to new items. In general, views try to keep listitems available only for the items that can actually be seen on screen. + +While this behavior allows views to scale effortlessly to huge lists, it has a few implication on what can be done with views. For example, it is not possible to query a view for a listitem used for a certain position - there might not be one and even if there is, that listitem might soon be recycled for a new position. + +It is also important that developers save state they care about in the item and do not rely on the widgets they created as those widgets can be recycled for a new position at any time causing any state to be lost. + +Another important requirement for views is that they need to know which items are not visible so they can be recycled. Views achieve that by implementing the #GtkScrollable interface and expecting to be placed directly into a #GtkScrolledWindow. + +Of course, if you are only using models with few items, this is not important and you can treat views like any other widget. But if you use large lists and your performance suffers, you should be aware of this. Views also allow tuning the number of listitems they create such as with gtk_grid_view_set_max_columns(), and developers running into performance problems should definitely study the tradeoffs of those and experiment with them. + +## Displaying trees + +While #GtkTreeView provided builtin support for trees, the list widgets, and in particular #GListModel do not. This was a design choice because the common use case is displaying lists and not trees and it greatly simplifies the API interface provided. + +However, GTK provides functionality to make trees look and behave like lists for the people who still want to display lists. This is achieved by using the #GtkTreeListModel model to flatten a tree into a list. The #GtkTreeExpander widget can then be used inside a listitem to allow users to expand and collapse rows and provide a similar experience to #GtkTreeView. + +Developers should refer to those objects' API reference for more discussion on the topic. + +## comparison to GtkTreeView + +Developers familiar with #GtkTreeView may wonder how this way of doing lists compares to the way they know. This section will try to outline the similarities and differences between the two. + +This new approach tries to provide roughly the same functionality as the old approach but often uses a very different approach to achieve these goals. + +The main difference and one of the primary reasons for this new development is that items can be displayed using regular widgets and #GtkCellRenderer is no longer necessary. This allows all benefits that widgets provide, such as complex layout and animating widgets and not only makes cell renderers obsolete, but also #GtkCellArea. + +The other big difference is the massive change to the data model. #GtkTreeModel was a rather complex interface for a tree data structure and #GListModel was deliberately designed to be a simple data structure for lists only. (See above (FIXME: link) for how to still do trees with this new model.) Another big change is that the new model allows for bulk changes via the #GListModel:items-changed signal while #GtkTreeModel only allows a single item to change at once. The goal here is of course to encourage implementation of custom list models. + +Another consequence of the new model is that it is now easily possible to refer to the contents of a row in the model directly by keeping the item, while #GtkTreeRowReference was a very slow mechanism to achieve the same. And because the items are real objects, developers can make them emit change signals causing listitems and their children to update, which wasn't possible with #GtkTreeModel. + +The selection handling is also different. While selections used to be managed via custom code in each widget, selection state is now meant to be managed by the selection models. In particular this allows for complex use cases with specialized requirements (FIXME: Can I add a shoutout to @mitch here because I vividly remember a huge discussion about GtkTreeView's selection behavior and the Gimp). + +Finally here's a quick list of equivalent functionality to look for when transitioning code for easy lookup: + +| old | new | +| ------------------- | ----------------------------------- | +| #GtkTreeModel | #GListModel | +| #GtkTreePath | #guint position, #GtkTreeListRow | +| #GtkTreeIter | #guint position | +| GtkTreeRowReference | #GObject item | +| #GtkListStore | #GListStore | +| #GtkTreeStore | #GtkTreeListModel, #GtkTreeExpander | +| #GtkTreeSelection | #GtkSelectionModel | +| #GtkTreeViewColumn | FIXME: ColumnView | +| #GtkTreeView | #GtkListView, FIXME: ColumnView | +| #GtkCellView | ? | +| #GtkComboBox | FIXME | +| #GtkIconView | #GtkGridView | +| #GtkTreeSortable | FIXME: ColumnView? | +| #GtkTreeModelSort | #GtkSortListModel | +| #GtkTreeModelFilter | #GtkFilterListModel | +| #GtkCellLayout | #GtkListItemFactory | +| #GtkCellArea | #GtkWidget | +| #GtkCellRenderer | #GtkWidget | + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index d279fed315..b8ef12a6bd 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -1,3 +1,5 @@ +fs = import('fs') + private_headers = [ 'imm-extra.h', 'gtkbitmaskprivateimpl.h', @@ -338,12 +340,10 @@ images = [ ] content_files = [ - 'actions.xml', 'broadway.xml', 'building.xml', 'compiling.xml', 'css-overview.xml', - 'drawing-model.xml', 'glossary.xml', 'gtk4-broadwayd.xml', 'gtk4-builder-tool.xml', @@ -355,9 +355,6 @@ content_files = [ 'gtk4-query-settings.xml', 'gtk4-update-icon-cache.xml', 'gtk4-widget-factory.xml', - 'input-handling.xml', - 'migrating-2to4.xml', - 'migrating-3to4.xml', 'osx.xml', 'other_software.xml', 'overview.xml', @@ -373,18 +370,21 @@ content_files = [ ] expand_content_files = [ - 'actions.xml', 'compiling.xml', - 'drawing-model.xml', 'glossary.xml', - 'input-handling.xml', - 'migrating-2to4.xml', - 'migrating-3to4.xml', 'question_index.xml', 'text_widget.xml', 'tree_widget.xml', ] +expand_content_md_files = [ + 'migrating-2to4.md', + 'migrating-3to4.md', + 'actions.md', + 'input-handling.md', + 'drawing-model.md' +] + types_conf = configuration_data() if os_win32 types_conf.set('DISABLE_ON_W32', '%') @@ -402,6 +402,15 @@ if get_option('gtk_doc') configure_file(input: 'version.xml.in', output: 'version.xml', configuration: version_conf) configure_file(input: 'getting_started.xml.in', output: 'getting_started.xml', configuration: src_dir_conf) + expand_md = find_program('gtk-markdown-to-docbook') + expand_md_targets = [] + foreach t : expand_content_md_files + expand_md_targets += custom_target(t, + input: [ t ], + output: [ fs.replace_suffix(t, '.xml') ], + command: [ expand_md, '@INPUT@', '@OUTPUT@']) + endforeach + gnome.gtkdoc('gtk4', mode: 'none', main_xml: 'gtk4-docs.xml', @@ -431,7 +440,7 @@ if get_option('gtk_doc') '--extra-dir=../gdk', '--extra-dir=../gsk', ], - content_files: content_files, + content_files: content_files + expand_md_targets, expand_content_files: expand_content_files, html_assets: images, install: true) diff --git a/docs/reference/gtk/migrating-2to4.md b/docs/reference/gtk/migrating-2to4.md new file mode 100644 index 0000000000..dbfd941e23 --- /dev/null +++ b/docs/reference/gtk/migrating-2to4.md @@ -0,0 +1,5 @@ +# Migrating from GTK 2.x to GTK 4 {#gtk-migrating-2-to-4} + +If your application is still using GTK 2, you should first convert it to GTK 3, +by following the [migration guide](https://developer.gnome.org/gtk3/stable/gtk-migrating-2-to-3.html) +in the GTK 3 documentation, and then follow [these instructions](#gtk-migrating-3-to-4). diff --git a/docs/reference/gtk/migrating-2to4.xml b/docs/reference/gtk/migrating-2to4.xml deleted file mode 100644 index 9b071acd44..0000000000 --- a/docs/reference/gtk/migrating-2to4.xml +++ /dev/null @@ -1,15 +0,0 @@ - - -]> - - Migrating from GTK 2.x to GTK 4 - - - If your application is still using GTK 2, you should first convert it to - GTK 3, by following the migration guide in the GTK 3 - documentation, and then follow . - - - diff --git a/docs/reference/gtk/migrating-3to4.md b/docs/reference/gtk/migrating-3to4.md new file mode 100644 index 0000000000..55fc99a710 --- /dev/null +++ b/docs/reference/gtk/migrating-3to4.md @@ -0,0 +1,928 @@ +# Migrating from GTK 3.x to GTK 4 {#gtk-migrating-3-to-4} + +GTK 4 is a major new version of GTK that breaks both API and ABI +compared to GTK 3.x. Thankfully, most of the changes are not hard +to adapt to and there are a number of steps that you can take to +prepare your GTK 3.x application for the switch to GTK 4. After +that, there's a number of adjustments that you may have to do +when you actually switch your application to build against GTK 4. + +## Preparation in GTK 3.x + +The steps outlined in the following sections assume that your +application is working with GTK 3.24, which is the final stable +release of GTK 3.x. It includes all the necessary APIs and tools +to help you port your application to GTK 4. If you are using +an older version of GTK 3.x, you should first get your application +to build and work with the latest minor release in the 3.24 series. + +### Do not use deprecated symbols + +Over the years, a number of functions, and in some cases, entire +widgets have been deprecated. These deprecations are clearly spelled +out in the API reference, with hints about the recommended replacements. +The API reference for GTK 3 also includes an +[index](https://developer.gnome.org/gtk3/3.24/api-index-deprecated.html) +of all deprecated symbols. + +To verify that your program does not use any deprecated symbols, +you can use defines to remove deprecated symbols from the header files, +as follows: +``` +make CFLAGS+="-DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED" +``` + +Note that some parts of our API, such as enumeration values, are +not well covered by the deprecation warnings. In most cases, using +them will require you to also use deprecated functions, which will +trigger warnings. + +### Enable diagnostic warnings + +Deprecations of properties and signals cannot be caught at compile +time, as both properties and signals are installed and used after +types have been instantiated. In order to catch deprecations and +changes in the run time components, you should use the +`G_ENABLE_DIAGNOSTIC` environment variable when running your +application, e.g.: +``` +G_ENABLE_DIAGNOSTIC=1 ./your-app +``` + +### Do not use widget style properties + +Style properties do not exist in GTK 4. You should stop using them in +your custom CSS and in your code. + +### Review your window creation flags + +GTK 4 removes the `GDK_WA_CURSOR` flag. Instead, just use +gdk_window_set_cursor() to set a cursor on the window after +creating it. GTK 4 also removes the `GDK_WA_VISUAL` flag, and +always uses an RGBA visual for windows. To prepare your code for +this, use `gdk_window_set_visual (gdk_screen_get_rgba_visual ())` +after creating your window. GTK 4 also removes the `GDK_WA_WMCLASS` +flag. If you need this X11-specific functionality, use XSetClassHint() +directly. + +### Stop using direct access to GdkEvent structs + +In GTK 4, event structs are opaque and immutable. Many fields already +have accessors in GTK 3, and you should use those to reduce the amount +of porting work you have to do at the time of the switch. + +### Stop using gdk_pointer_warp() + +Warping the pointer is disorienting and unfriendly to users. +GTK 4 does not support it. In special circumstances (such as when +implementing remote connection UIs) it can be necessary to +warp the pointer; in this case, use platform APIs such as +XWarpPointer() directly. + +### Stop using non-RGBA visuals + +GTK 4 always uses RGBA visuals for its windows; you should make +sure that your code works with such visuals. At the same time, +you should stop using GdkVisual APIs, since this object not longer +exists in GTK 4. Most of its APIs are deprecated already and not +useful when dealing with RGBA visuals. + +### Stop using GtkBox padding, fill and expand child properties + +GTK 4 removes these #GtkBox child properties, so you should stop using +them. You can replace GtkBox:padding using the #GtkWidget:margin properties +on your #GtkBox child widgets. + +The fill child property can be replaced by setting appropriate values +for the #GtkWidget:halign and #GtkWidget:valign properties of the child +widgets. If you previously set the fill child property to %TRUE, you can +achieve the same effect by setting the halign or valign properties to +%GTK_ALIGN_FILL, depending on the parent box -- halign for a horizontal +box, valign for a vertical one. + +\#GtkBox also uses the expand child property. It can be replaced by setting +#GtkWidget:hexpand or #GtkWidget:vexpand on the child widgets. To match the +old behavior of the #GtkBox's expand child property, you need to set +#GtkWidget:hexpand on the child widgets of a horizontal #GtkBox and +#GtkWidget:vexpand on the child widgets of a vertical #GtkBox. + +Note that there's a subtle but important difference between #GtkBox's +expand and fill child properties and the ones in #GtkWidget: setting +#GtkWidget:hexpand or #GtkWidget:vexpand to %TRUE will propagate up +the widget hierarchy, so a pixel-perfect port might require you to reset +the expansion flags to %FALSE in a parent widget higher up the hierarchy. + +### Stop using the state argument of GtkStyleContext getters + +The getters in the GtkStyleContext API, such as +gtk_style_context_get_property(), gtk_style_context_get(), +or gtk_style_context_get_color() only accept the context's current +state for their state argument. You should update all callers to pass +the current state. + +### Stop using gdk_pixbuf_get_from_window() and gdk_cairo_set_source_surface() + +These functions are not supported in GTK 4. Instead, either use +backend-specific APIs, or render your widgets using +#GtkWidgetClass.snapshot() (once you are using GTK 4). + +Stop using GtkButton's image-related API + +The functions and properties related to automatically add a GtkImage +to a GtkButton, and using a GtkSetting to control its visibility, are +not supported in GTK 4. Instead, you can just pack a GtkImage inside +a GtkButton, and control its visibility like you would for any other +widget. If you only want to add a named icon to a GtkButton, you can +use gtk_button_new_from_icon_name(). + +### Stop using GtkWidget event signals + +Event controllers and #GtkGestures replace event signals in GTK 4. +They have been backported to GTK 3.x so you can prepare for this change. + +### Set a proper application ID + +In GTK 4 we want the application's #GApplication 'application-id' +(and therefore the D-Bus name), the desktop file basename and Wayland's +xdg-shell app_id to match. In order to achieve this with GTK 3.x call +g_set_prgname() with the same application ID you passed to #GtkApplication. +Rename your desktop files to match the application ID if needed. + +The call to g_set_prgname() can be removed once you fully migrated to GTK 4. + +You should be aware that changing the application ID makes your +application appear as a new, different app to application installers. +You should consult the appstream documentation for best practices +around renaming applications. + +### Stop using gtk_main() and related APIs + +GTK 4 removes the gtk_main_ family of APIs. The recommended replacement +is GtkApplication, but you can also iterate the GLib mainloop directly, +using GMainContext APIs. The replacement for gtk_events_pending() is +g_main_context_pending(), the replacement for gtk_main_iteration() is +g_main_context_iteration(). + +### Reduce the use of gtk_widget_destroy() + +GTK 4 introduces a gtk_window_destroy() api. While that is not available +in GTK 3, you can prepare for the switch by using gtk_widget_destroy() +only on toplevel windows, and replace all other uses with +gtk_container_remove() or g_object_unref(). + +### Reduce the use of generic container APIs + +GTK 4 removes gtk_container_add() and gtk_container_remove(). While there +is not always a replacement for gtk_container_remove() in GTK 3, you can +replace many uses of gtk_container_add() with equivalent container-specific +APIs such as gtk_box_pack_start() or gtk_grid_attach(), and thereby reduce +the amount of work you have to do at the time of the switch. + +## Changes that need to be done at the time of the switch + +This section outlines porting tasks that you need to tackle when +you get to the point that you actually build your application against +GTK 4. Making it possible to prepare for these in GTK 3 would +have been either impossible or impractical. + +### Stop using GdkScreen + +The GdkScreen object has been removed in GTK 4. Most of its APIs already +had replacements in GTK 3 and were deprecated, a few remaining replacements +have been added to GdkDisplay. + +### Stop using the root window + +The root window is an X11-centric concept that is no longer exposed in the +backend-neutral GDK API. If you need to interact with the X11 root window, +you can use gdk_x11_display_get_xrootwindow() to get its XID. + +### Stop using GdkVisual + +This object is not useful with current GTK drawing APIs and has been removed +without replacement. + +### Stop using GdkDeviceManager + +The GdkDeviceManager object has been removed in GTK 4. Most of its APIs already +had replacements in GTK 3 and were deprecated in favor of GdkSeat. + +### Adapt to GdkWindow API changes + +GdkWindow has been renamed to GdkSurface. + +In GTK 4, the two roles of a standalone toplevel window and of a popup +that is placed relative to a parent window have been separated out into +two interfaces, #GdkToplevel and #GdkPopup. Surfaces implementing these +interfaces are created with gdk_surface_new_toplevel() and +gdk_surface_new_popup(), respectively, and they are presented on screen +using gdk_toplevel_present() and gdk_popup_present(). The present() +functions take parameters in the form of an auxiliary layout struct, +#GdkPopupLayout or #GdkToplevelLayout. If your code is dealing directly +with surfaces, you may have to change it to call the API in these +interfaces, depending on whether the surface you are dealing with +is a toplevel or a popup. + +As part of this reorganization, X11-only concepts such as sticky or +keep-below have been removed. If you need to use them on your X11 windows, +you will have to set the corresponding X11 properties (as specified in the +EWMH) yourself. Subsurfaces are only supported with the Wayland backend, +using gdk_wayland_surface_new_subsurface(). Native and foreign subwindows +are no longer supported. These concepts were complicating the code and +could not be supported across backends. + +gdk_window_reparent() is no longer available. + +A number of minor API cleanups have happened in GdkSurface +as well. For example, gdk_surface_input_shape_combine_region() +has been renamed to gdk_surface_set_input_region(), and +gdk_surface_begin_resize_drag() has been renamed to +gdk_toplevel_begin_resize(). + +### The "iconified" window state has been renamed to "minimized" + +The %GDK_SURFACE_STATE_ICONIFIED value of the +#GdkSurfaceState enumeration is now %GDK_SURFACE_STATE_MINIMIZED. + +The #GdkWindow functions gdk_window_iconify() +and gdk_window_deiconify() have been renamed to +gdk_toplevel_minimize() and gdk_toplevel_present(), respectively. + +The behavior of the minimization and unminimization operations have +not been changed, and they still require support from the underlying +windowing system. + +### Adapt to GdkEvent API changes + +Direct access to GdkEvent structs is no longer possible in GTK 4. +GdkEvent is now a strictly read-only type, and you can no longer +change any of its fields, or construct new events. All event fields +have accessors that you will have to use. + +Event compression is always enabled in GTK 4. If you need to see +the uncoalesced motion history, use gdk_motion_event_get_history() +on the latest motion event. + +### Stop using grabs + +GTK 4 no longer provides the gdk_device_grab() or gdk_seat_grab() +apis. If you need to dismiss a popup when the user clicks outside +(the most common use for grabs), you can use the GdkPopup +#GdkPopup:autohide property instead. GtkPopover also has a +#GtkPopover:autohide property for this. If you need to prevent +the user from interacting with a window while a dialog is open, +use the #GtkWindow:modal property of the dialog. + +### Adapt to coordinate API changes + +A number of coordinate APIs in GTK 3 had _double variants: +gdk_device_get_position(), gdk_device_get_surface_at_position(), +gdk_surface_get_device_position(). These have been changed to use +doubles, and the _double variants have been removed. Update your +code accordingly. + +Any APIs that deal with global (or root) coordinates have been +removed in GTK 4, since not all backends support them. You should +replace your use of such APIs with surface-relative equivalents. +Examples of this are gdk_surface_get_origin(), gdk_surface_move() +or gdk_event_get_root_coords(). + +### Adapt to GdkKeymap API changes + +GdkKeymap no longer exists as an independent object. + +If you need access to keymap state, it is now exposed as properties +on the #GdkDevice representing the keyboard: #GdkDevice:direction, +#GdkDevice:has-bidi-layouts, #GdkDevice:caps-lock-state, +#GdkDevice:num-lock-state, #GdkDevice:scroll-lock-state and +#GdkDevice:modifier-state. To obtain the keyboard device, you can use +`gdk_seat_get_keyboard (gdk_display_get_default_seat (display)`. + +If you need access to translated keys for event handling, #GdkEvent +now includes all of the translated key state, including consumed +modifiers, group and shift level, so there should be no need to +manually call gdk_keymap_translate_keyboard_state() (which has +been removed). + +If you need to do forward or backward mapping between key codes +and key values, use gdk_display_map_keycode() and gdk_display_map_keyval(), +which are the replacements for gdk_keymap_get_entries_for_keycode() +and gdk_keymap_get_entries_for_keyval(). + +### Adapt to changes in keyboard modifier handling + +GTK 3 has the idea that use of modifiers may differ between different +platforms, and has a #GdkModifierIntent api to let platforms provide +hint about how modifiers are expected to be used. It also promoted +the use of instead of to specify accelerators that +adapt to platform conventions. + +In GTK 4, the meaning of modifiers has been fixed, and backends are +expected to map the platform conventions to the existing modifiers. +The expected use of modifiers in GTK 4 is: + +GDK_CONTROL_MASK + : Primary accelerators + GDK_ALT_MASK + : Mnemonics +GDK_SHIFT_MASK + : Extending selections +GDK_CONTROL_MASK + : Modifying selections +GDK_CONTROL_MASK|GDK_ALT_MASK + : Prevent text input + +Consequently, #GdkModifierIntent and related APIs have been removed, +and is preferred over in accelerators. + +A related change is that GTK 4 no longer supports the use of archaic +X11 'real' modifiers with the names Mod1,..., Mod5, and %GDK_MOD1_MASK +has been renamed to %GDK_ALT_MASK. + +### Stop using gtk_get_current_... APIs + +The function gtk_get_current_event() and its variants have been +replaced by equivalent event controller APIs: +gtk_event_controller_get_current_event(), etc. + +### Convert your ui files + +A number of the changes outlined below affect .ui files. The +gtk4-builder-tool simplify command can perform many of the +necessary changes automatically, when called with the --3to4 +option. You should always review the resulting changes. + +### Adapt to event controller API changes + +A few changes to the event controller and #GtkGesture APIs +did not make it back to GTK 3, and have to be taken into account +when moving to GTK 4. One is that the #GtkEventControllerMotion::enter +and #GtkEventControllerMotion::leave signals have gained new arguments. +Another is that #GtkGestureMultiPress has been renamed to #GtkGestureClick, +and has lost its area property. A #GtkEventControllerFocus has been +split off from #GtkEventcontrollerKey. + +### Focus handling changes + +The semantics of the #GtkWidget:can-focus property have changed. +In GTK 3, this property only meant that the widget itself would not +accept keyboard input, but its children still might (in the case of +containers). In GTK 4, if :can-focus is %FALSE, the focus cannot enter +the widget or any of its descendents, and the default value has changed +from %FALSE to %TRUE. In addition, there is a #GtkWidget:focusable +property, which controls whether an individual widget can receive +the input focus. + +The feature to automatically keep the focus widget scrolled into view +with gtk_container_set_focus_vadjustment() has been removed together with +GtkContainer, and is provided by scrollable widgets instead. In the common +case that the scrollable is a #GtkViewport, use #GtkViewport:scroll-to-focus. + +### Stop using GtkEventBox + +GtkEventBox is no longer needed and has been removed. +All widgets receive all events. + +### Stop using GtkButtonBox + +GtkButtonBox has been removed. Use a GtkBox instead. + +### Adapt to GtkBox API changes + +The GtkBox pack-start and -end methods have been replaced by gtk_box_prepend() +and gtk_box_append(). You can also reorder box children as necessary. + +### Adapt to GtkHeaderBar and GtkActionBar API changes + +The gtk_header_bar_set_show_close_button() function has been renamed to +the more accurate name gtk_header_bar_set_show_title_buttons(). The +corresponding getter and the property itself have also been renamed. + +The gtk_header_bar_set_custom_title() function has been renamed to +the more accurate name gtk_header_bar_set_title_widget(). The +corresponding getter and the property itself have also been renamed. + +The gtk_header_bar_set_title() function has been removed along with its +corresponding getter and the property. By default #GtkHeaderBar shows +the title of the window, so if you were setting the title of the header +bar, consider setting the window title instead. If you need to show a +title that's different from the window title, use the +#GtkHeaderBar:title-widget property to add a #GtkLabel as shown in the +example in #GtkHeaderBar documentation. + +The gtk_header_bar_set_subtitle() function has been removed along with +its corresponding getter and the property. The old "subtitle" behavior +can be replicated by setting the #GtkHeaderBar:title-widget property to +a #GtkBox with two labels inside, with the title label matching the +example in #GtkHeaderBar documentation, and the subtitle label being +similar, but with "subtitle" style class instead of "title". + +The gtk_header_bar_set_has_subtitle() function has been removed along +with its corresponding getter and the property. Its behavior can be +replicated by setting the #GtkHeaderBar:title-widget property to a +#GtkStack with #GtkStack:vhomogeneous property set to %TRUE and two +pages, each with a #GtkBox with title and subtitle as described above. + +The ::pack-type child properties of GtkHeaderBar and GtkActionBar have +been removed. If you need to programmatically place children, use the +pack_start() and pack_end() APIs. In ui files, use the type attribute +on the child element. + +gtk4-builder-tool can help with this conversion, with the --3to4 option +of the simplify command. + +### Adapt to GtkStack, GtkAssistant and GtkNotebook API changes + +The child properties of GtkStack, GtkAssistant and GtkNotebook have been +converted into child meta objects. +Instead of gtk_container_child_set (stack, child, …), you can now use +g_object_set (gtk_stack_get_page (stack, child), …). In .ui files, the +GtkStackPage objects must be created explicitly, and take the child widget +as property. GtkNotebook and GtkAssistant are similar. + +gtk4-builder-tool can help with this conversion, with the --3to4 option +of the simplify command. + +### Adapt to GtkBin removal + +The abstract base class GtkBin for single-child containers has been +removed. The former subclasses are now derived directly from GtkWidget, +and have a "child" property for their child widget. To add a child, use +the setter for the "child" property (e.g. gtk_frame_set_child()) instead +of gtk_container_add(). Adding a child in a ui file with still works. + +The affected classes are: + +- GtkAspectFrame +- GtkButton (and subclasses) +- GtkComboBox +- GtkFlowBoxChild +- GtkFrame +- GtkListBoxRow +- GtkOverlay +- GtkPopover +- GtkRevealer +- GtkScrolledWindow +- GtkSearchBar +- GtkViewport +- GtkWindow (and subclasses) + +If you have custom widgets that were derived from GtkBin, you should +port them to derive from GtkWidget. Notable vfuncs that you will have +to implement include dispose() (to unparent your child), compute_expand() +(if you want your container to propagate expand flags) and +get_request_mode() (if you want your container to support height-for-width. + +You may also want to implement the GtkBuildable interface, to support +adding children with in ui files. + +### Adapt to GtkContainer removal + +The abstract base class GtkContainer for general containers has been +removed. The former subclasses are now derived directly from GtkWidget, +and have class-specific add() and remove() functions. +The most noticable change is the use of gtk_box_append() or gtk_box_prepend() +instead of gtk_container_add() for adding children to GtkBox, and the change +to use container-specific remove functions, such as gtk_stack_remove() instead +of gtk_container_remove(). Adding a child in a ui file with still works. + +The affected classes are: + +- GtkActionBar +- GtkBox (and subclasses) +- GtkExpander +- GtkFixed +- GtkFlowBox +- GtkGrid +- GtkHeaderBar +- GtkIconView +- GtkInfoBar +- GtkListBox +- GtkNotebook +- GtkPaned +- GtkStack +- GtkTextView +- GtkTreeView + +Without GtkContainer, there are no longer facilities for defining and +using child properties. If you have custom widgets using child properties, +they will have to be converted either to layout properties provided +by a layout manager (if they are layout-related), or handled in some +other way. One possibility is to use child meta objects, as seen with +GtkAssistantPage, GtkStackPage and the like. + +### Stop using GtkContainer::border-width + +GTK 4 has removed the #GtkContainer::border-width property (together +with the rest of GtkContainer). Use other means to influence the spacing +of your containers, such as the CSS margin and padding properties on child +widgets. + +### Adapt to gtk_widget_destroy() removal + +The function gtk_widget_destroy() has been removed. To explicitly destroy +a toplevel window, use gtk_window_destroy(). To destroy a widget that is +part of a hierarchy, remove it from its parent using a container-specific +remove api, such as gtk_box_remove() or gtk_stack_remove(). To destroy +a freestanding non-toplevel widget, use g_object_unref() to drop your +reference. + +### Adapt to coordinate API changes + +A number of APIs that are accepting or returning coordinates have +been changed from ints to doubles: gtk_widget_translate_coordinates(), +gtk_fixed_put(), gtk_fixed_move(). This change is mostly transparent, +except for cases where out parameters are involved: you need to +pass double* now, instead of int*. + +### Adapt to GtkStyleContext API changes + +The getters in the GtkStyleContext API, such as +gtk_style_context_get_property(), gtk_style_context_get(), +or gtk_style_context_get_color() have lost their state argument, +and always use the context's current state. Update all callers +to omit the state argument. + +The most commonly used GtkStyleContext API, gtk_style_context_add_class(), +has been moved to GtkWidget as gtk_widget_add_css_class(), as have the +corresponding gtk_style_context_remove_class() and +gtk_style_context_has_class() APIs. + +### Adapt to GtkCssProvider API changes + +In GTK 4, the various #GtkCssProvider load functions have lost their +#GError argument. If you want to handle CSS loading errors, use the +#GtkCssProvider::parsing-error signal instead. gtk_css_provider_get_named() +has been replaced by gtk_css_provider_load_named(). + +### Stop using GtkShadowType and GtkRelief properties + +The shadow-type properties in GtkScrolledWindow, GtkViewport, +and GtkFrame, as well as the relief properties in GtkButton +and its subclasses have been removed. GtkScrolledWindow, GtkButton +and GtkMenuButton have instead gained a boolean has-frame +property. + +### Adapt to GtkWidget's size request changes + +GTK 3 used five different virtual functions in GtkWidget to +implement size requisition, namely the gtk_widget_get_preferred_width() +family of functions. To simplify widget implementations, GTK 4 uses +only one virtual function, GtkWidgetClass::measure() that widgets +have to implement. gtk_widget_measure() replaces the various +gtk_widget_get_preferred_ functions for querying sizes. + +### Adapt to GtkWidget's size allocation changes + +The #GtkWidget.size_allocate() vfunc takes the baseline as an argument +now, so you no longer need to call gtk_widget_get_allocated_baseline() +to get it. + +The ::size-allocate signal has been removed, since it is easy +to misuse. If you need to learn about sizing changes of custom +drawing widgets, use the #GtkDrawingArea::resize or #GtkGLArea::resize +signals. + +### Switch to GtkWidget's children APIs + +In GTK 4, any widget can have children (and GtkContainer is gone). +There is new API to navigate the widget tree, for use in widget +implementations: gtk_widget_get_first_child(), gtk_widget_get_last_child(), +gtk_widget_get_next_sibling(), gtk_widget_get_prev_sibling(). + +### Don't use -gtk-gradient in your CSS + +GTK now supports standard CSS syntax for both linear and radial +gradients, just use those. + +### Don't use -gtk-icon-effect in your CSS + +GTK now supports a more versatile -gtk-icon-filter instead. Replace +-gtk-icon-effect: dim; with -gtk-icon-filter: opacity(0.5); and +-gtk-icon-effect: hilight; with -gtk-icon-filter: brightness(1.2);. + +### Don't use -gtk-icon-theme in your CSS + +GTK now uses the current icon theme, without a way to change this. + +### Don't use -gtk-outline-...-radius in your CSS + +These non-standard properties have been removed from GTK +CSS. Just use regular border radius. + +### Adapt to drawing model changes + +This area has seen the most radical changes in the transition from GTK 3 +to GTK 4. Widgets no longer use a draw() function to render their contents +to a cairo surface. Instead, they have a snapshot() function that creates +one or more GskRenderNodes to represent their content. Third-party widgets +that use a draw() function or a #GtkWidget::draw signal handler for custom +drawing will need to be converted to use gtk_snapshot_append_cairo(). + +The auxiliary #GtkSnapshot object has APIs to help with creating render +nodes. + +If you are using a #GtkDrawingArea for custom drawing, you need to switch +to using gtk_drawing_area_set_draw_func() to set a draw function instead +of connnecting a handler to the #GtkWidget::draw signal. + +### Stop using APIs to query GdkSurfaces + +A number of APIs for querying special-purpose windows have been removed, +since these windows are no longer publically available: +gtk_tree_view_get_bin_window(), gtk_viewport_get_bin_window(), +gtk_viewport_get_view_window(). + +### Widgets are now visible by default + +The default value of #GtkWidget:visible in GTK 4 is %TRUE, so you no +longer need to explicitly show all your widgets. On the flip side, you +need to hide widgets that are not meant to be visible from the start. +The only widgets that still need to be explicitly shown are toplevel +windows, dialogs and popovers. + +A convenient way to remove unnecessary property assignments like this +from ui files it run the command `gtk4-builder-tool simplify --replace` +on them. + +The function gtk_widget_show_all(), the #GtkWidget:no-show-all property +and its getter and setter have been removed in GTK 4, so you should stop +using them. + +### Adapt to changes in animated hiding and showing of widgets + +Widgets that appear and disappear with an animation, such as GtkPopover, +GtkInfoBar, GtkRevealer no longer use gtk_widget_show() and gtk_widget_hide() +for this, but have gained dedicated APIs for this purpose that you should +use. + +### Stop passing commandline arguments to gtk_init + +The gtk_init() and gtk_init_check() functions no longer accept commandline +arguments. Just call them without arguments. Other initialization functions +that were purely related to commandline argument handling, such as +gtk_parse_args() and gtk_get_option_group(), are gone. The APIs to +initialize GDK separately are also gone, but it is very unlikely +that you are affected by that. + +### GdkPixbuf is deemphasized + +A number of #GdkPixbuf-based APIs have been removed. The available replacements +are either using #GIcon, or the newly introduced #GdkTexture or #GdkPaintable +classes instead. If you are dealing with pixbufs, you can use +gdk_texture_new_for_pixbuf() to convert them to texture objects where needed. + +### GtkWidget event signals are removed + +Event controllers and #GtkGestures have already been introduced in GTK 3 to handle +input for many cases. In GTK 4, the traditional widget signals for handling input, +such as #GtkWidget::motion-event or #GtkWidget::event have been removed. + +### Invalidation handling has changed + +Only gtk_widget_queue_draw() is left to mark a widget as needing redraw. +Variations like gtk_widget_queue_draw_rectangle() or gtk_widget_queue_draw_region() +are no longer available. + +### Stop using GtkWidget::draw + +The #GtkWidget::draw signal has been removed. Widgets need to implement the +#GtkWidgetClass.snapshot() function now. Connecting draw signal handlers is +no longer possible. If you want to keep using cairo for drawing, use +gtk_snaphot_append_cairo(). + +### Window content observation has changed + +Observing widget contents and widget size is now done by using the +#GtkWidgetPaintable object instead of connecting to widget signals. + +### Monitor handling has changed + +Instead of a monitor number, #GdkMonitor is now used throughout. +gdk_display_get_monitors() returns the list of monitors that can be queried +or observed for monitors to pass to APIs like gtk_window_fullscreen_on_monitor(). + +### Adapt to cursor API changes + +Use the new gtk_widget_set_cursor() function to set cursors, instead of +setting the cursor on the underlying window directly. This is necessary +because most widgets don't have their own window anymore, turning any +such calls into global cursor changes. + +For creating standard cursors, gdk_cursor_new_for_display() has been removed, +you have to use cursor names instead of GdkCursorType. For creating custom cursors, +use gdk_cursor_new_from_texture(). The ability to get cursor images has been removed. + +### Adapt to icon size API changes + +Instead of the existing extensible set of symbolic icon sizes, GTK now only +supports normal and large icons with the #GtkIconSize enumeration. The actual sizes +can be defined by themes via the CSS property -gtk-icon-size. + +GtkImage setters like gtk_image_set_from_icon_name() no longer take a #GtkIconSize +argument. You can use the separate gtk_image_set_icon_size() setter if you need +to override the icon size. + +The :stock-size property of GtkCellRendererPixbuf has been renamed to +#GtkCellRendererPixbuf:icon-size. + +### Adapt to changes in the GtkAssistant API + +The :has-padding property is gone, and GtkAssistant no longer adds padding +to pages. You can easily do that yourself. + +### Adapt to changes in the API of GtkEntry, GtkSearchEntry and GtkSpinButton + +The GtkEditable interface has been made more useful, and the core functionality of +GtkEntry has been broken out as a GtkText widget. GtkEntry, GtkSearchEntry, +GtkSpinButton and the new GtkPasswordEntry now use a GtkText widget internally +and implement GtkEditable. In particular, this means that it is no longer +possible to use GtkEntry API such as gtk_entry_grab_focus_without_selecting() +on a search entry. + +Use GtkEditable API for editable functionality, and widget-specific APIs for +things that go beyond the common interface. For password entries, use +GtkPasswordEntry. As an example, gtk_spin_button_set_max_width_chars() +has been removed in favor of gtk_editable_set_max_width_chars(). + +### Adapt to changes in GtkOverlay API + +The GtkOverlay :pass-through child property has been replaced by the +#GtkWidget:can-target property. Note that they have the opposite sense: +pass-through == !can-target. + +### Use GtkFixed instead of GtkLayout + +Since GtkScrolledWindow can deal with widgets that do not implement +the GtkScrollable interface by automatically wrapping them into a +GtkViewport, GtkLayout is redundant, and has been removed in favor +of the existing GtkFixed container widget. + +### Adapt to search entry changes + +The way search entries are connected to global events has changed; +gtk_search_entry_handle_event() has been dropped and replaced by +gtk_search_entry_set_key_capture_widget() and +gtk_event_controller_key_forward(). + +### Stop using gtk_window_activate_default() + +The handling of default widgets has been changed, and activating +the default now works by calling gtk_widget_activate_default() +on the widget that caused the activation. If you have a custom widget +that wants to override the default handling, you can provide an +implementation of the default.activate action in your widgets' action +groups. + +### Stop setting ::has-default and ::has-focus in .ui files + +The special handling for the ::has-default and ::has-focus properties +has been removed. If you want to define the initial focus or the +the default widget in a .ui file, set the ::default-widget or +::focus-widget properties of the toplevel window. + +### Stop using the GtkWidget::display-changed signal + +To track the current display, use the #GtkWidget::root property instead. + +### GtkPopover::modal has been renamed to autohide + +The modal property has been renamed to autohide. +gtk-builder-tool can assist with the rename in ui files. + +### gtk_widget_get_surface has been removed + +gtk_widget_get_surface() has been removed. +Use gtk_native_get_surface() in combination with +gtk_widget_get_native() instead. + +### gtk_widget_is_toplevel has been removed + +gtk_widget_is_toplevel() has been removed. +Use GTK_IS_ROOT, GTK_IS_NATIVE or GTK_IS_WINDOW +instead, as appropriate. + +### gtk_widget_get_toplevel has been removed + +gtk_widget_get_toplevel() has been removed. +Use gtk_widget_get_root() or gtk_widget_get_native() +instead, as appropriate. + +### GtkEntryBuffer ::deleted-text has changed + +To allow signal handlers to access the deleted text before it +has been deleted #GtkEntryBuffer::deleted-text has changed from +%G_SIGNAL_RUN_FIRST to %G_SIGNAL_RUN_LAST. The default handler +removes the text from the #GtkEntryBuffer. + +To adapt existing code, use g_signal_connect_after() or +%G_CONNECT_AFTER when using g_signal_connect_data() or +g_signal_connect_object(). + +### GtkMenu, GtkMenuBar and GtkMenuItem are gone + +These widgets were heavily relying on X11-centric concepts such as +override-redirect windows and grabs, and were hard to adjust to other +windowing systems. + +Menus can already be replaced using GtkPopoverMenu in GTK 3. Additionally, +GTK 4 introduces GtkPopoverMenuBar to replace menubars. These new widgets +can only be constructed from menu models, so the porting effort involves +switching to menu models and actions. + +Tabular menus were rarely used and complicated the menu code, +so they have not been brought over to #GtkPopoverMenu. If you need +complex layout in menu-like popups, consider directly using a +#GtkPopover instead. + +Since menus are gone, GtkMenuButton also lost its ability to show menus, +and needs to be used with popovers in GTK 4. + +### GtkToolbar has been removed + +Toolbars were using outdated concepts such as requiring special toolitem +widgets. Toolbars should be replaced by using a GtkBox with regular widgets +instead and the "toolbar" style class. + +### GtkAspectFrame is no longer a frame + +GtkAspectFrame is no longer derived from GtkFrame and does not +place a label and frame around its child anymore. It still lets +you control the aspect ratio of its child. + +### Stop using custom tooltip windows + +Tooltips no longer use GtkWindows in GTK 4, and it is no longer +possible to provide a custom window for tooltips. Replacing the content +of the tooltip with a custom widget is still possible, with +gtk_tooltip_set_custom(). + +### Switch to the new Drag-and-Drop api + +The source-side Drag-and-Drop apis in GTK 4 have been changed to use an event +controller, #GtkDragSource. Instead of calling gtk_drag_source_set() +and connecting to #GtkWidget signals, you create a #GtkDragSource object, +attach it to the widget with gtk_widget_add_controller(), and connect +to #GtkDragSource signals. Instead of calling gtk_drag_begin() on a widget +to start a drag manually, call gdk_drag_begin(). +The ::drag-data-get signal has been replaced by the #GtkDragSource::prepare +signal, which returns a #GdkContentProvider for the drag operation. + +The destination-side Drag-and-Drop apis in GTK 4 have also been changed +to use an event controller, #GtkDropTarget. Instead of calling +gtk_drag_dest_set() and connecting to #GtkWidget signals, you create +a #GtkDropTarget object, attach it to the widget with +gtk_widget_add_controller(), and connect to #GtkDropTarget signals. +The ::drag-motion signal has been renamed to #GtkDropTarget::accept, and +instead of ::drag-data-received, you need to use async read methods on the +#GdkDrop object, such as gdk_drop_read_async() or gdk_drop_read_value_async(). + +### Adapt to GtkIconTheme API changes + +gtk_icon_theme_lookup_icon() returns a #GtkIconPaintable object now, instead +of a #GtkIconInfo. It always returns a paintable in the requested size, and +never fails. A number of no-longer-relevant lookup flags and API variants +have been removed. + +### Update to GtkFileChooser API changes + +GtkFileChooser moved to a GFile-based API. If you need to convert a +path or a URI, use g_file_new_for_path(), g_file_new_for_commandline_arg(), +or g_file_new_for_uri(); similarly, if you need to get a path or a URI +from a GFile, use g_file_get_path(), or g_file_get_uri(). With the +removal or path and URI-based functions, the "local-only" property has +been removed; GFile can be used to access non-local as well as local +resources. + +The GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER action has been removed. Use +%GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, instead. If a new folder is needed, +the user can create one. + +The "confirm-overwrite" signal, and the "do-overwrite-confirmation" +property have been removed from GtkFileChooser. The file chooser widgets +will automatically handle the confirmation of overwriting a file when +using GTK_FILE_CHOOSER_ACTION_SAVE. + +GtkFileChooser does not support a custom extra widget any more. If you +need to add extra widgets, use gtk_file_chooser_add_choice() instead. + +GtkFileChooser does not support a custom preview widget any more. If +you need to show a custom preview, you can create your own GtkDialog +with a GtkFileChooserWidget and your own preview widget that you +update whenever the #GtkFileChooser::selection-changed signal is +emitted. + +### Stop using blocking dialog functions + +GtkDialog, GtkNativeDialog, and GtkPrintOperation removed their +blocking API using nested main loops. Nested main loops present +re-entrancy issues and other hard to debug issues when coupled +with other event sources (IPC, accessibility, network operations) +that are not under the toolkit or the application developer's +control. Additionally, "stop-the-world" functions do not fit +the event-driven programming model of GTK. + +You can replace calls to gtk_dialog_run() +by specifying that the #GtkDialog must be modal using +gtk_window_set_modal() or the %GTK_DIALOG_MODAL flag, and +connecting to the #GtkDialog::response signal. diff --git a/docs/reference/gtk/migrating-3to4.xml b/docs/reference/gtk/migrating-3to4.xml deleted file mode 100644 index 2c58d0a3f8..0000000000 --- a/docs/reference/gtk/migrating-3to4.xml +++ /dev/null @@ -1,1265 +0,0 @@ - - -]> - - Migrating from GTK 3.x to GTK 4 - - - GTK 4 is a major new version of GTK that breaks both API and ABI - compared to GTK 3.x. Thankfully, most of the changes are not hard - to adapt to and there are a number of steps that you can take to - prepare your GTK 3.x application for the switch to GTK 4. After - that, there's a number of adjustments that you may have to do - when you actually switch your application to build against GTK 4. - - -
- Preparation in GTK 3.x - - - The steps outlined in the following sections assume that your - application is working with GTK 3.24, which is the final stable - release of GTK 3.x. It includes all the necessary APIs and tools - to help you port your application to GTK 4. If you are using - an older version of GTK 3.x, you should first get your application - to build and work with the latest minor release in the 3.24 series. - - -
- Do not use deprecated symbols - - Over the years, a number of functions, and in some cases, entire - widgets have been deprecated. These deprecations are clearly spelled - out in the API reference, with hints about the recommended replacements. - The API reference for GTK 3 also includes an - index of all deprecated symbols. - - - To verify that your program does not use any deprecated symbols, - you can use defines to remove deprecated symbols from the header files, - as follows: - - make CFLAGS+="-DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED" - - - - Note that some parts of our API, such as enumeration values, are - not well covered by the deprecation warnings. In most cases, using - them will require you to also use deprecated functions, which will - trigger warnings. - -
- -
- Enable diagnostic warnings - - Deprecations of properties and signals cannot be caught at compile - time, as both properties and signals are installed and used after - types have been instantiated. In order to catch deprecations and - changes in the run time components, you should use the - G_ENABLE_DIAGNOSTIC environment variable when - running your application, e.g.: - - G_ENABLE_DIAGNOSTIC=1 ./your-app - - -
- -
- Do not use widget style properties - - Style properties do not exist in GTK 4. You should stop using them in - your custom CSS and in your code. - -
- -
- Review your window creation flags - - GTK 4 removes the GDK_WA_CURSOR flag. Instead, just use - gdk_window_set_cursor() to set a cursor on the window after - creating it. GTK 4 also removes the GDK_WA_VISUAL flag, and - always uses an RGBA visual for windows. To prepare your code - for this, use - gdk_window_set_visual (gdk_screen_get_rgba_visual ()) - after creating your window. - GTK 4 also removes the GDK_WA_WMCLASS flag. If you need this - X11-specific functionality, use XSetClassHint() directly. - -
- -
- Stop using direct access to GdkEvent structs - - In GTK 4, event structs are opaque and immutable. Many fields already - have accessors in GTK 3, and you should use those to reduce the amount - of porting work you have to do at the time of the switch. - -
- -
- Stop using gdk_pointer_warp() - - Warping the pointer is disorienting and unfriendly to users. - GTK 4 does not support it. In special circumstances (such as when - implementing remote connection UIs) it can be necessary to - warp the pointer; in this case, use platform APIs such as - XWarpPointer() directly. - -
- -
- Stop using non-RGBA visuals - - GTK 4 always uses RGBA visuals for its windows; you should make - sure that your code works with such visuals. At the same time, - you should stop using GdkVisual APIs, since this object not longer - exists in GTK 4. Most of its APIs are deprecated already and not - useful when dealing with RGBA visuals. - -
- -
- Stop using GtkBox padding, fill and expand child properties - - GTK 4 removes these #GtkBox child properties, so you should stop using - them. You can replace GtkBox:padding using the #GtkWidget:margin properties - on your #GtkBox child widgets. - - - The fill child property can be replaced by setting appropriate values - for the #GtkWidget:halign and #GtkWidget:valign properties of the child - widgets. If you previously set the fill child property to %TRUE, you can - achieve the same effect by setting the halign or valign properties to - %GTK_ALIGN_FILL, depending on the parent box -- halign for a horizontal - box, valign for a vertical one. - - - #GtkBox also uses the expand child property. It can be replaced by setting - #GtkWidget:hexpand or #GtkWidget:vexpand on the child widgets. To match the - old behavior of the #GtkBox's expand child property, you need to set - #GtkWidget:hexpand on the child widgets of a horizontal #GtkBox and - #GtkWidget:vexpand on the child widgets of a vertical #GtkBox. - - - Note that there's a subtle but important difference between #GtkBox's - expand and fill child properties and the ones in #GtkWidget: setting - #GtkWidget:hexpand or #GtkWidget:vexpand to %TRUE will propagate up - the widget hierarchy, so a pixel-perfect port might require you to reset - the expansion flags to %FALSE in a parent widget higher up the hierarchy. - -
- -
- Stop using the state argument of GtkStyleContext getters - - The getters in the GtkStyleContext API, such as - gtk_style_context_get_property(), gtk_style_context_get(), - or gtk_style_context_get_color() only accept the context's current - state for their state argument. You should update all callers to pass - the current state. - -
- -
- Stop using gdk_pixbuf_get_from_window() and gdk_cairo_set_source_surface() - - These functions are not supported in GTK 4. Instead, either use - backend-specific APIs, or render your widgets using - #GtkWidgetClass.snapshot() (once you are using GTK 4). - -
- -
- Stop using GtkButton's image-related API - - The functions and properties related to automatically add a GtkImage - to a GtkButton, and using a GtkSetting to control its visibility, are - not supported in GTK 4. Instead, you can just pack a GtkImage inside - a GtkButton, and control its visibility like you would for any other - widget. If you only want to add a named icon to a GtkButton, you can - use gtk_button_new_from_icon_name(). - -
- -
- Stop using GtkWidget event signals - - Event controllers and #GtkGestures replace event signals in GTK 4. - They have been backported to GTK 3.x so you can prepare for this change. - -
- -
- Set a proper application ID - - In GTK 4 we want the application's #GApplication 'application-id' - (and therefore the D-Bus name), the desktop file basename and Wayland's - xdg-shell app_id to match. In order to achieve this with GTK 3.x call - g_set_prgname() with the same application ID you passed to #GtkApplication. - Rename your desktop files to match the application ID if needed. - - - The call to g_set_prgname() can be removed once you fully migrated - to GTK 4. - - - You should be aware that changing the application ID makes your - application appear as a new, different app to application installers. - You should consult the appstream documentation for best practices - around renaming applications. - -
- -
- Stop using gtk_main() and related APIs - - - GTK 4 removes the gtk_main_ family of APIs. The recommended replacement - is GtkApplication, but you can also iterate the GLib mainloop directly, - using GMainContext APIs. The replacement for gtk_events_pending() is - g_main_context_pending(), the replacement for gtk_main_iteration() is - g_main_context_iteration(). - -
- -
- Reduce the use of gtk_widget_destroy() - - GTK 4 introduces a gtk_window_destroy() api. While that is not available - in GTK 3, you can prepare for the switch by using gtk_widget_destroy() - only on toplevel windows, and replace all other uses with - gtk_container_remove() or g_object_unref(). - -
- -
- Reduce the use of generic container APIs - - GTK 4 removes gtk_container_add() and gtk_container_remove(). While there - is not always a replacement for gtk_container_remove() in GTK 3, you can - replace many uses of gtk_container_add() with equivalent container-specific - APIs such as gtk_box_pack_start() or gtk_grid_attach(), and thereby reduce - the amount of work you have to do at the time of the switch. - -
- -
- Stop using app menus - - GNOME has stopped using exported app menus some time ago, and GTK 4 is - dropping the GtkApplication APIs that are related to app menus. If you - want to transparently support menubars on OS X, you can integrate the - items from your app menu into your menubar, and keep using - gtk_application_set_menubar(). - -
-
- -
- Changes that need to be done at the time of the switch - - - This section outlines porting tasks that you need to tackle when - you get to the point that you actually build your application against - GTK 4. Making it possible to prepare for these in GTK 3 would - have been either impossible or impractical. - - -
- Stop using GdkScreen - - The GdkScreen object has been removed in GTK 4. Most of its APIs already - had replacements in GTK 3 and were deprecated, a few remaining replacements - have been added to GdkDisplay. - -
- -
- Stop using the root window - - The root window is an X11-centric concept that is no longer exposed in the - backend-neutral GDK API. If you need to interact with the X11 root window, - you can use gdk_x11_display_get_xrootwindow() to get its XID. - -
- -
- Stop using GdkVisual - - This object is not useful with current GTK drawing APIs and has been removed - without replacement. - -
- -
- Stop using GdkDeviceManager - - The GdkDeviceManager object has been removed in GTK 4. Most of its APIs already - had replacements in GTK 3 and were deprecated in favor of GdkSeat. - -
- -
- Adapt to GdkWindow API changes - - GdkWindow has been renamed to GdkSurface. - - - In GTK 4, the two roles of a standalone toplevel window and of a popup - that is placed relative to a parent window have been separated out into - two interfaces, #GdkToplevel and #GdkPopup. Surfaces implementing these - interfaces are created with gdk_surface_new_toplevel() and - gdk_surface_new_popup(), respectively, and they are presented on screen - using gdk_toplevel_present() and gdk_popup_present(). The present() - functions take parameters in the form of an auxiliary layout struct, - #GdkPopupLayout or #GdkToplevelLayout. If your code is dealing directly - with surfaces, you may have to change it to call the API in these - interfaces, depending on whether the surface you are dealing with - is a toplevel or a popup. - - - As part of this reorganization, X11-only concepts such as sticky or - keep-below have been removed. If you need to use them on your X11 windows, - you will have to set the corresponding X11 properties (as specified in the - EWMH) yourself. Subsurfaces are only supported with the Wayland backend, - using gdk_wayland_surface_new_subsurface(). Native and foreign subwindows - are no longer supported. These concepts were complicating the code and - could not be supported across backends. - - - gdk_window_reparent() is no longer available. - - - A number of minor API cleanups have happened in GdkSurface - as well. For example, gdk_surface_input_shape_combine_region() - has been renamed to gdk_surface_set_input_region(), and - gdk_surface_begin_resize_drag() has been renamed to - gdk_toplevel_begin_resize(). - -
- -
- The "iconified" window state has been renamed to "minimized" - - The GDK_SURFACE_STATE_ICONIFIED value of the - #GdkSurfaceState enumeration is now %GDK_SURFACE_STATE_MINIMIZED. - - - The #GdkWindow functions gdk_window_iconify() - and gdk_window_deiconify() have been renamed to - gdk_toplevel_minimize() and gdk_toplevel_present(), respectively. - - - The behavior of the minimization and unminimization operations have - not been changed, and they still require support from the underlying - windowing system. - -
- -
- Adapt to GdkEvent API changes - - Direct access to GdkEvent structs is no longer possible in GTK 4. - GdkEvent is now a strictly read-only type, and you can no longer - change any of its fields, or construct new events. All event fields - have accessors that you will have to use. - - - Event compression is always enabled in GTK 4. If you need to see - the uncoalesced motion history, use gdk_motion_event_get_history() - on the latest motion event. - -
- -
- Stop using grabs - - GTK 4 no longer provides the gdk_device_grab() or gdk_seat_grab() - apis. If you need to dismiss a popup when the user clicks outside - (the most common use for grabs), you can use the GdkPopup - #GdkPopup:autohide property instead. GtkPopover also has a - #GtkPopover:autohide property for this. If you need to prevent - the user from interacting with a window while a dialog is open, - use the #GtkWindow:modal property of the dialog. - -
- -
- Adapt to coordinate API changes - - A number of coordinate APIs in GTK 3 had _double variants: - gdk_device_get_position(), gdk_device_get_surface_at_position(), - gdk_surface_get_device_position(). These have been changed to use - doubles, and the _double variants have been removed. Update your - code accordingly. - - - Any APIs that deal with global (or root) coordinates have been - removed in GTK 4, since not all backends support them. You should - replace your use of such APIs with surface-relative equivalents. - Examples of this are gdk_surface_get_origin(), gdk_surface_move() - or gdk_event_get_root_coords(). - -
- -
- Adapt to GdkKeymap API changes - - GdkKeymap no longer exists as an independent object. - - - If you need access to keymap state, it is now exposed as properties - on the #GdkDevice representing the keyboard: #GdkDevice:direction, - #GdkDevice:has-bidi-layouts, #GdkDevice:caps-lock-state, - #GdkDevice:num-lock-state, #GdkDevice:scroll-lock-state and - #GdkDevice:modifier-state. To obtain the keyboard device, you can use - gdk_seat_get_keyboard (gdk_display_get_default_seat (display)). - - - If you need access to translated keys for event handling, #GdkEvent - now includes all of the translated key state, including consumed - modifiers, group and shift level, so there should be no need to - manually call gdk_keymap_translate_keyboard_state() (which has - been removed). - - - If you need to do forward or backward mapping between key codes - and key values, use gdk_display_map_keycode() and gdk_display_map_keyval(), - which are the replacements for gdk_keymap_get_entries_for_keycode() - and gdk_keymap_get_entries_for_keyval(). - -
- -
- Adapt to changes in keyboard modifier handling - - GTK 3 has the idea that use of modifiers may differ between different - platforms, and has a #GdkModifierIntent api to let platforms provide - hint about how modifiers are expected to be used. It also promoted - the use of <Primary> instead of <Control> to specify - accelerators that adapt to platform conventions. - - - In GTK 4, the meaning of modifiers has been fixed, and backends are - expected to map the platform conventions to the existing modifiers. - The expected use of modifiers in GTK 4 is: - - GDK_CONTROL_MASK - Primary accelerators - GDK_ALT_MASK - Mnemonics - GDK_SHIFT_MASK - Extending selections - GDK_CONTROL_MASK - Modifying selections - GDK_CONTROL_MASK|GDK_ALT_MASK - Prevent text input - - - - Consequently, #GdkModifierIntent and related APIs have been removed, - and <Control> is preferred over <Primary> in accelerators. - - - A related change is that GTK 4 no longer supports the use of archaic - X11 'real' modifiers with the names Mod1,..., Mod5, and %GDK_MOD1_MASK - has been renamed to %GDK_ALT_MASK. - -
- -
- Stop using gtk_get_current_... APIs - - The function gtk_get_current_event() and its variants have been - replaced by equivalent event controller APIs: - gtk_event_controller_get_current_event(), etc. - -
- -
- Convert your ui files - - - A number of the changes outlined below affect .ui files. The - gtk4-builder-tool simplify command can perform many of the - necessary changes automatically, when called with the --3to4 - option. You should always review the resulting changes. - -
- -
- Adapt to event controller API changes - - A few changes to the event controller and #GtkGesture APIs - did not make it back to GTK 3, and have to be taken into account - when moving to GTK 4. One is that the #GtkEventControllerMotion::enter - and #GtkEventControllerMotion::leave signals have gained new arguments. - Another is that #GtkGestureMultiPress has been renamed to #GtkGestureClick, - and has lost its area property. A #GtkEventControllerFocus has been - split off from #GtkEventcontrollerKey. - -
- -
- Focus handling changes - - The semantics of the #GtkWidget:can-focus property have changed. - In GTK 3, this property only meant that the widget itself would not - accept keyboard input, but its children still might (in the case of - containers). In GTK 4, if :can-focus is %FALSE, the focus cannot enter - the widget or any of its descendents, and the default value has changed - from %FALSE to %TRUE. In addition, there is a #GtkWidget:focusable - property, which controls whether an individual widget can receive - the input focus. - - - The feature to automatically keep the focus widget scrolled into view - with gtk_container_set_focus_vadjustment() has been removed together with - GtkContainer, and is provided by scrollable widgets instead. In the common - case that the scrollable is a #GtkViewport, use #GtkViewport:scroll-to-focus. - -
- -
- Stop using GtkEventBox - - GtkEventBox is no longer needed and has been removed. - All widgets receive all events. - -
- -
- Stop using GtkButtonBox - - GtkButtonBox has been removed. Use a GtkBox instead. - -
- -
- Adapt to GtkBox API changes - - The GtkBox pack-start and -end methods have been replaced by gtk_box_prepend() - and gtk_box_append(). You can also reorder box children as necessary. - -
- -
- Adapt to GtkHeaderBar and GtkActionBar API changes - - The gtk_header_bar_set_show_close_button() function has been renamed to - the more accurate name gtk_header_bar_set_show_title_buttons(). The - corresponding getter and the property itself have also been renamed. - - - The gtk_header_bar_set_custom_title() function has been renamed to - the more accurate name gtk_header_bar_set_title_widget(). The - corresponding getter and the property itself have also been renamed. - - - The gtk_header_bar_set_title() function has been removed along with its - corresponding getter and the property. By default #GtkHeaderBar shows - the title of the window, so if you were setting the title of the header - bar, consider setting the window title instead. If you need to show a - title that's different from the window title, use the - #GtkHeaderBar:title-widget property to add a #GtkLabel as shown in the - example in #GtkHeaderBar documentation. - - - The gtk_header_bar_set_subtitle() function has been removed along with - its corresponding getter and the property. The old "subtitle" behavior - can be replicated by setting the #GtkHeaderBar:title-widget property to - a #GtkBox with two labels inside, with the title label matching the - example in #GtkHeaderBar documentation, and the subtitle label being - similar, but with "subtitle" style class instead of "title". - - - The gtk_header_bar_set_has_subtitle() function has been removed along - with its corresponding getter and the property. Its behavior can be - replicated by setting the #GtkHeaderBar:title-widget property to a - #GtkStack with #GtkStack:vhomogeneous property set to %TRUE and two - pages, each with a #GtkBox with title and subtitle as described above. - - - The ::pack-type child properties of GtkHeaderBar and GtkActionBar have - been removed. If you need to programmatically place children, use the - pack_start() and pack_end() APIs. In ui files, use the type attribute - on the child element. - - - gtk4-builder-tool can help with this conversion, with the --3to4 option - of the simplify command. - -
- -
- Adapt to GtkStack, GtkAssistant and GtkNotebook API changes - - The child properties of GtkStack, GtkAssistant and GtkNotebook have been - converted into child meta objects. - Instead of gtk_container_child_set (stack, child, …), you can now use - g_object_set (gtk_stack_get_page (stack, child), …). In .ui files, the - GtkStackPage objects must be created explicitly, and take the child widget - as property. GtkNotebook and GtkAssistant are similar. - - - gtk4-builder-tool can help with this conversion, with the --3to4 option - of the simplify command. - -
- -
- Adapt to GtkBin removal - - The abstract base class GtkBin for single-child containers has been - removed. The former subclasses are now derived directly from GtkWidget, - and have a "child" property for their child widget. To add a child, use - the setter for the "child" property (e.g. gtk_frame_set_child()) instead - of gtk_container_add(). Adding a child in a ui file with - child still works. - The affected classes are: - - - GtkAspectFrame - GtkButton (and subclasses) - GtkComboBox - GtkFlowBoxChild - GtkFrame - GtkListBoxRow - GtkOverlay - GtkPopover - GtkRevealer - GtkScrolledWindow - GtkSearchBar - GtkViewport - GtkWindow (and subclasses) - - - If you have custom widgets that were derived from GtkBin, you should - port them to derive from GtkWidget. Notable vfuncs that you will have - to implement include dispose() (to unparent your child), compute_expand() - (if you want your container to propagate expand flags) and - get_request_mode() (if you want your container to support height-for-width. - - - You may also want to implement the GtkBuildable interface, to support - adding children with child in ui files. - -
- -
- Adapt to GtkContainer removal - - The abstract base class GtkContainer for general containers has been - removed. The former subclasses are now derived directly from GtkWidget, - and have class-specific add() and remove() functions. - The most noticable change is the use of gtk_box_append() or gtk_box_prepend() - instead of gtk_container_add() for adding children to GtkBox, and the change - to use container-specific remove functions, such as gtk_stack_remove() instead - of gtk_container_remove(). Adding a child in a ui file with - child still works. - The affected classes are: - - - GtkActionBar - GtkBox (and subclasses) - GtkExpander - GtkFixed - GtkFlowBox - GtkGrid - GtkHeaderBar - GtkIconView - GtkInfoBar - GtkListBox - GtkNotebook - GtkPaned - GtkStack - GtkTextView - GtkTreeView - - - Without GtkContainer, there are no longer facilities for defining and - using child properties. If you have custom widgets using child properties, - they will have to be converted either to layout properties provided - by a layout manager (if they are layout-related), or handled in some - other way. One possibility is to use child meta objects, as seen with - GtkAssistantPage, GtkStackPage and the like. - -
- -
- Stop using GtkContainer::border-width - - GTK 4 has removed the #GtkContainer::border-width property (together - with the rest of GtkContainer). Use other means to influence the spacing - of your containers, such as the CSS margin and padding properties on child - widgets. - -
- -
- Adapt to gtk_widget_destroy() removal - - The function gtk_widget_destroy() has been removed. To explicitly destroy - a toplevel window, use gtk_window_destroy(). To destroy a widget that is - part of a hierarchy, remove it from its parent using a container-specific - remove api, such as gtk_box_remove() or gtk_stack_remove(). To destroy - a freestanding non-toplevel widget, use g_object_unref() to drop your - reference. - -
- -
- Adapt to coordinate API changes - - A number of APIs that are accepting or returning coordinates have - been changed from ints to doubles: gtk_widget_translate_coordinates(), - gtk_fixed_put(), gtk_fixed_move(). This change is mostly transparent, - except for cases where out parameters are involved: you need to - pass double* now, instead of int*. - -
- -
- Adapt to GtkStyleContext API changes - - The getters in the GtkStyleContext API, such as - gtk_style_context_get_property(), gtk_style_context_get(), - or gtk_style_context_get_color() have lost their state argument, - and always use the context's current state. Update all callers - to omit the state argument. - - - The most commonly used GtkStyleContext API, gtk_style_context_add_class(), - has been moved to GtkWidget as gtk_widget_add_css_class(), as have the - corresponding gtk_style_context_remove_class() and - gtk_style_context_has_class() APIs. - -
- -
- Adapt to GtkCssProvider API changes - - In GTK 4, the various #GtkCssProvider load functions have lost their - #GError argument. If you want to handle CSS loading errors, use the - #GtkCssProvider::parsing-error signal instead. gtk_css_provider_get_named() - has been replaced by gtk_css_provider_load_named(). - -
- -
- Stop using GtkShadowType and GtkRelief properties - - The shadow-type properties in GtkScrolledWindow, GtkViewport, - and GtkFrame, as well as the relief properties in GtkButton - and its subclasses have been removed. GtkScrolledWindow, GtkButton - and GtkMenuButton have instead gained a boolean has-frame - property. - -
- -
- Adapt to GtkWidget's size request changes - - GTK 3 used five different virtual functions in GtkWidget to - implement size requisition, namely the gtk_widget_get_preferred_width() - family of functions. To simplify widget implementations, GTK 4 uses - only one virtual function, GtkWidgetClass::measure() that widgets - have to implement. gtk_widget_measure() replaces the various - gtk_widget_get_preferred_ functions for querying sizes. - -
- -
- Adapt to GtkWidget's size allocation changes - - The #GtkWidget.size_allocate() vfunc takes the baseline as an argument - now, so you no longer need to call gtk_widget_get_allocated_baseline() - to get it. - - - The ::size-allocate signal has been removed, since it is easy - to misuse. If you need to learn about sizing changes of custom - drawing widgets, use the #GtkDrawingArea::resize or #GtkGLArea::resize - signals. - -
- -
- Switch to GtkWidget's children APIs - - In GTK 4, any widget can have children (and GtkContainer is gone). - There is new API to navigate the widget tree, for use in widget - implementations: gtk_widget_get_first_child(), gtk_widget_get_last_child(), - gtk_widget_get_next_sibling(), gtk_widget_get_prev_sibling(). - -
- -
- Don't use -gtk-gradient in your CSS - - GTK now supports standard CSS syntax for both linear and radial - gradients, just use those. - -
- -
- Don't use -gtk-icon-effect in your CSS - - GTK now supports a more versatile -gtk-icon-filter instead. Replace - -gtk-icon-effect: dim; with -gtk-icon-filter: opacity(0.5); and - -gtk-icon-effect: hilight; with -gtk-icon-filter: brightness(1.2);. - -
- -
- Don't use -gtk-icon-theme in your CSS - - GTK now uses the current icon theme, without a way to change this. - -
- -
- Don't use -gtk-outline-...-radius in your CSS - - These non-standard properties have been removed from GTK - CSS. Just use regular border radius. - -
- -
- Adapt to drawing model changes - - This area has seen the most radical changes in the transition from GTK 3 - to GTK 4. Widgets no longer use a draw() function to render their contents - to a cairo surface. Instead, they have a snapshot() function that creates - one or more GskRenderNodes to represent their content. Third-party widgets - that use a draw() function or a #GtkWidget::draw signal handler for custom - drawing will need to be converted to use gtk_snapshot_append_cairo(). - - - The auxiliary #GtkSnapshot object has APIs to help with creating render - nodes. - - - If you are using a #GtkDrawingArea for custom drawing, you need to switch - to using gtk_drawing_area_set_draw_func() to set a draw function instead - of connnecting a handler to the #GtkWidget::draw signal. - -
- -
- Stop using APIs to query GdkSurfaces - - A number of APIs for querying special-purpose windows have been removed, - since these windows are no longer publically available: - gtk_tree_view_get_bin_window(), gtk_viewport_get_bin_window(), - gtk_viewport_get_view_window(). - -
- -
- Widgets are now visible by default - - The default value of #GtkWidget:visible in GTK 4 is %TRUE, so you no - longer need to explicitly show all your widgets. On the flip side, you - need to hide widgets that are not meant to be visible from the start. - The only widgets that still need to be explicitly shown are toplevel - windows, dialogs and popovers. - - - A convenient way to remove unnecessary property assignments like this - from ui files it run the command gtk4-builder-tool simplify --replace - on them. - - - The function gtk_widget_show_all(), the #GtkWidget:no-show-all property - and its getter and setter have been removed in GTK 4, so you should stop - using them. - -
- -
- Adapt to changes in animated hiding and showing of widgets - - Widgets that appear and disappear with an animation, such as GtkPopover, - GtkInfoBar, GtkRevealer no longer use gtk_widget_show() and gtk_widget_hide() - for this, but have gained dedicated APIs for this purpose that you should - use. - -
- -
- Stop passing commandline arguments to gtk_init - - The gtk_init() and gtk_init_check() functions no longer accept commandline - arguments. Just call them without arguments. Other initialization functions - that were purely related to commandline argument handling, such as - gtk_parse_args() and gtk_get_option_group(), are gone. The APIs to - initialize GDK separately are also gone, but it is very unlikely - that you are affected by that. - -
- -
- GdkPixbuf is deemphasized - - A number of #GdkPixbuf-based APIs have been removed. The available replacements - are either using #GIcon, or the newly introduced #GdkTexture or #GdkPaintable - classes instead. If you are dealing with pixbufs, you can use - gdk_texture_new_for_pixbuf() to convert them to texture objects where needed. - -
- -
- GtkWidget event signals are removed - - Event controllers and #GtkGestures have already been introduced in GTK 3 to handle - input for many cases. In GTK 4, the traditional widget signals for handling input, - such as #GtkWidget::motion-event or #GtkWidget::event have been removed. - -
- -
- Invalidation handling has changed - - Only gtk_widget_queue_draw() is left to mark a widget as needing redraw. - Variations like gtk_widget_queue_draw_rectangle() or gtk_widget_queue_draw_region() - are no longer available. - -
- -
- Stop using GtkWidget::draw - - The #GtkWidget::draw signal has been removed. Widgets need to implement the - #GtkWidgetClass.snapshot() function now. Connecting draw signal handlers is - no longer possible. If you want to keep using cairo for drawing, use - gtk_snaphot_append_cairo(). - -
- -
- Window content observation has changed - - Observing widget contents and widget size is now done by using the - #GtkWidgetPaintable object instead of connecting to widget signals. - -
- -
- Monitor handling has changed - - Instead of a monitor number, #GdkMonitor is now used throughout. - gdk_display_get_monitors() returns the list of monitors that can be queried - or observed for monitors to pass to APIs like gtk_window_fullscreen_on_monitor(). - -
- -
- Adapt to cursor API changes - - Use the new gtk_widget_set_cursor() function to set cursors, instead of - setting the cursor on the underlying window directly. This is necessary - because most widgets don't have their own window anymore, turning any - such calls into global cursor changes. - - - For creating standard cursors, gdk_cursor_new_for_display() has been removed, - you have to use cursor names instead of GdkCursorType. For creating custom cursors, - use gdk_cursor_new_from_texture(). The ability to get cursor images has been removed. - -
- -
- Adapt to icon size API changes - - Instead of the existing extensible set of symbolic icon sizes, GTK now only - supports normal and large icons with the #GtkIconSize enumeration. The actual sizes - can be defined by themes via the CSS property -gtk-icon-size. - - - GtkImage setters like gtk_image_set_from_icon_name() no longer take a #GtkIconSize - argument. You can use the separate gtk_image_set_icon_size() setter if you need - to override the icon size. - - - The :stock-size property of GtkCellRendererPixbuf has been renamed to - #GtkCellRendererPixbuf:icon-size. - -
- -
- Adapt to changes in the GtkAssistant API - - The :has-padding property is gone, and GtkAssistant no longer adds padding - to pages. You can easily do that yourself. - -
- -
- Adapt to changes in the API of GtkEntry, GtkSearchEntry and GtkSpinButton - - The GtkEditable interface has been made more useful, and the core functionality of - GtkEntry has been broken out as a GtkText widget. GtkEntry, GtkSearchEntry, - GtkSpinButton and the new GtkPasswordEntry now use a GtkText widget internally - and implement GtkEditable. In particular, this means that it is no longer - possible to use GtkEntry API such as gtk_entry_grab_focus_without_selecting() - on a search entry. - - - Use GtkEditable API for editable functionality, and widget-specific APIs for - things that go beyond the common interface. For password entries, use - GtkPasswordEntry. As an example, gtk_spin_button_set_max_width_chars() - has been removed in favor of gtk_editable_set_max_width_chars(). - -
- -
- Adapt to changes in GtkOverlay API - - The GtkOverlay :pass-through child property has been replaced by the - #GtkWidget:can-target property. Note that they have the opposite sense: - pass-through == !can-target. - -
- -
- Use GtkFixed instead of GtkLayout - - Since GtkScrolledWindow can deal with widgets that do not implement - the GtkScrollable interface by automatically wrapping them into a - GtkViewport, GtkLayout is redundant, and has been removed in favor - of the existing GtkFixed container widget. - -
- -
- Adapt to search entry changes - - The way search entries are connected to global events has changed; - gtk_search_entry_handle_event() has been dropped and replaced by - gtk_search_entry_set_key_capture_widget() and - gtk_event_controller_key_forward(). - -
- -
- Stop using gtk_window_activate_default() - - The handling of default widgets has been changed, and activating - the default now works by calling gtk_widget_activate_default() - on the widget that caused the activation. If you have a custom widget - that wants to override the default handling, you can provide an - implementation of the default.activate action in your widgets' action - groups. - -
- -
- Stop setting ::has-default and ::has-focus in .ui files - - The special handling for the ::has-default and ::has-focus properties - has been removed. If you want to define the initial focus or the - the default widget in a .ui file, set the ::default-widget or - ::focus-widget properties of the toplevel window. - -
- -
- Stop using the GtkWidget::display-changed signal - - To track the current display, use the #GtkWidget::root property - instead. - -
- -
- GtkPopover::modal has been renamed to autohide - - The modal property has been renamed to autohide. - gtk-builder-tool can assist with the rename in ui files. - -
- -
- gtk_widget_get_surface has been removed - - gtk_widget_get_surface() has been removed. - Use gtk_native_get_surface() in combination with - gtk_widget_get_native() instead. - -
- -
- gtk_widget_is_toplevel has been removed - - gtk_widget_is_toplevel() has been removed. - Use GTK_IS_ROOT, GTK_IS_NATIVE or GTK_IS_WINDOW - instead, as appropriate. - -
- -
- gtk_widget_get_toplevel has been removed - - gtk_widget_get_toplevel() has been removed. - Use gtk_widget_get_root() or gtk_widget_get_native() - instead, as appropriate. - -
- -
- GtkEntryBuffer ::deleted-text has changed - - To allow signal handlers to access the deleted text before it - has been deleted #GtkEntryBuffer::deleted-text has changed from - %G_SIGNAL_RUN_FIRST to %G_SIGNAL_RUN_LAST. The default handler - removes the text from the #GtkEntryBuffer. - - - To adapt existing code, use g_signal_connect_after() or - %G_CONNECT_AFTER when using g_signal_connect_data() or - g_signal_connect_object(). - -
- -
- GtkMenu, GtkMenuBar and GtkMenuItem are gone - - These widgets were heavily relying on X11-centric concepts such as - override-redirect windows and grabs, and were hard to adjust to other - windowing systems. - - - Menus can already be replaced using GtkPopoverMenu in GTK 3. Additionally, - GTK 4 introduces GtkPopoverMenuBar to replace menubars. These new widgets - can only be constructed from menu models, so the porting effort involves - switching to menu models and actions. - - - Tabular menus were rarely used and complicated the menu code, - so they have not been brought over to #GtkPopoverMenu. If you need - complex layout in menu-like popups, consider directly using a - #GtkPopover instead. - - - Since menus are gone, GtkMenuButton also lost its ability to show menus, - and needs to be used with popovers in GTK 4. - -
- -
- GtkToolbar has been removed - - Toolbars were using outdated concepts such as requiring special toolitem - widgets. Toolbars should be replaced by using a GtkBox with regular widgets - instead and the "toolbar" style class. - -
- -
- GtkAspectFrame is no longer a frame - - GtkAspectFrame is no longer derived from GtkFrame and does not - place a label and frame around its child anymore. It still lets - you control the aspect ratio of its child. - -
- -
- Stop using custom tooltip windows - - Tooltips no longer use GtkWindows in GTK 4, and it is no longer - possible to provide a custom window for tooltips. Replacing the content - of the tooltip with a custom widget is still possible, with - gtk_tooltip_set_custom(). - -
- -
- Switch to the new Drag-and-Drop api - - The source-side Drag-and-Drop apis in GTK 4 have been changed to use an event - controller, #GtkDragSource. Instead of calling gtk_drag_source_set() - and connecting to #GtkWidget signals, you create a #GtkDragSource object, - attach it to the widget with gtk_widget_add_controller(), and connect - to #GtkDragSource signals. Instead of calling gtk_drag_begin() on a widget - to start a drag manually, call gdk_drag_begin(). - The ::drag-data-get signal has been replaced by the #GtkDragSource::prepare - signal, which returns a #GdkContentProvider for the drag operation. - - - The destination-side Drag-and-Drop apis in GTK 4 have also been changed - to use an event controller, #GtkDropTarget. Instead of calling - gtk_drag_dest_set() and connecting to #GtkWidget signals, you create - a #GtkDropTarget object, attach it to the widget with - gtk_widget_add_controller(), and connect to #GtkDropTarget signals. - The ::drag-motion signal has been renamed to #GtkDropTarget::accept, and - instead of ::drag-data-received, you need to use async read methods on the - #GdkDrop object, such as gdk_drop_read_async() or gdk_drop_read_value_async(). - -
- -
- Adapt to GtkIconTheme API changes - - gtk_icon_theme_lookup_icon() returns a #GtkIconPaintable object now, instead - of a #GtkIconInfo. It always returns a paintable in the requested size, and - never fails. A number of no-longer-relevant lookup flags and API variants - have been removed. - -
- -
- Update to GtkFileChooser API changes - - GtkFileChooser moved to a GFile-based API. If you need to convert a - path or a URI, use g_file_new_for_path(), g_file_new_for_commandline_arg(), - or g_file_new_for_uri(); similarly, if you need to get a path or a URI - from a GFile, use g_file_get_path(), or g_file_get_uri(). With the - removal or path and URI-based functions, the "local-only" property has - been removed; GFile can be used to access non-local as well as local - resources. - - - The GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER action has been removed. Use - %GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, instead. If a new folder is needed, - the user can create one. - - - The "confirm-overwrite" signal, and the "do-overwrite-confirmation" - property have been removed from GtkFileChooser. The file chooser widgets - will automatically handle the confirmation of overwriting a file when - using GTK_FILE_CHOOSER_ACTION_SAVE. - - - GtkFileChooser does not support a custom extra widget any more. If you - need to add extra widgets, use gtk_file_chooser_add_choice() instead. - - - GtkFileChooser does not support a custom preview widget any more. If - you need to show a custom preview, you can create your own GtkDialog - with a GtkFileChooserWidget and your own preview widget that you - update whenever the #GtkFileChooser::selection-changed signal is - emitted. - -
- -
- Stop using blocking dialog functions - - GtkDialog, GtkNativeDialog, and GtkPrintOperation removed their - blocking API using nested main loops. Nested main loops present - re-entrancy issues and other hard to debug issues when coupled - with other event sources (IPC, accessibility, network operations) - that are not under the toolkit or the application developer's - control. Additionally, "stop-the-world" functions do not fit - the event-driven programming model of GTK. - - - You can replace calls to gtk_dialog_run() - by specifying that the #GtkDialog must be modal using - gtk_window_set_modal() or the %GTK_DIALOG_MODAL flag, and - connecting to the #GtkDialog::response signal. - -
-
-
From 3338d24da49b103a0621d5f39ead5142b71d05bc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 23 May 2020 21:33:25 -0400 Subject: [PATCH 02/18] docs: Be more careful when expanding abbreviations We must not expand #symbol in the middle of a url, where it is probably a fragment identifier. Restrict problem. --- docs/reference/gtk/gtk-markdown-to-docbook | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/reference/gtk/gtk-markdown-to-docbook b/docs/reference/gtk/gtk-markdown-to-docbook index b461a44230..247c613dce 100755 --- a/docs/reference/gtk/gtk-markdown-to-docbook +++ b/docs/reference/gtk/gtk-markdown-to-docbook @@ -37,10 +37,13 @@ def ExpandAbbreviations(symbol, text): text = re.sub(r'\\\%', r'\%', text) # Convert '#symbol', but not '\#symbol'. + + # Only convert #foo after a space to avoid interfering with + # fragment identifiers in urls def f3(m): return m.group(1) + MakeHashXRef(m.group(2), "type") - text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text) + text = re.sub(r'(\A|[ ])#([\w\-:\.]+[\w]+)', f3, text) text = re.sub(r'\\#', '#', text) return text From 9873d983ed0a4b4b374515d8d818d50b0aff0299 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 23 May 2020 21:32:35 -0400 Subject: [PATCH 03/18] docs: Convert css overview to markdown --- docs/reference/gtk/css-overview.md | 79 ++ docs/reference/gtk/css-overview.xml | 1179 -------------------------- docs/reference/gtk/css-properties.md | 238 ++++++ docs/reference/gtk/gtk4-docs.xml | 1 + docs/reference/gtk/meson.build | 5 +- 5 files changed, 321 insertions(+), 1181 deletions(-) create mode 100644 docs/reference/gtk/css-overview.md delete mode 100644 docs/reference/gtk/css-overview.xml create mode 100644 docs/reference/gtk/css-properties.md diff --git a/docs/reference/gtk/css-overview.md b/docs/reference/gtk/css-overview.md new file mode 100644 index 0000000000..9c1fad71eb --- /dev/null +++ b/docs/reference/gtk/css-overview.md @@ -0,0 +1,79 @@ +# CSS in GTK {#css} + +This chapter describes how GTK uses CSS for styling and layout. +It is not meant to be an explanation of CSS from first principles, +but focuses on listing supported CSS features and differences +between Web CSS and GTK. + +There is plenty of introductory documentation available that +can be used to learn about CSS in general. In the tables below +we include links to the official specs that can be used to look +up the definition of individual selectors and properties. + +## CSS nodes + +GTK applies the style information found in style sheets by matching +the selectors against a tree of nodes. Each node in the tree has a +name, a state and possibly style classes. The children of each node +are linearly ordered. + +Every widget has one or more of these CSS nodes, and determines their +name, state, style classes and how they are layed out as children and +siblings in the overall node tree. The documentation for each widget +explains what CSS nodes it has. + +### The CSS nodes of a GtkScale + +``` +scale[.fine-tune] +├── marks.top +│ ├── mark +┊ ┊ +│ ╰── mark +├── trough +│ ├── slider +│ ├── [highlight] +│ ╰── [fill] +╰── marks.bottom + ├── mark + ┊ + ╰── mark +``` + +## Selectors + +Selectors work very similar to the way they do on the web. + +All widgets have one or more CSS nodes with element names and style +classes. When style classes are used in selectors, they have to be prefixed +with a period. Widget names can be used in selectors like IDs. When used +in a selector, widget names must be prefixed with a # character. + +### GTK CSS Selectors + +| Pattern | Reference | Notes | +|:--------|:----------|:------| +| * | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#universal-selector) | | +| E | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#type-selectors) | | +| E.class | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#class-html) | | +| E#id | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#id-selectors) | | +| E:nth-child(n) | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#structural-pseudos) | | +| E:nth-last-child(n) | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#structural-pseudos) | | +| E:first-child | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#structural-pseudos) | | +| E:last-child | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#structural-pseudos) | | +| E:only-child | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#structural-pseudos) | | +| E:link, E:visited | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#link) | Corresponds to GTK_STATE_FLAG_LINK and GTK_STATE_FLAGS_VISITED | +| E:active, E:hover, E:focus | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#useraction-pseudos) | Correspond to GTK_STATE_FLAG_ACTIVE, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAGS_FOCUSED | +| E:focus-within | [CSS Selector Level 4](https://drafts.csswg.org/selectors/#focus-within-pseudo) | Set on all ancestors of the focus widget, unlike CSS | +| E:focus-visible | [CSS Selector Level 4](https://drafts.csswg.org/selectors/#focus-within-pseudo) | Set on focus widget and all ancestors, unlike CSS | +| E:disabled | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#UIstates) | Corresponds to GTK_STATE_FLAG_INSENSITIVE | +| E:disabled | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#UIstates) | Corresponds to GTK_STATE_FLAG_CHECKED | +| E:indeterminate | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#indeterminate) | Corresponds to GTK_STATE_FLAG_INCONSISTENT | +| E:backdrop, E:selected | | Corresponds to GTK_STATE_FLAG_BACKDROP, GTK_STATE_FLAG_SELECTED | +| E:not(selector) | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#negation) | | +| E:dir(ltr), E:dir(rtl) | [CSS Selector Level 4](https://drafts.csswg.org/selectors/#the-dir-pseudo) | | +| E:drop(active) | [CSS Selector Level 4](https://drafts.csswg.org/selectors/#drag-pseudos) | | +| E F | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#descendent-combinators) | | +| E > F | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#child-combinators) | | +| E ~ F | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#general-sibling-combinators) | | +| E + F | [CSS Selector Level 3](https://www.w3.org/TR/css3-selectors/#adjacent-sibling-combinators) | | diff --git a/docs/reference/gtk/css-overview.xml b/docs/reference/gtk/css-overview.xml deleted file mode 100644 index 67f32d6731..0000000000 --- a/docs/reference/gtk/css-overview.xml +++ /dev/null @@ -1,1179 +0,0 @@ - - - - -GTK CSS Overview -3 -GTK Library - - - -GTK CSS Overview - -The language used for style information in GTK - - - - - CSS in GTK - - - This chapter describes how GTK uses CSS for styling and layout. - It is not meant to be an explanation of CSS from first principles, - but focuses on listing supported CSS features and differences - between Web CSS and GTK. - - - - There is plenty of introductory documentation available that - can be used to learn about CSS in general. In the tables below - we include links to the official specs that can be used to look - up the definition of individual selectors and properties. - - - - CSS nodes - - - GTK applies the style information found in style sheets by matching - the selectors against a tree of nodes. Each node in the tree has a - name, a state and possibly style classes. The children of each node - are linearly ordered. - - - - Every widget has one or more of these CSS nodes, and determines their - name, state, style classes and how they are layed out as children and - siblings in the overall node tree. The documentation for each widget - explains what CSS nodes it has. - - - - The CSS nodes of a GtkScale - - - - - - - Selectors - - - Selectors work very similar to the way they do on the web. - - - - All widgets have one or more CSS nodes with element names and style - classes. When style classes are used in selectors, they have to be prefixed - with a period. Widget names can be used in selectors like IDs. When used - in a selector, widget names must be prefixed with a # character. - - - - GTK CSS Selectors - - - PatternReferenceNotes - - - - * - CSS Selectors Level 3 - - - - E - CSS Selectors Level 3 - - - - E.class - CSS Selectors Level 3 - - - - - E#id - CSS Selectors Level 3 - GTK uses the widget name as ID - - - E:nth-child(nth-child) - CSS Selectors Level 3 - - - - E:nth-last-child(nth-child) - CSS Selectors Level 3 - - - - E:first-child - CSS Selectors Level 3 - - - - E:last-child - CSS Selectors Level 3 - - - - E:only-child - CSS Selectors Level 3 - Equivalent to E:first-child:last-child - - - E:link, E:visited - CSS Selectors Level 3 - Corresponds to GTK_STATE_FLAG_LINK and GTK_STATE_FLAGS_VISITED - - - E:active, E:hover, E:focus - CSS Selectors Level 3 - Correspond to GTK_STATE_FLAG_ACTIVE, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAGS_FOCUSED - - - E:focus-within - CSS Selectors Level 4 - Set on all ancestors of the focus widget, unlike CSS - - - E:focus-visible - CSS Selectors Level 4 - Set on focus widget and all ancestors, unlike CSS - - - E:disabled - CSS Selectors Level 3 - Corresponds to GTK_STATE_FLAG_INSENSITIVE - - - E:checked - CSS Selectors Level 3 - Corresponds to GTK_STATE_FLAG_CHECKED - - - E:indeterminate - CSS3 Selectors Level 3 - Corresponds to GTK_STATE_FLAG_INCONSISTENT - - - E:backdrop, E:selected - - Corresponds to GTK_STATE_FLAG_BACKDROP, GTK_STATE_FLAG_SELECTED - - - E:not(selector) - CSS Selectors Level 3 - - - - E:dir(ltr), E:dir(rtl) - CSS Selectors Level 4 - - - - E:drop(active) - CSS Selectors Level 4 - - - - E F - CSS Selectors Level 3 - - - - E > F - CSS Selectors Level 3 - - - - E ~ F - CSS Selectors Level 3 - - - - E + F - CSS Selectors Level 3 - - - - -
- -
- -
- - - GTK CSS Properties - - - GTK supports CSS properties and shorthands as far as they can be applied - in the context of widgets, and adds its own properties only when needed. - All GTK-specific properties have a -gtk prefix. - - - - Basic types - - - All properties support the following keywords: inherit, initial, unset, with - the same meaning as defined in the CSS Cascading and Inheritance spec. - - - - The following units are supported for basic datatypes: - - - - - Length px, pt, em, ex, rem, pc, in, cm, mm, calc() - - - Percentage %, calc() - - - Angle deg | grad | turn, calc() - - - Time s | ms, calc() - - - - - Length values with the em or ex units are resolved using the font - size value, unless they occur in setting the font-size itself, in - which case they are resolved using the inherited font size value. - - - - The rem unit is resolved using the initial font size value, which is - not quite the same as the CSS definition of rem. - - - - The calc() notation adds considerable expressive power. There are limits - on what types can be combined in such an expression (e.g. it does not make - sense to add a number and a time). For the full details, see the - CSS3 Values and - Units spec. - - - - A common pattern among shorthand properties (called 'four sides') is one - where one to four values can be specified, to determine a value for each - side of an area. In this case, the specified values are interpreted as - follows: - - - 4 values: top right bottom left - - - 3 values: top horizontal bottom - - - 2 values: vertical horizontal - - - 1 value: all - - - - - - - - Colors - - - GTK extends the CSS syntax with several additional ways to specify colors. - - - The first is a reference to a color defined via a @define-color rule in CSS. - The syntax for @define-color rules is as follows: - - @define-color Name Color - - - - To refer to the color defined by a @define-color rule, prefix the name with @. - - - GTK also supports color expressions, which allow colors to be transformed to - new ones. Color expressions can be nested, providing a rich language to - define colors. Color expressions resemble functions, taking 1 or more colors - and in some cases a number as arguments. - - lighter(Color) produces a brigher variant of Color - darker(Color) produces a darker variant of Color - shade(Color, Number) changes the lightness of Color. The number ranges from 0 for black to 2 for white. - alpha(Color, Number) replaces the alpha value of color with number (between 0 and 1) - mix(Color1, Color2, Number) interpolates between the two colors - - - - - - Images - - GTK extends the CSS syntax for images and also uses it for specifying icons. - To load a themed icon, use - - -gtk-icontheme(Name) - - The specified icon name is used to look up a themed icon, while taking into - account the values of the -gtk-icon-palette property. - This kind of image is mainly used as value of the -gtk-icon-source property. - - - Symbolic icons from the icon theme are recolored according to the - -gtk-icon-palette property, which defines a list of named colors. - The recognized names for colors in symbolic icons are error, warning - and success. The default palette maps these three names to symbolic - colors with the names @error_color, @warning_color and @success_color - respectively. The syntax for defining a custom palette is a comma-separated - list of name-color pairs, e.g. - - success blue, warning #fc3, error magenta - - - - Recoloring is sometimes needed for images that are not part of an icon theme, - and the - - -gtk-recolor(uri, palette) - - syntax makes this available. -gtk-recolor requires a url as first argument. - The remaining arguments specify the color palette to use. If the palette is - not explicitly specified, the current value of the -gtk-icon-palette property - is used. - - - GTK supports scaled rendering on hi-resolution displays. This works best if - images can specify normal and hi-resolution variants. From CSS, this can be - done with - - -gtk-scaled(Image1, Image2) - - - - - - - GTK CSS Properties - - - - - - - Color and Filter Properties - NameReferenceNotes - - - - color - - CSS Color Level 3 - - - - - opacity - - CSS Color Level 3 - - - - - filter - - CSS Filter Effects Level 1 - - - - Font Properties - - font-family - - CSS Fonts Level 3 - - defaults to gtk-font-name setting - - - font-size - - CSS Fonts Level 3 - - defaults to gtk-font-name setting - - - font-style - - CSS Fonts Level 3 - - - - - font-variant - - CSS Fonts Level 3 - - only CSS2 values supported - - - font-weight - - CSS Fonts Level 3 - - - - - font-stretch - - CSS Fonts Level 3 - - - - - font-kerning - - CSS Fonts Level 3 - - - - - font-variant-ligatures - - CSS Fonts Level 3 - - - - - font-variant-position - - CSS Fonts Level 3 - - - - - font-variant-caps - - CSS Fonts Level 3 - - - - - font-variant-numeric - - CSS Fonts Level 3 - - - - - font-variant-alternates - - CSS Fonts Level 3 - - - - - font-variant-east-asian - - CSS Fonts Level 3 - - - - - font-feature-settings - - CSS Fonts Level 3 - - - - - font-variation-settings - - CSS Fonts Level 4 - - - - - -gtk-dpi - Number - defaults to screen resolution - - - Font Shorthands - - font - - CSS Fonts Level 3 - - CSS allows line-height, etc - - - font-variant - - CSS Fonts Level 3 - - - - Text Caret Properties - - caret-color - - CSS Basic User Interface Level 3 - - CSS allows an auto value - - - -gtk-secondary-caret-color - Color - used for the secondary caret in bidirectional text - - Text Decoration Properties - - letter-spacing - - CSS Text Level 3 - - - - - text-decoration-line - - CSS Text Decoration Level 3 - - CSS allows overline - - - text-decoration-color - - CSS Text Decoration Level 3 - - - - - text-decoration-style - - CSS Text Decoration Level 3 - - CSS allows dashed and dotted - - - text-shadow - - CSS Text Decoration Level 3 - - - - Text Decoration Shorthands - - text-decoration - - CSS Text Decoration Level 3 - - - - Icon Properties - - -gtk-icon-source - Image, builtin or none - used for builtin icons in buttons and expanders - - - -gtk-icon-size - Length - size used for builtin icons in buttons and expanders - - - -gtk-icon-style - requested, regular or symbolic - preferred style for application-loaded icons - - - -gtk-icon-transform - - Transform list or none - - applied to builtin and application-loaded icons - - - -gtk-icon-palette - Color palette, as explained above - used to recolor symbolic icons - - - -gtk-icon-shadow - - Shadow or none - - applied to builtin and application-loaded icons - - - -gtk-icon-filter - - Filter value list or none - - applied to builtin and application-loaded icons - - - Box Properties - - transform - - CSS Transforms Level 2 - - - - - min-width - - CSS Box Model Level 3 - - CSS allows percentages - - - min-height - - CSS Box Model Level 3 - - CSS allows percentages - - - margin-top - - CSS Box Model Level 3 - - CSS allows percentages or auto - - - margin-right - - CSS Box Model Level 3 - - CSS allows percentages or auto - - - margin-bottom - - CSS Box Model Level 3 - - CSS allows percentages or auto - - - margin-left - - CSS Box Model Level 3 - - CSS allows percentages or auto - - - padding-top - - CSS Box Model Level 3 - - CSS allows percentages - - - padding-right - - CSS Box Model Level 3 - - CSS allows percentages - - - padding-bottom - - CSS Box Model Level 3 - - CSS allows percentages - - - padding-left - - CSS Box Model Level 3 - - CSS allows percentages - - - Box Shorthands - - margin - - CSS Box Model Level 3 - - a 'four sides' property - - - padding - - CSS Box Model Level 3 - - a 'four sides' property - - - Border Properties - - border-top-width - - CSS Backgrounds and Borders Level 3 - CSS allows other values - - - border-right-width - - CSS Backgrounds and Borders Level 3 - - CSS allows other values - - - border-bottom-width - - CSS Backgrounds and Borders Level 3 - - CSS allows other values - - - border-left-width - - CSS Backgrounds and Borders Level 3 - - CSS allows other values - - - border-top-style - - CSS Backgrounds and Borders Level 3 - - - - - border-right-style - - CSS Backgrounds and Borders Level 3 - - - - - border-bottom-style - - CSS Backgrounds and Borders Level 3 - - - - - border-left-style - - CSS Backgrounds and Borders Level 3 - - - - - border-top-right-radius - - CSS Backgrounds and Borders Level 3 - - - - - border-bottom-right-radius - - CSS Backgrounds and Borders Level 3 - - - - - border-bottom-left-radius - - CSS Backgrounds and Borders Level 3 - - - - - border-top-left-radius - - CSS Backgrounds and Borders Level 3 - - - - - border-top-color - - CSS Backgrounds and Borders Level 3 - - - - - border-right-color - - CSS Backgrounds and Borders Level 3 - - - - - border-bottom-color - - CSS Backgrounds and Borders Level 3 - - - - - border-left-color - - CSS Backgrounds and Borders Level 3 - - - - - border-image-source - - CSS Backgrounds and Borders Level 3 - - - - - border-image-repeat - - CSS Backgrounds and Borders Level 3 - - - - - border-image-slice - - CSS Backgrounds and Borders Level 3 - - a 'four sides' property - - - border-image-width - - CSS Backgrounds and Borders Level 3 - - a 'four sides' property - - - Border Shorthands - - border-width - - CSS Backgrounds and Borders Level 3 - - a 'four sides' property - - - border-style - - CSS Backgrounds and Borders Level 3 - - a 'four sides' property - - - border-color - - CSS Backgrounds and Borders Level 3 - - a 'four sides' property - - - border-top - - CSS Backgrounds and Borders Level 3 - - - - - border-right - - CSS Backgrounds and Borders Level 3 - - - - - border-bottom - - CSS Backgrounds and Borders Level 3 - - - - - border-left - - CSS Backgrounds and Borders Level 3 - - - - - border - - CSS Backgrounds and Borders Level 3 - - - - - border-radius - - CSS Backgrounds and Borders Level 3 - - - - - border-image - - CSS Backgrounds and Borders Level 3 - - - - - Outline Properties - - outline-style - - CSS Basic User Interface Level 3 - - initial value is none, auto is not supported - - - outline-width - - CSS Basic User Interface Level 3 - - - - - outline-color - - CSS Basic User Interface Level 3 - - initial value is currentColor, invert is not supported - - - outline-offset - - CSS Basic User Interface Level 3 - - - - - Outline Shorthands - - outline - - CSS Basic User Interface Level 3 - - - - - Background Properties - - background-color - - CSS Backgrounds and Borders Level 3 - - - - - background-clip - - CSS Backgrounds and Borders Level 3 - - - - - background-origin - - CSS Backgrounds and Borders Level 3 - - - - - background-size - - CSS Backgrounds and Borders Level 3 - - - - - background-position - - CSS Backgrounds and Borders Level 3 - - - - - background-repeat - - CSS Backgrounds and Borders Level 3 - - - - - background-image - - CSS Backgrounds and Borders Level 3 - - not supported: urls without quotes, colors in crossfades - - - box-shadow - - CSS Backgrounds and Borders Level 3 - - - - - background-blend-mode - - CSS Compositing and Blending Level 1 - - only affects multiple backgrounds - - - Background Shorthands - - background - - CSS Backgrounds and Borders Level 3 - - - - - Transition Properties - - transition-property - - CSS Transitions - - - - - transition-duration - - CSS Transitions - - - - - transition-timing-function - - CSS Transitions - - - - - transition-delay - - CSS Transitions - - - - - Transition Shorthands - - transition - - CSS Transitions - - - - - Animation Properties - - animation-name - - CSS Animations Level 1 - - - - - animation-duration - - CSS Animations Level 1 - - - - - animation-timing-function - - CSS Animations Level 1 - - - - - animation-iteration-count - - CSS Animations Level 1 - - - - - animation-direction - - CSS Animations Level 1 - - - - - animation-play-state - - CSS Animations Level 1 - - - - - animation-delay - - CSS Animations Level 1 - - - - - animation-fill-mode - - CSS Animations Level 1 - - - - - Animation Shorthands - - animation - - CSS Animations Level 1 - - - - - Table-related Properties - - border-spacing - - CSS Table Level 3 - - respected by GtkBox and GtkGrid - - - -
- -
-
diff --git a/docs/reference/gtk/css-properties.md b/docs/reference/gtk/css-properties.md new file mode 100644 index 0000000000..7e6fcdedaa --- /dev/null +++ b/docs/reference/gtk/css-properties.md @@ -0,0 +1,238 @@ +# GTK CSS Properties + +GTK supports CSS properties and shorthands as far as they can be applied +in the context of widgets, and adds its own properties only when needed. +All GTK-specific properties have a -gtk prefix. + +## Basic types + +All properties support the following keywords: inherit, initial, unset, +with the same meaning as defined in the +[CSS Cascading and Inheritance](https://www.w3.org/TR/css3-cascade/#defaulting-keywords) +spec. + +The following units are supported for basic datatypes: + +Length + : px, pt, em, ex, rem, pc, in, cm, mm, calc() +Percentage + : %, calc() +Angle + : deg | grad | turn, calc() +Time + : s | ms, calc() + +Length values with the em or ex units are resolved using the font +size value, unless they occur in setting the font-size itself, in +which case they are resolved using the inherited font size value. + +The rem unit is resolved using the initial font size value, which is +not quite the same as the CSS definition of rem. + +The calc() notation adds considerable expressive power. There are limits +on what types can be combined in such an expression (e.g. it does not make +sense to add a number and a time). For the full details, see the +[CSS3 VAlues and Units](https://www.w3.org/TR/css3-values/#calc-notation) +spec. + +A common pattern among shorthand properties (called 'four sides') is one +where one to four values can be specified, to determine a value for each +side of an area. In this case, the specified values are interpreted as +follows: + +4 values: + : top right bottom left +3 values: + : top horizontal bottom +2 values: + : vertical horizontal +1 value: + : all + +## Colors + +GTK extends the CSS syntax with several additional ways to specify colors. + +The first is a reference to a color defined via a @define-color rule in CSS. +The syntax for @define-color rules is as follows: + +``` +@define-color Name Color +``` + +To refer to the color defined by a @define-color rule, prefix the name with @. + +GTK also supports color expressions, which allow colors to be transformed to +new ones. Color expressions can be nested, providing a rich language to +define colors. Color expressions resemble functions, taking 1 or more colors +and in some cases a number as arguments. + +`lighter(Color)` + : produces a brigher variant of Color +`darker(Color)` + : produces a darker variant of Color +`shade(Color, Number)` + : changes the lightness of Color. The number ranges from 0 for black to 2 for white. +`alpha(Color, Number)` + : replaces the alpha value of color with number (between 0 and 1) +`mix(Color1, Color2, Number)` + : interpolates between the two colors + +## Images + +GTK extends the CSS syntax for images and also uses it for specifying icons. +To load a themed icon, use + +``` +-gtk-icontheme(Name) +``` + +The specified icon name is used to look up a themed icon, while taking into +account the values of the -gtk-icon-palette property. This kind of image is +mainly used as value of the -gtk-icon-source property. + +Symbolic icons from the icon theme are recolored according to the +-gtk-icon-palette property, which defines a list of named colors. +The recognized names for colors in symbolic icons are error, warning +and success. The default palette maps these three names to symbolic +colors with the names @error_color, @warning_color and @success_color +respectively. The syntax for defining a custom palette is a comma-separated +list of name-color pairs, e.g. + +``` +success blue, warning #fc3, error magenta +``` + +Recoloring is sometimes needed for images that are not part of an icon theme, +and the + +``` +-gtk-recolor(uri, palette) +``` + +syntax makes this available. -gtk-recolor requires a url as first argument. +The remaining arguments specify the color palette to use. If the palette is +not explicitly specified, the current value of the -gtk-icon-palette property +is used. + +GTK supports scaled rendering on hi-resolution displays. This works best if +images can specify normal and hi-resolution variants. From CSS, this can be +done with + +``` +-gtk-scaled(Image1, Image2) +``` + +## GTK CSS Properties + +| Property | Reference | Notes | +|:-----------|:----------|:------| +|color | [CSS Color Level 3](https://www.w3.org/TR/css3-color/#foreground) | | +|opacity | [CSS Color Level 3](https://www.w3.org/TR/css3-color/#opacity) | | +|filter | [CSS Filter Effect Level 1](https://drafts.fxtf.org/filters/#FilterProperty) | | +|font-family | [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-family-prop) | defaults to gtk-font-name setting | +|font-size | [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-size-prop) | defaults to gtk-font-name setting | +|font-style | [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-style-prop) | | +|font-variant| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#descdef-font-variant) | only CSS2 values supported | +|font-weight | [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-weight-prop) | | +|font-stretch| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-stretch-prop) | | +|font-kerning| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-kerning-prop) | | +|font-variant-ligatures| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-ligatures-prop) | | +|font-variant-position| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-position-prop) | | +|font-variant-caps| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-position-prop) | | +|font-variant-numeric| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-numeric-prop) | | +|font-variant-alternates| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-alternates-prop) | | +|font-variant-east-asian| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-east-asian-prop) | | +|font-feature-settings| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-feature-settings-prop) | | +|font-variation-settings| [CSS Fonts Level 4](https://www.w3.org/TR/css-fonts-4/#font-variation-settings-def) | | +|-gtk-dpi|[Number](https://www.w3.org/TR/css3-values/#number-value) | defaults to screen resolution | +|font| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-prop) | CSS allows line-height, etc | +|font-variant| [CSS Fonts Level 3](https://www.w3.org/TR/css3-fonts/#font-variant-prop) | | +|caret-color|[CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#caret-color) | CSS allows an auto value | +|-gtk-secondary-caret-color|[Color](https://www.w3.org/TR/css-color-3/#valuea-def-color) | used for the secondary caret in bidirectional text | +|letter-spacing| [CSS Text Level 3](https://www.w3.org/TR/css3-text/#letter-spacing) | | +|text-decoration-line| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-line-property) | CSS allows overline | +|text-decoration-color| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-color-property) | | +|text-decoration-style| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-style-property) | CSS allows dashed and dotted | +|text-shadow| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-shadow-property) | | +|text-decoration| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-property) | | +|-gtk-icon-source| [Image](https://www.w3.org/TR/css-backgrounds-3/#typedef-image), `builtin` or `none` | used for builtin icons in buttons and expanders | +|-gtk-icon-size| [Length](https://www.w3.org/TR/css3-values/#length-value) | size used for builtin icons in buttons and expanders | +|-gtk-icon-style| `requested`, `regular` or `symbolic` | preferred style for application-loaded icons | +|-gtk-icon-transform| [Transform list](https://drafts.csswg.org/css-transforms-1/#typedef-transform-list) or `none` | applied to builtin and application-loaded icons | +|-gtk-icon-palette| Color palette, as explained above | used to recolor symbolic icons | +|-gtk-icon-shadow| [Shadow](https://www.w3.org/TR/css-backgrounds-3/#typedef-shadow) or `none` | applied to builtin and application-loaded icons | +|-gtk-icon-filter| [Filter value list](https://www.w3.org/TR/filter-effects-1/#typedef-filter-value-list) or `none` | applied to builtin and application-loaded icons | +|transform| [CSS Transforms Level 2](https://drafts.csswg.org/css-transforms-2/) | | +|min-width| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#min-width) | CSS allows percentages | +|min-height| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#min-height) | CSS allows percentages | +|margin-top| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#margin-top) | CSS allows percentages or auto | +|margin-right| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#margin-right) | CSS allows percentages or auto | +|margin-bottom| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#margin-bottom) | CSS allows percentages or auto | +|margin-left| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#margin-left) | CSS allows percentages or auto | +|padding-top| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#padding-top) | CSS allows percentages | +|padding-right| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#padding-right) | CSS allows percentages | +|padding-bottom| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#padding-bottom) | CSS allows percentages | +|padding-left| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#padding-left) | CSS allows percentages | +|margin| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#margin) | a 'four sides' property | +|padding| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#padding) | a 'four sides' property | +|border-top-width| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-width) | CSS allows other values | +|border-right-width| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-width) | CSS allows other values | +|border-bottom-width| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-width) | CSS allows other values | +|border-left-width| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-width) | CSS allows other values | +|border-top-style| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-style) | | +|border-right-style| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-style) | | +|border-bottom-style| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-style) | | +|border-left-style| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-style) | | +|border-top-right-radius| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-radius) | | +|border-bottom-right-radius| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-radius) | | +|border-bottom-left-radius| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-radius) | | +|border-top-left-radius| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-radius) | | +|border-top-color| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-color) | | +|border-right-color| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-color) | | +|border-bottom-color| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-color) | | +|border-left-color| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-color) | | +|border-image-source| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-image-source) | | +|border-image-repeat| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-image-repeat) | | +|border-image-slice| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-image-slice) | a 'four sides' property | +|border-image-width| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-image-width) | a 'four sides' property | +|border-width| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-width) | a 'four sides' property | +|border-style| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#the-border-style) | a 'four sides' property | +|border-color| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-color) | a 'four sides' property | +|border-top| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-top) | | +|border-right| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-right) | | +|border-bottom| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-bottom) | | +|border-left| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-left) | | +|border| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border) | | +|border-radius| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-radius) | | +|border-image| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#border-image) | | +|outline-style| [CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#outline-style) | initial value is none, auto is not supported | +|outline-width| [CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#outline-width) | | +|outline-color| [CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#outline-color) | initial value is currentColor, invert is not supported | +|outline-offset| [CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#outline-offset) | | +|outline| [CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#propdef-outline) | | +|background-color| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-color) | | +|background-clip| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-clip) | | +|background-origin| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-origin) | | +|background-size| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-size) | | +|background-position| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-position) | | +|background-repeat| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-repeat) | | +|background-image| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-image) | not supported: urls without quotes, colors in crossfades | +|box-shadow| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#box-shadow) | | +|background-blend-mode| [CSS Compositing and Blending Level 1](https://www.w3.org/TR/compositing-1/#propdef-background-blend-mode) | only affects multiple backgrounds | +|background| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background) | | +|transition-property| [CSS Transitions](https://www.w3.org/TR/css3-transitions/#transition-property) | | +|transition-duration| [CSS Transitions](https://www.w3.org/TR/css3-transitions/#transition-duration) | | +|transition-timing-function| [CSS Transitions](https://www.w3.org/TR/css3-transitions/#transition-timing-function) | | +|transition-delay| [CSS Transitions](https://www.w3.org/TR/css3-transitions/#transition-delay) | | +|transition| [CSS Transitions](https://www.w3.org/TR/css3-transitions/#transition) | | +|animation-name| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-name) | | +|animation-duration| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-duration) | | +|animation-timing-function| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-timing-function) | | +|animation-iteration-count| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-iteration-count) | | +|animation-direction| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-direction) | | +|animation-play-state| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-play-state) | | +|animation-delay| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-delay) | | +|animation-fill-mode| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-fill-mode) | | +|animation| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation) | | +|border-spacing| [CSS Table Level 3](https://www.w3.org/TR/css-tables-3/#border-spacing-property) | respected by GtkBox and GtkGrid | diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 6905a4ea42..a0ec153bbd 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -378,6 +378,7 @@ Theming in GTK + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index b8ef12a6bd..b30bbb91b7 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -343,7 +343,6 @@ content_files = [ 'broadway.xml', 'building.xml', 'compiling.xml', - 'css-overview.xml', 'glossary.xml', 'gtk4-broadwayd.xml', 'gtk4-builder-tool.xml', @@ -382,7 +381,9 @@ expand_content_md_files = [ 'migrating-3to4.md', 'actions.md', 'input-handling.md', - 'drawing-model.md' + 'drawing-model.md', + 'css-overview.md', + 'css-properties.md' ] types_conf = configuration_data() From 3bf90b42187ae864c7f67620396c5bd85fba03eb Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 23 May 2020 21:41:15 -0400 Subject: [PATCH 04/18] doc: Check for pandoc Add an explicit check for pandoc, so we get a clear error message if it is missing. --- docs/reference/gtk/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index b30bbb91b7..13b4072023 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -403,6 +403,8 @@ if get_option('gtk_doc') configure_file(input: 'version.xml.in', output: 'version.xml', configuration: version_conf) configure_file(input: 'getting_started.xml.in', output: 'getting_started.xml', configuration: src_dir_conf) + # gtk-markdown-to-docbook uses pandoc + pandoc = find_program('pandoc', required: true) expand_md = find_program('gtk-markdown-to-docbook') expand_md_targets = [] foreach t : expand_content_md_files From b99511ee5524f9009a2dfafe67a4df4dfa31f045 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 00:04:36 -0400 Subject: [PATCH 05/18] docs: Convert building, compiling, running to markdown --- docs/reference/gtk/building.md | 270 ++++++++ docs/reference/gtk/building.xml | 518 --------------- .../gtk/{compiling.xml => compiling.md} | 81 +-- docs/reference/gtk/gtk4-docs.xml | 2 +- docs/reference/gtk/meson.build | 7 +- docs/reference/gtk/running.md | 350 ++++++++++ docs/reference/gtk/running.xml | 601 ------------------ 7 files changed, 645 insertions(+), 1184 deletions(-) create mode 100644 docs/reference/gtk/building.md delete mode 100644 docs/reference/gtk/building.xml rename docs/reference/gtk/{compiling.xml => compiling.md} (61%) create mode 100644 docs/reference/gtk/running.md delete mode 100644 docs/reference/gtk/running.xml diff --git a/docs/reference/gtk/building.md b/docs/reference/gtk/building.md new file mode 100644 index 0000000000..6aa2891b24 --- /dev/null +++ b/docs/reference/gtk/building.md @@ -0,0 +1,270 @@ +# Compiling the GTK Libraries {#gtk-building} + +## Building GTK + +Before we get into the details of how to compile GTK, we should +mention that in many cases, binary packages of GTK prebuilt for +your operating system will be available, either from your +operating system vendor or from independent sources. If such a +set of packages is available, installing it will get you +programming with GTK much faster than building it yourself. In +fact, you may well already have GTK installed on your system already. + +In order to build GTK, you will need *meson* installed on your +system. On Linux, and other UNIX-like operating systems, you will +also need *ninja*. This guide does not cover how to install these +two requirements, but you can refer to the +[Meson website](http://mesonbuild.com) for more information. The +[Ninja](https://ninja-build.org) build tool is also usable on +various operating systems, so we will refer to it in the examples. + +If you are building GTK from a source distribution or from a Git +clone, you will need to use *meson* to configure the project. The +most commonly useful argument is the `--prefix` one, which determines +where the files will go once installed. To install GTK under a prefix +like `/opt/gtk` you would run Meson as: + +``` +meson setup --prefix /opt/gtk builddir +``` + +Meson will create the `builddir` directory and place all the build +artefacts there. + +You can get a list of all available options for the build by +running `meson configure`. + +After Meson successfully configured the build directory, you then +can run the build, using Ninja: + +``` +cd builddir +ninja +ninja install +``` + +If you don't have permission to write to the directory you are +installing in, you may have to change to root temporarily before +running `ninja install`. + +Several environment variables are useful to pass to set before +running *meson*. `CPPFLAGS` contains options to pass to the C +compiler, and is used to tell the compiler where to look for +include files. The `LDFLAGS` variable is used in a similar fashion +for the linker. Finally the `PKG_CONFIG_PATH` environment variable +contains a search path that `pkg-config` (see below) uses when +looking for files describing how to compile programs using different +libraries. If you were installing GTK and it's dependencies into +`/opt/gtk`, you might want to set these variables as: + +``` +CPPFLAGS="-I/opt/gtk/include" +LDFLAGS="-L/opt/gtk/lib" +PKG_CONFIG_PATH="/opt/gtk/lib/pkgconfig" +export CPPFLAGS LDFLAGS PKG_CONFIG_PATH +``` + +You may also need to set the `LD_LIBRARY_PATH` environment variable +so the systems dynamic linker can find the newly installed libraries, +and the `PATH` environment program so that utility binaries installed +by the various libraries will be found. + +``` +LD_LIBRARY_PATH="/opt/gtk/lib" +PATH="/opt/gtk/bin:$PATH" +export LD_LIBRARY_PATH PATH +``` + +## Build types {#build-types} + +Meson has different build types, exposed by the `buildtype` +configuration option. GTK enables and disables functionality +depending on the build type used when calling *meson* to +configure the build. + +### Debug builds + +GTK will enable debugging code paths in both the `debug` and +`debugoptimized` build types. Builds with `buildtype` set to +`debug` will additionally enable consistency checks on the +internal state of the toolkit. + +It is recommended to use the `debug` or `debugoptimized` build +types when developing GTK itself. Additionally, `debug` builds of +GTK are recommended for profiling and debugging GTK applications, +as they include additional validation of the internal state. + +The `debugoptimized` build type is the default for GTK if no build +type is specified when calling *meson*. + +### Release builds + +The `release` build type will disable debugging code paths and +additional run time safeties, like checked casts for object +instances. + +The `plain` build type provided by Meson should only be used when +packaging GTK, and it's expected that packagers will provide their +own compiler flags when building GTK. See the previous section for +the list of environment variables to be used to define compiler and +linker flags. + +## Dependencies {#dependencies} + +Before you can compile the GTK widget toolkit, you need to have +various other tools and libraries installed on your +system. Dependencies of GTK have their own build systems, so +you will need to refer to their own installation instructions. + +A particular important tool used by GTK to find its dependencies +is `pkg-config`. + +[pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) +is a tool for tracking the compilation flags needed for libraries +that are used by the GTK libraries. (For each library, a small `.pc` +text file is installed in a standard location that contains the +compilation flags needed for that library along with version number +information.) + +Some of the libraries that GTK depends on are maintained by the +GTK team: GLib, GdkPixbuf, Pango, ATK and GObject Introspection. +Other libraries are maintained separately. + +- The GLib library provides core non-graphical functionality + such as high level data types, Unicode manipulation, and + an object and type system to C programs. It is available + from [here](https://download.gnome.org/sources/glib/). +- The [GdkPixbuf](https://git.gnome.org/browse/gdk-pixbuf/) + library provides facilities for loading images in a variety of + file formats. It is available [here](ttps://download.gnome.org/sources/gdk-pixbuf/). +- [Pango](http://www.pango.org) is a library for internationalized + text handling. It is available [here](https://download.gnome.org/sources/pango/). +- ATK is the Accessibility Toolkit. It provides a set of generic + interfaces allowing accessibility technologies such as + screen readers to interact with a graphical user interface. + It is available [here](https://download.gnome.org/sources/atk/). +- [GObject Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection) + is a framework for making introspection data available to language + bindings. It is available [here](https://download.gnome.org/sources/gobject-introspection/). +- The [GNU libiconv](https://www.gnu.org/software/libiconv/) library + is needed to build GLib if your system doesn't have the iconv() + function for doing conversion between character encodings. Most + modern systems should have iconv(). +- The libintl library from the [GNU gettext](https://www.gnu.org/software/gettext/) + package is needed if your system doesn't have the gettext() + functionality for handling message translation databases. +- The libraries from the X window system are needed to build + Pango and GTK. You should already have these installed on + your system, but it's possible that you'll need to install + the development environment for these libraries that your + operating system vendor provides. +- The [fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/) + library provides Pango with a standard way of locating + fonts and matching them against font names. +- [Cairo](https://www.cairographics.org) is a graphics library that + supports vector graphics and image compositing. Both Pango and GTK + use Cairo for drawing. +- [libepoxy](https://github.com/anholt/libepoxy) is a library that + abstracts the differences between different OpenGL libraries. GTK + uses it for cross-platform GL support and for its own drawing. +- [Graphene](http://ebassi.github.io/graphene/) is a library that + provides vector and matrix types for 2D and 3D transformations. + GTK uses it internally for drawing. +- The [Wayland](https://wayland.freedesktop.org) libraries are needed + to build GTK with the Wayland backend. +- The [shared-mime-info](https://www.freedesktop.org/wiki/Software/shared-mime-info) + package is not a hard dependency of GTK, but it contains definitions + for mime types that are used by GIO and, indirectly, by GTK. + gdk-pixbuf will use GIO for mime type detection if possible. + For this to work, shared-mime-info needs to be installed and + `XDG_DATA_DIRS` set accordingly at configure time. Otherwise, + gdk-pixbuf falls back to its built-in mime type detection. + +## Building and testing GTK {#building} + +First make sure that you have the necessary external +dependencies installed: `pkg-config`, Meson, Ninja, +the JPEG, PNG, and TIFF libraries, FreeType, and, if necessary, +libiconv and libintl. To get detailed information about building +these packages, see the documentation provided with the +individual packages. On any average Linux system, it's quite likely +you'll have all of these installed already, or they will be easily +accessible through your operating system package repositories. + +Then build and install the GTK libraries in the order: +GLib, Cairo, Pango, ATK, then GTK. For each library, follow the +instructions they provide, and make sure to share common settings +between them and the GTK build; if you are using a separate prefix +for GTK, for instance, you will need to use the same prefix for +all its dependencies you build. If you're lucky, this will all go +smoothly, and you'll be ready to [start compiling your own GTK +applications](#gtk-compiling). You can test your GTK installation +by running the `gtk4-demo` program that GTK installs. + +If one of the projects you're configuring or building fails, look +closely at the error messages printed; these will often provide useful +information as to what went wrong. Every build system has its own +log that can help you understand the issue you're encountering. If +all else fails, you can ask for help on the +[GTK forums](#gtk-resources). + +## Extra Configuration Options {#extra-configuration-options} + +In addition to the normal options provided by Meson, +GTK defines various arguments that modify what should +be built. All of these options are passed to `meson` +as `-Doption=value`. Most of the time, the value can +be `true` or `false`. To see a summary of all supported +options and their allowed values, run +``` +meson configure builddir +``` + +### `xinerama` + +By default GTK will try to link against the Xinerama libraries +if they are found. This option can be used to explicitly control +whether Xinerama should be used. + +### `gtk_doc` and `man-pages` + +The *gtk-doc* package is used to generate the reference documentation +included with GTK. By default support for *gtk-doc* is disabled +because it requires various extra dependencies to be installed. +If you have *gtk-doc* installed and are modifying GTK, you may want +to enable *gtk-doc* support by passing in `-Dgtk_doc=true`. + +Additionally, some tools provided by GTK have their own +manual pages generated using a similar set of dependencies; +if you have *xsltproc* then you can generate manual pages by +passing `-Dman-pages=true` when configuring the build. + +### `print-backends` + +By default, GTK will try to build various print backends +if their dependencies are found. This option can be used +to explicitly control which print backends should be built. + +### `x11-backend`, `win32-backend`, `broadway-backend`, `wayland-backend` and `quartz-backend` + +Enable specific backends for GDK. If none of these options +are given, the Wayland backend will be enabled by default, +if the platform is Linux; the X11 backend will also be enabled +by default, unless the platform is Windows, in which case the +default is win32, or the platform is macOS, in which case the +default is quartz. If any backend is explicitly enabled or disabled, +no other platform will be enabled automatically. + +### `introspection` + +Allows to disable building introspection support. This is option +is mainly useful for shortening turnaround times on developer +systems. Installed builds of GTK should always have introspection +support. + +### `build-tests`, `install-tests`, `demos` + +By default, GTK will build quite a few tests and demos. +While these are useful on a developer system, they are not +needed when GTK is built e.g. for a flatpak runtime. These +options allow to disable building tests and demos. diff --git a/docs/reference/gtk/building.xml b/docs/reference/gtk/building.xml deleted file mode 100644 index 49b45b1d65..0000000000 --- a/docs/reference/gtk/building.xml +++ /dev/null @@ -1,518 +0,0 @@ - - - - -Compiling the GTK libraries -3 -GTK Library - - - -Compiling the GTK Libraries - -How to compile GTK itself - - - - Building GTK - - Before we get into the details of how to compile GTK, we should - mention that in many cases, binary packages of GTK prebuilt for - your operating system will be available, either from your - operating system vendor or from independent sources. If such a - set of packages is available, installing it will get you - programming with GTK much faster than building it yourself. In - fact, you may well already have GTK installed on your system - already. - - - In order to build GTK, you will need meson - installed on your system. On Linux, and other UNIX-like operating - systems, you will also need ninja. This - guide does not cover how to install these two requirements, but you - can refer to the Meson website - for more information. The Ninja - build tool is also usable on various operating systems, so we will - refer to it in the examples. - - - If you are building GTK from a source distribution or from a Git - clone, you will need to use meson to - configure the project. The most commonly useful argument is the - --prefix one, which determines where the - files will go once installed. To install GTK under a prefix - like /opt/gtk you would run Meson as: - - - - meson setup --prefix /opt/gtk builddir - - - - Meson will create the builddir directory and - place all the build artefacts there. - - - You can get a list of all available options for the build by - running meson configure. - - - After Meson successfully configured the build directory, you then - can run the build, using Ninja: - - - - cd builddir - ninja - ninja install - - - - If you don't have permission to write to the directory you are - installing in, you may have to change to root temporarily before - running ninja install. - - - Several environment variables are useful to pass to set before - running meson. CPPFLAGS - contains options to pass to the C compiler, and is used to tell - the compiler where to look for include files. The LDFLAGS - variable is used in a similar fashion for the linker. Finally the - PKG_CONFIG_PATH environment variable contains - a search path that pkg-config (see below) - uses when looking for files describing how to compile - programs using different libraries. If you were installing GTK - and it's dependencies into /opt/gtk, you - might want to set these variables as: - - - CPPFLAGS="-I/opt/gtk/include" - LDFLAGS="-L/opt/gtk/lib" - PKG_CONFIG_PATH="/opt/gtk/lib/pkgconfig" - export CPPFLAGS LDFLAGS PKG_CONFIG_PATH - - - You may also need to set the LD_LIBRARY_PATH - environment variable so the systems dynamic linker can find - the newly installed libraries, and the PATH - environment program so that utility binaries installed by - the various libraries will be found. - - - LD_LIBRARY_PATH="/opt/gtk/lib" - PATH="/opt/gtk/bin:$PATH" - export LD_LIBRARY_PATH PATH - - - - - Build types - - Meson has different build types, exposed by the buildtype - configuration option. GTK enables and disables functionality depending on - the build type used when calling meson to - configure the build. - - - <systemitem>debug</systemitem> and <systemitem>debugoptimized</systemitem> - - - GTK will enable debugging code paths in both the - debug and debugoptimized - build types. Builds with buildtype set - to debug will additionally enable - consistency checks on the internal state of the toolkit. - - - - It is recommended to use the debug or - debugoptimized build types when developing - GTK itself. Additionally, debug builds of - GTK are recommended for profiling and debugging GTK applications, - as they include additional validation of the internal state. - - - - The debugoptimized build type is the - default for GTK if no build type is specified when calling - meson - - - - - <systemitem>release</systemitem> - - - The release build type will disable - debugging code paths and additional run time safeties, like - checked casts for object instances. - - - - - The plain build type provided by Meson - should only be used when packaging GTK, and it's expected - that packagers will provide their own compiler flags when - building GTK. See the previous section for the list of - environment variables to be used to define compiler and - linker flags. - - - - - Dependencies - - Before you can compile the GTK widget toolkit, you need to have - various other tools and libraries installed on your - system. Dependencies of GTK have their own build systems, so - you will need to refer to their own installation instructions. - - - A particular important tool used by GTK to find its dependencies - is pkg-config. - - - pkg-config - is a tool for tracking the compilation flags needed for - libraries that are used by the GTK libraries. (For each - library, a small .pc text file is installed - in a standard location that contains the compilation flags - needed for that library along with version number information.) - - - Some of the libraries that GTK depends on are maintained by - by the GTK team: GLib, GdkPixbuf, Pango, ATK and GObject Introspection. - Other libraries are maintained separately. - - - - - The GLib library provides core non-graphical functionality - such as high level data types, Unicode manipulation, and - an object and type system to C programs. It is available - from here. - - - - - The GdkPixbuf library - provides facilities for loading images in a variety of file formats. - It is available here. - - - - - Pango is a library - for internationalized text handling. It is available - here. - - - - - ATK is the Accessibility Toolkit. It provides a set of generic - interfaces allowing accessibility technologies such as - screen readers to interact with a graphical user interface. - It is available - here. - - - - - Gobject Introspection - is a framework for making introspection data available to - language bindings. It is available - here. - - - - - External dependencies - - - The GNU - libiconv library is needed to build GLib if your - system doesn't have the iconv() - function for doing conversion between character - encodings. Most modern systems should have - iconv(). - - - - - The libintl library from the GNU gettext - package is needed if your system doesn't have the - gettext() functionality for handling - message translation databases. - - - - - The libraries from the X window system are needed to build - Pango and GTK. You should already have these installed on - your system, but it's possible that you'll need to install - the development environment for these libraries that your - operating system vendor provides. - - - - - The fontconfig - library provides Pango with a standard way of locating - fonts and matching them against font names. - - - - - Cairo - is a graphics library that supports vector graphics and image - compositing. Both Pango and GTK use Cairo for drawing. - - - - - libepoxy - is a library that abstracts the differences between different - OpenGL libraries. GTK uses it for cross-platform GL support - and for its own drawing. - - - - - Graphene - is a library that provides vector and matrix types for 2D and - 3D transformations. GTK uses it internally for drawing. - - - - - The Wayland libraries - are needed to build GTK with the Wayland backend. - - - - - The shared-mime-info - package is not a hard dependency of GTK, but it contains definitions - for mime types that are used by GIO and, indirectly, by GTK. - gdk-pixbuf will use GIO for mime type detection if possible. For this - to work, shared-mime-info needs to be installed and - XDG_DATA_DIRS set accordingly at configure time. - Otherwise, gdk-pixbuf falls back to its built-in mime type detection. - - - - - - Building and testing GTK - - First make sure that you have the necessary external - dependencies installed: pkg-config, Meson, Ninja, - the JPEG, PNG, and TIFF libraries, FreeType, and, if necessary, - libiconv and libintl. To get detailed information about building - these packages, see the documentation provided with the - individual packages. On any average Linux system, it's quite likely - you'll have all of these installed already, or they will be easily - accessible through your operating system package repositories. - - - Then build and install the GTK libraries in the order: - GLib, Cairo, Pango, ATK, then GTK. For each library, follow the - instructions they provide, and make sure to share common settings - between them and the GTK build; if you are using a separate prefix - for GTK, for instance, you will need to use the same prefix for all - its dependencies you build. If you're lucky, this will all go smoothly, - and you'll be ready to start compiling - your own GTK applications. You can test your GTK installation - by running the gtk4-demo program that - GTK installs. - - - If one of the projects you're configuring or building fails, look - closely at the error messages printed; these will often provide useful - information as to what went wrong. Every build system has its own - log that can help you understand the issue you're encountering. If all - else fails, you can ask for help on the gtk-list mailing list. - See for more information. - - - - - Extra Configuration Options - - - In addition to the normal options provided by Meson, GTK defines - various arguments that modify what should be built. - - - meson - - - -Dx11-backend=true - -Dx11-backend=false - - - - -Dwayland-backend=true - -Dwayland-backend=false - - - - -Dbroadway-backend=true - -Dbroadway-backend=false - - - - -Dwin32-backend=true - -Dwin32-backend=false - - - - -Dquartz-backend=true - -Dquartz-backend=false - - - - -Dmedia=gstreamer - -Dmedia=ffmpeg - -Dmedia=all - -Dmedia=none - - - - -Dvulkan=yes - -Dvulkan=no - -Dvulkan=auto - - - - -Dxinerama=yes - -Dxinerama=no - -Dxinerama=auto - - - - -Dcloudproviders=true - -Dcloudproviders=false - - - - -Dprint-backends=all - -Dprint-backends=none - -Dprint-backends=cups,lpr,... - - - - -Dcolord=yes - -Dcolord=no - -Dcolord=auto - - - - -Dgtk_doc=true - -Dgtk_doc=false - - - - -Dman-pages=true - -Dman-pages=false - - - - -Dintrospection=true - -Dintrospection=false - - - - - - <systemitem>xinerama</systemitem> - - - By default GTK will try to link against the Xinerama libraries - if they are found. This options can be used to explicitly control - whether Xinerama should be used. - - - - - <systemitem>gtk_doc</systemitem> and - <systemitem>man-pages</systemitem> - - - The gtk-doc package is - used to generate the reference documentation included - with GTK. By default support for gtk-doc - is disabled because it requires various extra dependencies - to be installed. If you have - gtk-doc installed and - are modifying GTK, you may want to enable - gtk-doc support by passing - in gtk_doc. - - - Additionally, some tools provided by GTK have their own - manual pages generated using a similar set of dependencies; - if you have xsltproc then you - can generate manual pages by passing man-pages - when configuring the build. - - - - - <systemitem>print-backends</systemitem> - - - By default, GTK will try to build various print backends if - their dependencies are found. This option can be used to - explicitly control which print backends should be built. - - - - - <systemitem>x11-backend</systemitem>, - <systemitem>win32-backend</systemitem>, - <systemitem>quartz-backend</systemitem>, - <systemitem>broadway-backend</systemitem> and - <systemitem>wayland-backend</systemitem> - - - Enable specific backends for GDK. If none of these options - are given, the Wayland backend will be enabled by default, - if the platform is Linux; the X11 backend will also be enabled - by default, unless the platform is Windows, in which case the - default is win32, or the platform is macOS, in which case the - default is quartz. If any backend is explicitly enabled or disabled, - no other platform will be enabled automatically. - - - - - <systemitem>introspection</systemitem> - - - Allows to disable building introspection support. This is option - is mainly useful for shortening turnaround times on developer - systems. Installed builds of GTK should always have introspection - support. - - - - - <systemitem>build-tests</systemitem>, - <systemitem>install-tests</systemitem>, - <systemitem>demos</systemitem> - - - By default, GTK will build quite a few tests and demos. - While these are useful on a developer system, they are not - needed when GTK is built e.g. for a flatpak runtime. These - options allow to disable building tests and demos. - - - - - - diff --git a/docs/reference/gtk/compiling.xml b/docs/reference/gtk/compiling.md similarity index 61% rename from docs/reference/gtk/compiling.xml rename to docs/reference/gtk/compiling.md index 25ea551120..f0351029fd 100644 --- a/docs/reference/gtk/compiling.xml +++ b/docs/reference/gtk/compiling.md @@ -1,94 +1,55 @@ - - - - -Compiling GTK Applications -3 -GTK Library - +# Compiling GTK Applications on UNIX {#gtk-compiling} - -Compiling GTK Applications - -How to compile your GTK application - - - - -Compiling GTK Applications on UNIX - - To compile a GTK application, you need to tell the compiler where to find the GTK header files and libraries. This is done with the -pkg-config utility. - - -The following interactive shell session demonstrates how -pkg-config is used (the actual output on -your system may be different): - +`pkg-config` utility. + +The following interactive shell session demonstrates how `pkg-config` +is used (the actual output on your system may be different): + +``` $ pkg-config --cflags gtk4 -pthread -I/usr/include/gtk-4.0 -I/usr/lib64/gtk-4.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12 $ pkg-config --libs gtk4 -pthread -lgtk-4 -lgdk-4 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lgdk_pixbuf-2.0 -lpangocairo-1.0 -lcairo -lpango-1.0 -lfreetype -lfontconfig -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lrt -lglib-2.0 - - - +``` The simplest way to compile a program is to use the "backticks" feature of the shell. If you enclose a command in backticks -(not single quotes), then its output will be -substituted into the command line before execution. So to compile -a GTK Hello, World, you would type the following: - +(*not single quotes*), then its output will be substituted into the +command line before execution. So to compile a GTK Hello, World, you +would type the following: +``` $ cc `pkg-config --cflags gtk4` hello.c -o hello `pkg-config --libs gtk4` - - - - +``` Deprecated GTK functions are annotated to make the compiler emit warnings when they are used (e.g. with gcc, you need to use the -Wdeprecated-declarations option). If these warnings are problematic, they can be turned off by defining the preprocessor symbol %GDK_DISABLE_DEPRECATION_WARNINGS by using the commandline -option -DGDK_DISABLE_DEPRECATION_WARNINGS - +option `-DGDK_DISABLE_DEPRECATION_WARNINGS`. - GTK deprecation annotations are versioned; by defining the macros %GDK_VERSION_MIN_REQUIRED and %GDK_VERSION_MAX_ALLOWED, you can specify the range of GTK versions whose API you want to use. APIs that were deprecated before or introduced after this range will trigger compiler warnings. - - Here is how you would compile hello.c if you want to allow it to use symbols that were not deprecated in 4.2: - +``` $ cc `pkg-config --cflags gtk4` -DGDK_VERSION_MIN_REQIRED=GDK_VERSION_4_2 hello.c -o hello `pkg-config --libs gtk4` - - +``` - And here is how you would compile hello.c if you don't want it to use any symbols that were introduced after 4.2: - +``` $ cc `pkg-config --cflags gtk4` -DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_4_2 hello.c -o hello `pkg-config --libs gtk4` - - - - +``` The older deprecation mechanism of hiding deprecated interfaces entirely from the compiler by using the preprocessor symbol GTK_DISABLE_DEPRECATED is still used for deprecated macros, enumeration values, etc. To detect uses of these in your code, -use the commandline option -DGTK_DISABLE_DEPRECATED. +use the commandline option `-DGTK_DISABLE_DEPRECATED`. There are similar symbols GDK_DISABLE_DEPRECATED, -GDK_PIXBUF_DISABLE_DEPRECATED and G_DISABLE_DEPRECATED for GDK, GdkPixbuf and -GLib. - - - - +GDK_PIXBUF_DISABLE_DEPRECATED and G_DISABLE_DEPRECATED for GDK, +GdkPixbuf and GLib. diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index a0ec153bbd..18dd293336 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -419,7 +419,7 @@ GTK Platform Support - + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index 13b4072023..dadb199297 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -341,8 +341,6 @@ images = [ content_files = [ 'broadway.xml', - 'building.xml', - 'compiling.xml', 'glossary.xml', 'gtk4-broadwayd.xml', 'gtk4-builder-tool.xml', @@ -359,7 +357,6 @@ content_files = [ 'overview.xml', 'question_index.xml', 'resources.xml', - 'running.xml', 'text_widget.xml', 'tree_widget.xml', 'visual_index.xml', @@ -369,7 +366,6 @@ content_files = [ ] expand_content_files = [ - 'compiling.xml', 'glossary.xml', 'question_index.xml', 'text_widget.xml', @@ -377,6 +373,9 @@ expand_content_files = [ ] expand_content_md_files = [ + 'building.md', + 'compiling.md', + 'running.md', 'migrating-2to4.md', 'migrating-3to4.md', 'actions.md', diff --git a/docs/reference/gtk/running.md b/docs/reference/gtk/running.md new file mode 100644 index 0000000000..7ac5c811f0 --- /dev/null +++ b/docs/reference/gtk/running.md @@ -0,0 +1,350 @@ +# Running and debugging GTK Applications {#gtk-running} + +## Environment variables + +GTK inspects a number of environment variables in addition to +standard variables like `LANG`, `PATH`, `HOME` or `DISPLAY`; mostly +to determine paths to look for certain files. The [X11]{#x11-envar}, +[Windows]{#win32-envar} and [Broadway]{#broadway-envar} GDK backends +use some additional environment variables. + +### GTK_DEBUG {#GTK_Debug-Options} + +Unless GTK has been configured with `-Ddebug=false`, this variable +can be set to a list of debug options, which cause GTK to print out +different types of debugging information. + +actions + : Actions and menu models +builder + : GtkBuilder support +geometry + : Size allocation +icontheme + : Icon themes +keybindings + : Keybindings +modules + : Loading of modules +printing + : Printing support +size-request + : Size requests +text + : Text widget internals +tree + : Tree widget internals + + A number of keys are influencing behavior instead of just logging: + +interactive + : Open the [interactive debugger](#interactive-debugging) +no-css-cache + : Bypass caching for CSS style properties +touchscreen + : Pretend the pointer is a touchscreen device +updates + : Visual feedback about window updates +resize + : Highlight resizing widgets +layout + : Show layout borders +snapshot + : Include debug render nodes in the generated snapshots + +The special value `all` can be used to turn on all debug options. +The special value `help` can be used to obtain a list of all +supported debug options. + +### GTK_PATH {#gtk-path} + +Specifies a list of directories to search when GTK is looking for +dynamically loaded objects such as input method modules and print +backends. If the path to the dynamically loaded object is given as +an absolute path name, then GTK loads it directly. Otherwise, GTK +goes in turn through the directories in `GTK_PATH`, followed by +the directory `.gtk-4.0` in the user's home directory, followed +by the system default directory, which is `libdir/gtk-4.0/modules`. +(If `GTK_EXE_PREFIX` is defined, `libdir` is `$GTK_EXE_PREFIX/lib`. +Otherwise it is the libdir specified when GTK was configured, usually +`/usr/lib`, or `/usr/local/lib`.) + +For each directory in this list, GTK actually looks in a subdirectory +`directory/version/host/type`. Where `version` is derived from the +version of GTK (use `pkg-config --variable=gtk_binary_version gtk4` +to determine this from a script), `host` is the architecture on +which GTK was built. (use `pkg-config --variable=gtk_host gtk4` to +determine this from a script), and `type` is a directory specific to +the type of modules; currently it can be `modules`, `immodules` or +`printbackends`, corresponding to the types of modules mentioned +above. Either `version`, `host`, or both may be omitted. GTK looks +first in the most specific directory, then in directories with +fewer components. +The components of `GTK_PATH` are separated by the ':' character on +Linux and Unix, and the ';' character on Windows. + +Note that this environment variable is read by GTK 2.x and GTK 3.x +too, which makes it unsuitable for setting it system-wide (or +session-wide), since doing so will cause applications using +different GTK versions to see incompatible modules. + +### GTK_IM_MODULE + +Specifies an IM module to use in preference to the one determined +from the locale. If this isn't set and you are running on the system +that enables `XSETTINGS` and has a value in `Gtk/IMModule`, that will +be used for the default IM module. This also can be a colon-separated +list of input-methods, which GTK will try in turn until it finds one +available on the system. + +### GTK_EXE_PREFIX + +If set, GTK uses `$GTK_EXE_PREFIX/lib` instead of the libdir +configured when GTK was compiled. + +### GTK_DATA_PREFIX + +If set, GTK uses `$GTK_DATA_PREFIX` instead of the prefix +configured when GTK was compiled. + +### GTK_THEME + +If set, makes GTK use the named theme instead of the theme +that is specified by the gtk-theme-name setting. This is intended +mainly for easy debugging of theme issues. + +It is also possible to specify a theme variant to load, by appending +the variant name with a colon, like this: `GTK_THEME=Adwaita:dark`. + +The following environment variables are used by GdkPixbuf, GDK or +Pango, not by GTK itself, but we list them here for completeness +nevertheless. + +### GDK_PIXBUF_MODULE_FILE + +Specifies the file listing the GdkPixbuf loader modules to load. +This environment variable overrides the default value +`libdir/gtk-4.0/4.0.0/loaders.cache` (`libdir` is the sysconfdir +specified when GTK was configured, usually `/usr/lib`.) + +The `loaders.cache` file is generated by the +`gdk-pixbuf-query-loaders` utility. + +### GDK_DEBUG + +Unless GTK has been configured with `-Ddebug=false`, this variable +can be set to a list of debug options, which cause GDK to print out +different types of debugging information. + +cursor + : Information about cursor objects (only win32) +eventloop + : Information about event loop operation (mostly Quartz) +misc + : Miscellaneous information +frames + : Information about the frame clock +settings + : Information about xsettings +selection + : Information about selections +clipboard + : Information about clipboards +dnd + : Information about drag-and-drop +opengl + : Information about OpenGL +vulkan + : Information about Vulkan + +A number of options affect behavior instead of logging: + +nograbs + : Turn off all pointer and keyboard grabs +gl-disable + : Disable OpenGL support +gl-software + : Force OpenGL software rendering +gl-texture-rect + : Use the OpenGL texture rectangle extension, if available +gl-legacy + : Use a legacy OpenGL context +gl-gles + : Use a GLES OpenGL context +vulkan-disable + : Disable Vulkan support +vulkan-validate + : Load the Vulkan validation layer, if available + +The special value `all` can be used to turn on all +debug options. The special value `help` can be used +to obtain a list of all supported debug options. + +### GSK_DEBUG {#GSK-Debug-Options} + +Unless GTK has been configured with `-Ddebug=false`, +this variable can be set to a list of debug options, +which cause GSK to print out different types of debugging +information. + +renderer + : General renderer information +cairo + : cairo renderer information +opengl + : OpenGL renderer information +shaders + : Shaders +surface + : Surfaces +vulkan + : Vulkan renderer information +fallback + : Information about fallbacks +glyphcache + : Information about glyph caching + + A number of options affect behavior instead of logging: + +diff + : Show differences +geometry + : Show borders +full-redraw + : Force full redraws for every frame +sync + : Sync after each frame +vulkan-staging-image + : Use a staging image for Vulkan texture upload +vulkan-staging-buffer + : Use a staging buffer for Vulkan texture upload + +The special value `all` can be used to turn on all +debug options. The special value `help` can be used +to obtain a list of all supported debug options. + +### GDK_BACKEND + +If set, selects the GDK backend to use. Selecting a backend +requires that GTK is compiled with support for that backend. +The following backends can be selected, provided they are +included in the GDK libraries you are using: + +quartz + : Selects the native Quartz backend +win32 + : Selects the native backend for Microsoft Windows +x11 + : Selects the native backend for connecting to X11 servers +broadway + : Selects the Broadway backend for display in web browsers +wayland + : Selects the Wayland backend for connecting to Wayland compositors + +This environment variable can contain a comma-separated list of +backend names, which are tried in order. The list may also contain +a *, which means: try all remaining backends. The special value +`help` can be used to make GDK print out a list of all available +backends. For more information about selecting backends, +see the gdk_display_manager_get() function. + +### GDK_VULKAN_DEVICE + +This variable can be set to the index of a Vulkan device to override +the default selection of the device that is used for Vulkan rendering. +The special value `list` can be used to obtain a list of all Vulkan +devices. + +### GSK_RENDERER + +If set, selects the GSK renderer to use. The following renderers can +be selected, provided they are included in the GTK library you are +using and the GDK backend supports them: + +help + : Prints information about available options +broadway + : Selects the Broadway-backend specific renderer +cairo + : Selects the fallback Cairo renderer +gl + : Selects the default OpenGL renderer +vulkan + : Selects the Vulkan renderer + +### GTK_CSD + +The default value of this environment variable is 1. If changed +to 0, this disables the default use of client-side decorations +on GTK windows, thus making the window manager responsible for +drawing the decorations of windows that do not have a custom +titlebar widget. + +CSD is always used for windows with a custom titlebar widget set, +as the WM should not draw another titlebar or other decorations +around the custom one. + +### XDG_DTA_HOME, XDG_DATA_DIRS + +GTK uses these environment variables to locate icon themes +and MIME information. For more information, see the +[Icon Theme Specification](https://freedesktop.org/Standards/icon-theme-spec) +the [Shared MIME-Info Database](https://freedesktop.org/Standards/shared-mime-info-spec) +and the [Base Directory Specification](https://freedesktop.org/Standards/basedir-spec). + +### DESKTOP_STARTUP_ID + +GTK uses this environment variable to provide startup notification +according to the [Startup Notification Spec](https://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt). +Following the specification, GTK unsets this variable after reading +it (to keep it from leaking to child processes). So, if you need its +value for your own purposes, you have to read it before calling +gtk_init(). + +## Interactive debugging {#interactive-debugging} + +![The inspector](inspector.png) + +GTK includes an interactive debugger, called the GTK Inspector, which +lets you explore the widget tree of any GTK application at runtime, +as well as tweak the theme and trigger visual debugging aids. You can +easily try out changes at runtime before putting them into the code. + +Note that the GTK inspector can only show GTK internals. It can not +understand the application-specific logic of a GTK application. Also, +the fact that the GTK inspector is running in the application process +limits what it can do. It is meant as a complement to full-blown +debuggers and system tracing facilities such as DTrace, not as a +replacement. + +To enable the GTK inspector, you can use the Control-Shift-I or +Control-Shift-D keyboard shortcuts, or set the `GTK_DEBUG=interactive` +environment variable. + +There are a few more environment variables that can be set to influence +how the inspector renders its UI. `GTK_INSPECTOR_DISPLAY` and +`GTK_INSPECTOR_RENDERER` determine the GDK display and the GSK +renderer that the inspector is using. + +In some situations, it may be inappropriate to give users access to +the GTK inspector. The keyboard shortcuts can be disabled with the +`enable-inspector-keybinding` key in the `org.gtk.Settings.Debug` +GSettings schema. + +## Profiling {#profiling} + +GTK supports profiling with sysprof. It exports timing information +about frameclock phases and various characteristics of GskRenders +in a format that can be displayed by sysprof or GNOME Builder. + +A simple way to capture data is to set the `GTK_TRACE` environment +variable. When it is set, GTK will write profiling data to a file +called `gtk.PID.syscap`. + +When launching the application from sysprof, it will set the +`SYSPROF_TRACE_FD` environment variable to point GTK at a file +descriptor to write profiling data to. + +When GtkApplication registers with D-Bus, it exports the +`org.gnome.Sysprof2.Profiler` D-Bus interface that lets sysprof +request profiling data at runtime. diff --git a/docs/reference/gtk/running.xml b/docs/reference/gtk/running.xml deleted file mode 100644 index 1c4df5ad43..0000000000 --- a/docs/reference/gtk/running.xml +++ /dev/null @@ -1,601 +0,0 @@ - - - - -Running GTK Applications -3 -GTK Library - - - -Running GTK Applications - -How to run and debug your GTK application - - - - -Running and debugging GTK Applications - - -Environment variables - - -GTK inspects a number of environment variables in addition to standard -variables like LANG, PATH, HOME -or DISPLAY; mostly to determine paths to look for certain -files. The X11, -Windows and -Broadway GDK backends use some -additional environment variables. - - - - <envar>GTK_DEBUG</envar> - - - Unless GTK has been configured with , - this variable can be set to a list of debug options, which cause GTK - to print out different types of debugging information. - - - actions - Actions and menu models - - - builder - GtkBuilder support - - - geometry - Size allocation - - - icontheme - Icon themes - - - keybindings - Keybindings - - - modules - Loading of modules - - - printing - Printing support - - - size-request - Size requests - - - text - Text widget internals - - - tree - Tree widget internals - - - A number of keys are influencing behavior instead of just logging: - - - interactive - Open the interactive debugger - - - no-css-cache - Bypass caching for CSS style properties - - - touchscreen - Pretend the pointer is a touchscreen device - - - updates - Visual feedback about window updates - - - resize - Highlight resizing widgets - - - layout - Show layout borders - - - snapshot - Include debug render nodes in the generated snapshots - - - The special value all can be used to turn on all - debug options. The special value help can be used - to obtain a list of all supported debug options. - - - - - <envar>GTK_PATH</envar> - - - Specifies a list of directories to search when GTK is looking for - dynamically loaded objects such as input method - modules and print backends. If the path to - the dynamically loaded object is given as an absolute path name, - then GTK loads it directly. - Otherwise, GTK goes in turn through the directories in GTK_PATH, - followed by the directory .gtk-4.0 in the user's - home directory, followed by the system default directory, - which is libdir/gtk-4.0/modules. - (If GTK_EXE_PREFIX is defined, libdir is - $GTK_EXE_PREFIX/lib. Otherwise it is the libdir - specified when GTK was configured, usually - /usr/lib, or - /usr/local/lib.) - For each directory in this list, GTK actually looks in a - subdirectory - directory/version/host/type - Where version is derived from the - version of GTK (use pkg-config - --variable=gtk_binary_version gtk4 to determine this from a - script), host is the architecture on - which GTK was built. (use pkg-config - --variable=gtk_host gtk4 to determine this from a - script), and type is a directory - specific to the type of modules; currently it can be - modules, engines, - immodules, filesystems or - printbackends, corresponding to the types of - modules mentioned above. Either version, - host, or both may be omitted. GTK looks - first in the most specific directory, then in directories with - fewer components. - The components of GTK_PATH are separated by the ':' character on - Linux and Unix, and the ';' character on Windows. - - - Note that this environment variable is read by GTK 2.x and GTK 3.x too, - which makes it unsuitable for setting it system-wide (or session-wide), - since doing so will cause applications using different GTK versions - to see incompatible modules. - - - - - <envar>GTK_IM_MODULE</envar> - - - Specifies an IM module to use in preference to the one determined - from the locale. If this isn't set and you are running on the system - that enables XSETTINGS and has a value in - Gtk/IMModule, that will be used for the default - IM module. - This also can be a colon-separated list of input-methods, which - GTK will try in turn until it finds one available on the system. - - - - - <envar>GTK_EXE_PREFIX</envar> - - - If set, GTK uses $GTK_EXE_PREFIX/lib instead of - the libdir configured when GTK was compiled. - - - - - <envar>GTK_DATA_PREFIX</envar> - - - If set, makes GTK use $GTK_DATA_PREFIX - instead of the prefix configured when GTK was compiled. - - - - - <envar>GTK_THEME</envar> - - - If set, makes GTK use the named theme instead of the theme - that is specified by the gtk-theme-name setting. This is intended - mainly for easy debugging of theme issues. - - - It is also possible to specify a theme variant to load, by appending - the variant name with a colon, like this: `GTK_THEME=Adwaita:dark`. - - - - -The following environment variables are used by GdkPixbuf, GDK or -Pango, not by GTK itself, but we list them here for completeness -nevertheless. - - - - <envar>GDK_PIXBUF_MODULE_FILE</envar> - - - Specifies the file listing the GdkPixbuf loader modules to load. - This environment variable overrides the default value - libdir/gtk-4.0/4.0.0/loaders.cache - (libdir is the sysconfdir specified when - GTK was configured, usually /usr/local/lib.) - - - The loaders.cache file is generated by the - gdk-pixbuf-query-loaders utility. - - - - - <envar>GDK_DEBUG</envar> - - - Unless GTK has been configured with , - this variable can be set to a list of debug options, which cause GDK - to print out different types of debugging information. - - - cursor - Information about cursor objects (only win32) - - - eventloop - Information about event loop operation (mostly Quartz) - - - misc - Miscellaneous information - - - frames - Information about the frame clock - - - settings - Information about xsettings - - - selection - Information about selections - - - clipboard - Information about clipboards - - - dnd - Information about drag-and-drop - - - opengl - Information about OpenGL - - - vulkan - Information about Vulkan - - - A number of options affect behavior instead of logging: - - - nograbs - Turn off all pointer and keyboard grabs - - - gl-disable - Disable OpenGL support - - - gl-software - Force OpenGL software rendering - - - gl-texture-rect - Use the OpenGL texture rectangle extension, if available - - - gl-legacy - Use a legacy OpenGL context - - - gl-gles - Use a GLES OpenGL context - - - vulkan-disable - Disable Vulkan support - - - vulkan-validate - Load the Vulkan validation layer, if available - - - The special value all can be used to turn on all - debug options. The special value help can be used - to obtain a list of all supported debug options. - - - - - <envar>GSK_DEBUG</envar> - - - Unless GTK has been configured with , - this variable can be set to a list of debug options, which cause GSK - to print out different types of debugging information. - - - renderer - General renderer information - - - cairo - cairo renderer information - - - opengl - OpenGL renderer information - - - shaders - Shaders - - - ssurface - Surfaces - - - vulkan - Vulkan renderer information - - - fallback - Information about fallbacks - - - glyphcache - Information about glyph caching - - - A number of options affect behavior instead of logging: - - - diff - Show differences - - - geometry - Show borders - - - full-redraw - Force full redraws for every frame - - - sync - Sync after each frame - - - vulkan-staging-image - Use a staging image for Vulkan texture upload - - - vulkan-staging-buffer - Use a staging buffer for Vulkan texture upload - - - The special value all can be used to turn on all - debug options. The special value help can be used - to obtain a list of all supported debug options. - - - - - <envar>GDK_BACKEND</envar> - - - If set, selects the GDK backend to use. Selecting a backend requires that - GTK is compiled with support for that backend. The following backends can - be selected, provided they are included in the GDK libraries you are using: - - - - quartz - Selects the native Quartz backend - - - - win32 - Selects the native backend for Microsoft Windows - - - - x11 - Selects the native backend for connecting to X11 servers. - - - - broadway - Selects the Broadway backend for display in web browsers - - - - wayland - Selects the Wayland backend for connecting to Wayland display servers - - - - This environment variable can contain a comma-separated list of backend names, - which are tried in order. The list may also contain a *, which means: try all - remaining backends. The special value "help" can be used to make GDK print out - a list of all available backends. For more information about selecting backends, - see the gdk_display_manager_get() function. - - - - - <envar>GDK_VULKAN_DEVICE</envar> - - - This variable can be set to the index of a Vulkan device to override the - default selection of the device that is used for Vulkan rendering. - The special value list can be used to obtain a list - of all Vulkan devices. - - - - - <envar>GSK_RENDERER</envar> - - - If set, selects the GSK renderer to use. The following renderers can - be selected, provided they are included in the GTK library you are using - and the GDK backend supports them: - - - - help - Prints information about available options - - - - broadway - Selects the Broadway-backend specific renderer - - - - cairo - Selects the fallback Cairo renderer - - - - gl - Selects the default OpenGL renderer - - - - vulkan - Selects the Vulkan renderer - - - - - - - - <envar>GTK_CSD</envar> - - - The default value of this environment variable is 1. If changed to 0, this - disables the default use of client-side decorations on GTK windows, thus - making the window manager responsible for drawing the decorations of - windows that do not have a custom titlebar widget. - - - CSD is always used for windows with a custom titlebar widget set, as the WM - should not draw another titlebar or other decorations around the custom one. - - - - - <envar>XDG_DATA_HOME</envar>, <envar>XDG_DATA_DIRS</envar> - - - GTK uses these environment variables to locate icon themes - and MIME information. For more information, see - Icon Theme Specification, - the Shared MIME-info Database - and the Base Directory Specification. - - - - - <envar>DESKTOP_STARTUP_ID</envar> - - - GTK uses this environment variable to provide startup notification - according to the Startup Notification Spec. - Following the specification, GTK unsets this variable after reading - it (to keep it from leaking to child processes). So, if you need its - value for your own purposes, you have to read it before calling - gtk_init(). - - - - - - -Interactive debugging - - - - - GTK includes an interactive debugger, called the GTK Inspector, which - lets you explore the widget tree of any GTK application at runtime, as - well as tweak the theme and trigger visual debugging aids. You can - easily try out changes at runtime before putting them into the code. - - - Note that the GTK inspector can only show GTK internals. It can not - understand the application-specific logic of a GTK application. Also, - the fact that the GTK inspector is running in the application process - limits what it can do. It is meant as a complement to full-blown debuggers - and system tracing facilities such as DTrace, not as a replacement. - - - To enable the GTK inspector, you can use the Control-Shift-I or - Control-Shift-D keyboard shortcuts, or set the - GTK_DEBUG=interactive environment variable. - - - There are a few more environment variables that can be set to influence - how the inspector renders its UI. GTK_INSPECTOR_DISPLAY and - GTK_INSPECTOR_RENDERER determine the GDK display and - the GSK renderer that the inspector is using. - - - - In some situations, it may be inappropriate to give users access to the - GTK inspector. The keyboard shortcuts can be disabled with the - `enable-inspector-keybinding` key in the `org.gtk.Settings.Debug` - GSettings schema. - - - - - - Profiling - - - GTK supports profiling with sysprof. It exports timing information - about frameclock phases and various characteristics of GskRenders - in a format that can be displayed by sysprof or GNOME Builder. - - - A simple way to capture data is to set the GTK_TRACE - environment variable. When it is set, GTK will write profiling - data to a file called - gtk.PID.syscap. - - - When launching the application from sysprof, it will set the - SYSPROF_TRACE_FD environment variable to point - GTK at a file descriptor to write profiling data to. - - - When GtkApplication registers with D-Bus, it exports the - org.gnome.Sysprof2.Profiler interface - that lets sysprof request profiling data at runtime. - - - - - - From bf14d75cad59abea59c713611347e08b119364c0 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 00:08:58 -0400 Subject: [PATCH 06/18] docs: Refresh the inspector screenshot --- docs/reference/gtk/images/inspector.png | Bin 87410 -> 79044 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/reference/gtk/images/inspector.png b/docs/reference/gtk/images/inspector.png index 0f16b21e91efe60b370999b6a1ef1bedd4561631..9d9f2d928e68ca9cb326acf6dd6a47043aeab1a8 100644 GIT binary patch literal 79044 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 z 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 literal 87410 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 _TDFH7@;7QJjE`sGs9rR%j$2@s7;!d zxb{OuN;PLXu1MO&uQU0FYQ08oF}$K#q=-EUS{hfa7U~ zMNt4#we6FaPR89UR%@Jc?T3y9Wt=Ol!%gl|F>0vqOB27ptncb6TN9=(pWm7AqdV0Y z>m+fw{D_ykI+$EIZSC}uTatU{Rk4wLj*`L7k=#FU^sPX?imO1_kFoarH zSJz5)O7x9aOw5(#)^1%HP4W2`seLeh*2CMou*Y0xOagU3HA~F%>BnO0eVUg5+8hlt zZQI(YjuuWgUqkO}W|^f==R}%>%eQBwq?1i&_y6Kp+xI^jae_Z+H7vaMa6d{ zb6fQ?8EtR35mLbo&u3(1h+wO9WIOPy(1v+8uJQb8Y*c2}yPiN4o?#m#BauW+N5(=E z!pCOvxYF4%Y1cYE$Zch#?FP+rMXa$X(|(bAKDR8jmeDV=Tqr*5)3Gh~1xy~5tz2$l z7_UBm;&?8iL&D2?#+y(kWrtq&3$YxIcRL?-jZylGeM+V~VoHh1?AccTXp@CY{QQ)( z#OdhUkV$gSH@T`(ThVW&zCVOsR;^C-#%gF;v`|V1$bHv(Ek+wav!D50f^efD-sYsS zH{J$AjL>EM(7N@+n}AcrOF9{`@CvTly9?@=oQ+c}InFUA-&Yo)D$4S$GES4pZr2qf z_+6WUb2a?-Yl%=Y&xNE# z6wmkGZnLEug?m-IsiwVr$~AX03sLRt3n%r^8*OkuKDqsNye*eL&(&3sM``yfVZz47 z7)Md49%EhPQdqcTd)C<2>|%KZ1M_ge@mL|dQwg@C5r^8@ee94`k09UmrORDu-W$H9*Updt3a%2QxJIQL>CD;famUu-~NfwWUNw-%A�hLq1t-+ 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 Date: Sun, 24 May 2020 00:22:31 -0400 Subject: [PATCH 07/18] docs: Convert getting_started to markdown As part of the conversion, give up on including sources files from the examples directory, and instead include the content directly. All include mechanisms add complications. They were already complicated with xml, and markdown is not making things easier. We already did that for fragments, and if you make changes to these example sources, you probably need to revise the surrounding text anyway. --- docs/reference/gtk/getting_started.md | 1737 +++++++++++++++++++++ docs/reference/gtk/getting_started.xml.in | 1048 ------------- docs/reference/gtk/gtk4-docs.xml | 2 +- docs/reference/gtk/meson.build | 2 +- 4 files changed, 1739 insertions(+), 1050 deletions(-) create mode 100644 docs/reference/gtk/getting_started.md delete mode 100644 docs/reference/gtk/getting_started.xml.in diff --git a/docs/reference/gtk/getting_started.md b/docs/reference/gtk/getting_started.md new file mode 100644 index 0000000000..757b73faaf --- /dev/null +++ b/docs/reference/gtk/getting_started.md @@ -0,0 +1,1737 @@ +# Getting Started with GTK {#gtk-getting-started} + +GTK is a [widget toolkit](http://en.wikipedia.org/wiki/Widget_toolkit). +Each user interface created by GTK consists of widgets. This is implemented +in C using [GObject](#gobject), an object-oriented framework for C. Widgets +are organized in a hierarchy. The window widget is the main container. +The user interface is then built by adding buttons, drop-down menus, input +fields, and other widgets to the window. If you are creating complex user +interfaces it is recommended to use GtkBuilder and its GTK-specific markup +description language, instead of assembling the interface manually. You can +also use a visual user interface editor, like [glade](https://glade.gnome.org/). + +GTK is event-driven. The toolkit listens for events such as a click +on a button, and passes the event to your application. + +This chapter contains some tutorial information to get you started with +GTK programming. It assumes that you have GTK, its dependencies and a C +compiler installed and ready to use. If you need to build GTK itself first, +refer to the [Compiling the GTK libraries](#gtk-compiling) section in this +reference. + +## Basics + +To begin our introduction to GTK, we'll start with a very simple +application. This program will create an empty 200 × 200 pixel +window. + +![A window](window-default.png) + +Create a new file with the following content named `example-0.c`. + +``` {.c source=examples/window-default.c } +#include + +static void +activate (GtkApplication* app, + gpointer user_data) +{ + GtkWidget *window; + + window = gtk_application_window_new (app); + gtk_window_set_title (GTK_WINDOW (window), "Window"); + gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); + gtk_widget_show (window); +} + +int +main (int argc, + char **argv) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} +``` + +You can compile the program above with GCC using: + +``` +gcc `pkg-config --cflags gtk4` -o example-0 example-0.c `pkg-config --libs gtk4` +``` + +For more information on how to compile a GTK application, please +refer to the [Compiling GTK Applications](#gtk-compiling) +section in this reference. + +All GTK applications will, of course, include `gtk/gtk.h`, which declares +functions, types and macros required by GTK applications. + +Even if GTK installs multiple header files, only the top-level `gtk/gtk.h` +header can be directly included by third-party code. The compiler will abort +with an error if any othe header is directly included. + +In a GTK application, the purpose of the main() function is to create a +GtkApplication object and run it. In this example a GtkApplication pointer +named `app` is declared and then initialized using gtk_application_new(). + +When creating a GtkApplication, you need to pick an application identifier +(a name) and pass it to gtk_application_new() as parameter. For this example +`org.gtk.example` is used. For choosing an identifier for your application, see +[this guide](https://wiki.gnome.org/HowDoI/ChooseApplicationID). Lastly, +gtk_application_new() takes GApplicationFlags as input for your application, +if your application would have special needs. + +Next the [activate signal](https://wiki.gnome.org/HowDoI/GtkApplication) is +connected to the activate() function above the main() function. The `activate` +signal will be emitted when your application is launched with g_application_run() +on the line below. The g_application_run() call also takes as arguments the +command line arguments (the `argc` count and the `argv` string array). +Your application can override the command line handling, e.g. to open +files passed on the commandline. + +Within g_application_run() the activate signal is sent and we then proceed +into the activate() function of the application. This is where we construct +our GTK window, so that a window is shown when the application is launched. +The call to gtk_application_window_new() will create a new GtkWindow and +store it inside the `window` pointer. The window will have a frame, a title +bar, and window controls depending on the platform. + +A window title is set using gtk_window_set_title(). This function takes a +GtkWindow* pointer and a string as input. As our `window` pointer is a +GtkWidget pointer, we need to cast it to GtkWindow*. But instead of casting +`window` via `(GtkWindow*)`, `window` can be cast using the macro +`GTK_WINDOW()`. `GTK_WINDOW()` will check if the pointer is an instance of +the GtkWindow class, before casting, and emit a warning if the check fails. +More information about this convention can be found +[here](https://developer.gnome.org/gobject/stable/gtype-conventions.html). + +Finally the window size is set using gtk_window_set_default_size() +and the window is then shown by GTK via gtk_widget_show(). + +When you close the window, by for example pressing the X, the g_application_run() +call returns with a number which is saved inside an integer variable named +`status`. Afterwards, the GtkApplication object is freed from memory with +g_object_unref(). Finally the status integer is returned and the application +exits. + +While the program is running, GTK is receiving _events_. These are typically +input events caused by the user interacting with your program, but also things +like messages from the window manager or other applications. GTK processes +these and as a result, _signals_ may be emitted on your widgets. Connecting +handlers for these signals is how you normally make your program do something +in response to user input. + +The following example is slightly more complex, and tries to +showcase some of the capabilities of GTK. + +## Hello, World + +In the long tradition of programming languages and libraries, +this example is called *Hello, World*. + +![Hello, world](hello-world.png) + +### Hello World in C {#gtk-getting-started-hello-world} + +Create a new file with the following content named `example-1.c`. + +``` {.c source=examples/hello-world.c } +#include + +static void +print_hello (GtkWidget *widget, + gpointer data) +{ + g_print ("Hello World\n"); +} + +static void +activate (GtkApplication *app, + gpointer user_data) +{ + GtkWidget *window; + GtkWidget *button; + GtkWidget *box; + + window = gtk_application_window_new (app); + gtk_window_set_title (GTK_WINDOW (window), "Window"); + gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_window_set_child (GTK_WINDOW (window), box); + + button = gtk_button_new_with_label ("Hello World"); + g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); + g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window); + gtk_box_append (GTK_BOX (box), button); + + gtk_widget_show (window); +} + +int +main (int argc, + char **argv) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} + +``` + +You can compile the program above with GCC using: + +``` +gcc `pkg-config --cflags gtk4` -o example-1 example-1.c `pkg-config --libs gtk4` +``` + +As seen above, example-1.c builds further upon example-0.c by adding a +button to our window, with the label "Hello World". Two new GtkWidget +pointers are declared to accomplish this, `button` and `box`. The box +variable is created to store a GtkBox, which is GTK's way of controlling +the size and layout of buttons. + +The GtkBox is created with gtk_box_new() which takes a GtkOrientation +enum as parameter. The buttons which this box will contain can either be layed +out horizontally or vertically. This does not matter in this particular case, +as we are dealing with only one button. After initializing box with the newly +created GtkBox, the code adds the box widget to the window widget using +gtk_window_set_child(). + +Next the `button` variable is initialized in similar manner. +gtk_button_new_with_label() is called which returns a GtkButton to be +stored in `button`. Afterwards `button` is added to our `box`. + +Using g_signal_connect(), the button is connected to a function in our app called +print_hello(), so that when the button is clicked, GTK will call this function. +As the print_hello() function does not use any data as input, NULL is passed +to it. print_hello() calls g_print() with the string "Hello World" which will +print Hello World in a terminal if the GTK application was started from one. + +After connecting print_hello(), another signal is connected to the "clicked" +state of the button using g_signal_connect_swapped(). This functions is similar +to a g_signal_connect() with the difference lying in how the callback function +is treated. g_signal_connect_swapped() allows you to specify what the callback +function should take as parameter by letting you pass it as data. In this case +the function being called back is gtk_window_destroy() and the `window` pointer +is passed to it. This has the effect that when the button is clicked, the whole +GTK window is destroyed. In contrast if a normal g_signal_connect() were used +to connect the "clicked" signal with gtk_window_destroy(), then the function +would be called on `button` (which would not go well, since the function expects +a GtkWindow as argument). + +More information about creating buttons can be found +[here](https://wiki.gnome.org/HowDoI/Buttons). + +The rest of the code in `example-1.c` is identical to `example-0.c`. The next +section will elaborate further on how to add several GtkWidgets to your GTK +application. + +## Packing + +When creating an application, you'll want to put more than one widget inside +a window. When you do so, it becomes important to control how each widget is +positioned and sized. This is where packing comes in. + +GTK comes with a large variety of _layout containers_ whose purpose it +is to control the layout of the child widgets that are added to them. +See [Layout containers](#LayoutContainers) for an overview. + +The following example shows how the GtkGrid container lets you +arrange several buttons: + +![Grid packing](grid-packing.png) + +### Packing buttons {#gtk-getting-started-grid-packing} + +Create a new file with the following content named `example-2.c`. + +``` {.c source=examples/grid-packing.c } +#include + +static void +print_hello (GtkWidget *widget, + gpointer data) +{ + g_print ("Hello World\n"); +} + +static void +activate (GtkApplication *app, + gpointer user_data) +{ + GtkWidget *window; + GtkWidget *grid; + GtkWidget *button; + + /* create a new window, and set its title */ + window = gtk_application_window_new (app); + gtk_window_set_title (GTK_WINDOW (window), "Window"); + + /* Here we construct the container that is going pack our buttons */ + grid = gtk_grid_new (); + + /* Pack the container in the window */ + gtk_window_set_child (GTK_WINDOW (window), grid); + + button = gtk_button_new_with_label ("Button 1"); + g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); + + /* Place the first button in the grid cell (0, 0), and make it fill + * just 1 cell horizontally and vertically (ie no spanning) + */ + gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1); + + button = gtk_button_new_with_label ("Button 2"); + g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); + + /* Place the second button in the grid cell (1, 0), and make it fill + * just 1 cell horizontally and vertically (ie no spanning) + */ + gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 1); + + button = gtk_button_new_with_label ("Quit"); + g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window); + + /* Place the Quit button in the grid cell (0, 1), and make it + * span 2 columns. + */ + gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1); + + gtk_widget_show (window); + +} + +int +main (int argc, + char **argv) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} +``` + +You can compile the program above with GCC using: + +``` +gcc `pkg-config --cflags gtk4` -o example-2 example-2.c `pkg-config --libs gtk4` +``` + +## Custom Drawing + +Many widgets, like buttons, do all their drawing themselves. You just tell +them the label you want to see, and they figure out what font to use, draw +the button outline and focus rectangle, etc. Sometimes, it is necessary to +do some custom drawing. In that case, a GtkDrawingArea might be the right +widget to use. It offers a canvas on which you can draw by connecting to +the ::draw signal. + +The contents of a widget often need to be partially or fully redrawn, +e.g. when another window is moved and uncovers part of the widget, or +when the window containing it is resized. It is also possible to explicitly +cause part or all of the widget to be redrawn, by calling +gtk_widget_queue_draw() or its variants. GTK takes care of most of the +details by providing a ready-to-use cairo context to the ::draw signal +handler. + +The following example shows a ::draw signal handler. It is a bit more +complicated than the previous examples, since it also demonstrates +input event handling by means of event controllers. + +![Drawing](drawing.png) + +### Drawing in response to input {#gtk-getting-started-drawing} + +Create a new file with the following content named `example-4.c`. + +``` {.c source=examples/drawing.c } +#include + +/* Surface to store current scribbles */ +static cairo_surface_t *surface = NULL; + +static void +clear_surface (void) +{ + cairo_t *cr; + + cr = cairo_create (surface); + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + cairo_destroy (cr); +} + +/* Create a new surface of the appropriate size to store our scribbles */ +static void +resize_cb (GtkWidget *widget, + int width, + int height, + gpointer data) +{ + if (surface) + { + cairo_surface_destroy (surface); + surface = NULL; + } + + if (gtk_native_get_surface (gtk_widget_get_native (widget))) + { + surface = gdk_surface_create_similar_surface (gtk_native_get_surface (gtk_widget_get_native (widget)), + CAIRO_CONTENT_COLOR, + gtk_widget_get_width (widget), + gtk_widget_get_height (widget)); + + /* Initialize the surface to white */ + clear_surface (); + } +} + +/* Redraw the screen from the surface. Note that the draw + * callback receives a ready-to-be-used cairo_t that is already + * clipped to only draw the exposed areas of the widget + */ +static void +draw_cb (GtkDrawingArea *drawing_area, + cairo_t *cr, + int width, + int height, + gpointer data) +{ + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); +} + +/* Draw a rectangle on the surface at the given position */ +static void +draw_brush (GtkWidget *widget, + gdouble x, + gdouble y) +{ + cairo_t *cr; + + /* Paint to the surface, where we store our state */ + cr = cairo_create (surface); + + cairo_rectangle (cr, x - 3, y - 3, 6, 6); + cairo_fill (cr); + + cairo_destroy (cr); + + /* Now invalidate the drawing area. */ + gtk_widget_queue_draw (widget); +} + +static double start_x; +static double start_y; + +static void +drag_begin (GtkGestureDrag *gesture, + double x, + double y, + GtkWidget *area) +{ + start_x = x; + start_y = y; + + draw_brush (area, x, y); +} + +static void +drag_update (GtkGestureDrag *gesture, + double x, + double y, + GtkWidget *area) +{ + draw_brush (area, start_x + x, start_y + y); +} + +static void +drag_end (GtkGestureDrag *gesture, + double x, + double y, + GtkWidget *area) +{ + draw_brush (area, start_x + x, start_y + y); +} + +static void +pressed (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkWidget *area) +{ + clear_surface (); + gtk_widget_queue_draw (area); +} + +static void +close_window (void) +{ + if (surface) + cairo_surface_destroy (surface); +} + +static void +activate (GtkApplication *app, + gpointer user_data) +{ + GtkWidget *window; + GtkWidget *frame; + GtkWidget *drawing_area; + GtkGesture *drag; + GtkGesture *press; + + window = gtk_application_window_new (app); + gtk_window_set_title (GTK_WINDOW (window), "Drawing Area"); + + g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL); + + frame = gtk_frame_new (NULL); + gtk_window_set_child (GTK_WINDOW (window), frame); + + drawing_area = gtk_drawing_area_new (); + /* set a minimum size */ + gtk_widget_set_size_request (drawing_area, 100, 100); + + gtk_frame_set_child (GTK_FRAME (frame), drawing_area); + + gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_cb, NULL, NULL); + + g_signal_connect_after (drawing_area, "resize", G_CALLBACK (resize_cb), NULL); + + drag = gtk_gesture_drag_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); + gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (drag)); + g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), drawing_area); + g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), drawing_area); + g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), drawing_area); + + press = gtk_gesture_click_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY); + gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (press)); + + g_signal_connect (press, "pressed", G_CALLBACK (pressed), drawing_area); + + gtk_widget_show (window); +} + +int +main (int argc, + char **argv) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} +``` + +You can compile the program above with GCC using: + +``` +gcc `pkg-config --cflags gtk4` -o example-4 example-4.c `pkg-config --libs gtk4` +``` + +## Building user interfaces + +When constructing a more complicated user interface, with dozens +or hundreds of widgets, doing all the setup work in C code is +cumbersome, and making changes becomes next to impossible. + +Thankfully, GTK supports the separation of user interface +layout from your business logic, by using UI descriptions in an +XML format that can be parsed by the GtkBuilder class. + +### Packing buttons with GtkBuilder + +Create a new file with the following content named `example-3.c`. + +``` {.c source=examples/builder.c } +#include +#include + +static void +print_hello (GtkWidget *widget, + gpointer data) +{ + g_print ("Hello World\n"); +} + +static void +quit_cb (GtkWidget *widget, gpointer data) +{ + gboolean *done = data; + + *done = TRUE; + + g_main_context_wakeup (NULL); +} + +int +main (int argc, + char *argv[]) +{ + GtkBuilder *builder; + GObject *window; + GObject *button; + gboolean done = FALSE; + +#ifdef GTK_SRCDIR + g_chdir (GTK_SRCDIR); +#endif + + gtk_init (); + + /* Construct a GtkBuilder instance and load our UI description */ + builder = gtk_builder_new (); + gtk_builder_add_from_file (builder, "builder.ui", NULL); + + /* Connect signal handlers to the constructed widgets. */ + window = gtk_builder_get_object (builder, "window"); + g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done); + + button = gtk_builder_get_object (builder, "button1"); + g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); + + button = gtk_builder_get_object (builder, "button2"); + g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); + + button = gtk_builder_get_object (builder, "quit"); + g_signal_connect (button, "clicked", G_CALLBACK (quit_cb), &done); + + gtk_widget_show (GTK_WIDGET (window)); + + while (!done) + g_main_context_iteration (NULL, TRUE); + + return 0; +} +``` + +Create a new file with the following content named `builder.ui`. + +``` {.xml source=examples/builder.ui } + + + + Grid + + + + + Button 1 + + 0 + 0 + + + + + + Button 2 + + 1 + 0 + + + + + + Quit + + 0 + 1 + 2 + + + + + + + +``` + +You can compile the program above with GCC using: + +``` +gcc `pkg-config --cflags gtk4` -o example-3 example-3.c `pkg-config --libs gtk4` +``` + +Note that GtkBuilder can also be used to construct objects that are +not widgets, such as tree models, adjustments, etc. That is the reason +the method we use here is called gtk_builder_get_object() and returns +a GObject* instead of a GtkWidget*. + +Normally, you would pass a full path to gtk_builder_add_from_file() to +make the execution of your program independent of the current directory. +A common location to install UI descriptions and similar data is +`/usr/share/appname`. + +It is also possible to embed the UI description in the source code as a +string and use gtk_builder_add_from_string() to load it. But keeping the +UI description in a separate file has several advantages: It is then possible +to make minor adjustments to the UI without recompiling your program, and, +more importantly, graphical UI editors such as [glade](http://glade.gnome.org) +can load the file and allow you to create and modify your UI by point-and-click. + +## Building applications + +An application consists of a number of files: + +The binary + : This gets installed in `/usr/bin`. +A desktop file + : The desktop file provides important information about the application to + the desktop shell, such as its name, icon, D-Bus name, commandline to launch + it, etc. It is installed in `/usr/share/applications`. +An icon + : The icon gets installed in `/usr/share/icons/hicolor/48x48/apps`, where it +will be found regardless of the current theme. +A settings schema + : If the application uses GSettings, it will install its schema in + `/usr/share/glib-2.0/schemas`, so that tools like dconf-editor can find it. +Other resources + : Other files, such as GtkBuilder ui files, are best loaded from + resources stored in the application binary itself. This eliminates the + need for most of the files that would traditionally be installed in + an application-specific location in `/usr/share`. + +GTK includes application support that is built on top of GApplication. In this +tutorial we'll build a simple application by starting from scratch, adding more +and more pieces over time. Along the way, we'll learn about GtkApplication, +templates, resources, application menus, settings, GtkHeaderBar, GtkStack, +GtkSearchBar, GtkListBox, and more. + +The full, buildable sources for these examples can be found in the `examples/` +directory of the GTK source distribution, or +[online](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples in the GTK git +repository. You can build each example separately by using make with the +`Makefile.example` file. For more information, see the `README` included in the +examples directory. + +### A trivial application + +When using GtkApplication, the main() function can be very simple. We just call +g_application_run() and give it an instance of our application class. + +``` {.c source=examples/application1/main.c } +#include + +#include "exampleapp.h" + +int +main (int argc, char *argv[]) +{ + return g_application_run (G_APPLICATION (example_app_new ()), argc, argv); +} +``` + +All the application logic is in the application class, which is a subclass of +GtkApplication. Our example does not yet have any interesting functionality. +All it does is open a window when it is activated without arguments, and open +the files it is given, if it is started with arguments. + +To handle these two cases, we override the activate() vfunc, which gets called +when the application is launched without commandline arguments, and the open() +vfunc, which gets called when the application is launched with commandline +arguments. + +To learn more about GApplication entry points, consult the GIO +[documentation](https://developer.gnome.org/gio/2.36/GApplication.html#GApplication.description). + +``` {.c source=examples/application1/exampleapp.c } +#include + +#include "exampleapp.h" +#include "exampleappwin.h" + +struct _ExampleApp +{ + GtkApplication parent; +}; + +G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION); + +static void +example_app_init (ExampleApp *app) +{ +} + +static void +example_app_activate (GApplication *app) +{ + ExampleAppWindow *win; + + win = example_app_window_new (EXAMPLE_APP (app)); + gtk_window_present (GTK_WINDOW (win)); +} + +static void +example_app_open (GApplication *app, + GFile **files, + gint n_files, + const gchar *hint) +{ + GList *windows; + ExampleAppWindow *win; + int i; + + windows = gtk_application_get_windows (GTK_APPLICATION (app)); + if (windows) + win = EXAMPLE_APP_WINDOW (windows->data); + else + win = example_app_window_new (EXAMPLE_APP (app)); + + for (i = 0; i < n_files; i++) + example_app_window_open (win, files[i]); + + gtk_window_present (GTK_WINDOW (win)); +} + +static void +example_app_class_init (ExampleAppClass *class) +{ + G_APPLICATION_CLASS (class)->activate = example_app_activate; + G_APPLICATION_CLASS (class)->open = example_app_open; +} + +ExampleApp * +example_app_new (void) +{ + return g_object_new (EXAMPLE_APP_TYPE, + "application-id", "org.gtk.exampleapp", + "flags", G_APPLICATION_HANDLES_OPEN, + NULL); +} +``` + +Another important class that is part of the application support in GTK is +GtkApplicationWindow. It is typically subclassed as well. Our subclass does +not do anything yet, so we will just get an empty window. + +``` {.c source=examples/application1/examplewin.c } +#include + +#include "exampleapp.h" +#include "exampleappwin.h" + +struct _ExampleAppWindow +{ + GtkApplicationWindow parent; +}; + +G_DEFINE_TYPE(ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW); + +static void +example_app_window_init (ExampleAppWindow *app) +{ +} + +static void +example_app_window_class_init (ExampleAppWindowClass *class) +{ +} + +ExampleAppWindow * +example_app_window_new (ExampleApp *app) +{ + return g_object_new (EXAMPLE_APP_WINDOW_TYPE, "application", app, NULL); +} + +void +example_app_window_open (ExampleAppWindow *win, + GFile *file) +{ +} +``` + +As part of the initial setup of our application, we also +create an icon and a desktop file. + +![An icon](exampleapp.png) + +``` { source=examples/application1/org.gtk.exampleapp.desktop } +[Desktop Entry] +Type=Application +Name=Example +Icon=exampleapp +StartupNotify=true +Exec=@bindir@/exampleapp +``` + +Note that `@bindir@` needs to be replaced with the actual path to the binary +before this desktop file can be used. + +Here is what we've achieved so far: + +![An application](getting-started-app1.png) + +This does not look very impressive yet, but our application is already +presenting itself on the session bus, it has single-instance semantics, +and it accepts files as commandline arguments. + +### Populating the window + +In this step, we use a GtkBuilder template to associate a +GtkBuilder ui file with our application window class. + +Our simple ui file gives the window a title, and puts a GtkStack +widget as the main content. + +``` { .xml source=examples/application2/window.ui } + + + + +``` + +To make use of this file in our application, we revisit our +GtkApplicationWindow subclass, and call +gtk_widget_class_set_template_from_resource() from the class init +function to set the ui file as template for this class. We also +add a call to gtk_widget_init_template() in the instance init +function to instantiate the template for each instance of our +class. + +``` + ... + +static void +example_app_window_init (ExampleAppWindow *win) +{ + gtk_widget_init_template (GTK_WIDGET (win)); +} + +static void +example_app_window_class_init (ExampleAppWindowClass *class) +{ + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), + "/org/gtk/exampleapp/window.ui"); +} + + ... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application2/exampleappwin.c)) + +You may have noticed that we used the `_from_resource()` variant of the function +that sets a template. Now we need to use +[GLib's resource functionality](https://developer.gnome.org/gio/stable/GResource.html) +to include the ui file in the binary. This is commonly done by listing all resources +in a `.gresource.xml` file, such as this: + +``` { .xml source=examples/application2/exampleapp.gresource.xml } + + + + window.ui + + +``` + +This file has to be converted into a C source file that will be compiled and linked +into the application together with the other source files. To do so, we use the +`glib-compile-resources` utility: + +``` +glib-compile-resources exampleapp.gresource.xml --target=resources.c --generate-source +``` + +Our application now looks like this: + +![The application](getting-started-app2.png) + +### Opening files + +In this step, we make our application show the content of all the files +that it is given on the commandline. + +To this end, we add a member to the struct of our application window subclass +and keep a reference to the GtkStack there. The first member of the struct +should be the parent type from which the class is derived. Here, +ExampleAppWindow is derived from GtkApplicationWindow. The +gtk_widget_class_bind_template_child() function arranges things so that after +instantiating the template, the `stack` member of the struct will point to the +widget of the same name from the template. + +``` +... + +struct _ExampleAppWindow +{ + GtkApplicationWindow parent; + + GtkWidget *stack; +}; + +G_DEFINE_TYPE (ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW) + +... + +static void +example_app_window_class_init (ExampleAppWindowClass *class) +{ + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), + "/org/gtk/exampleapp/window.ui"); + gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack); +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c)) + +Now we revisit the example_app_window_open() function that is called for each +commandline argument, and construct a GtkTextView that we then add as a page +to the stack: + +``` +... + +void +example_app_window_open (ExampleAppWindow *win, + GFile *file) +{ + char *basename; + GtkWidget *scrolled, *view; + char *contents; + gsize length; + + basename = g_file_get_basename (file); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_hexpand (scrolled, TRUE); + gtk_widget_set_vexpand (scrolled, TRUE); + view = gtk_text_view_new (); + gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE); + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), view); + gtk_stack_add_titled (GTK_STACK (win->stack), scrolled, basename, basename); + + if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) + { + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + gtk_text_buffer_set_text (buffer, contents, length); + g_free (contents); + } + + g_free (basename); +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c)) + +Lastly, we add a GtkStackSwitcher to the titlebar area in the ui file, and we +tell it to display information about our stack. + +The stack switcher gets all its information it needs to display tabs from +the stack that it belongs to. Here, we are passing the label to show for +each file as the last argument to the gtk_stack_add_titled() function. + +Our application is beginning to take shape: + +![Application window](getting-started-app3.png) + +### A menu + +The menu is shown at the right side of the headerbar. It is meant to collect +infrequently used actions that affect the whole application. + +Just like the window template, we specify our menu in a ui file, and add it +as a resource to our binary. + +``` {.xml source=examples/application4/gears-menu.ui } + + +

+
+ + _Preferences + app.preferences + +
+
+ + _Quit + app.quit + +
+
+ +``` + +To make the menu appear, we have to load the ui file and associate the +resulting menu model with the menu button that we've added to the headerbar. +Since menus work by activating GActions, we also have to add a suitable set +of actions to our application. + +Adding the actions is best done in the startup() vfunc, which is guaranteed +to be called once for each primary application instance: + +``` +... + +static void +preferences_activated (GSimpleAction *action, + GVariant *parameter, + gpointer app) +{ +} + +static void +quit_activated (GSimpleAction *action, + GVariant *parameter, + gpointer app) +{ + g_application_quit (G_APPLICATION (app)); +} + +static GActionEntry app_entries[] = +{ + { "preferences", preferences_activated, NULL, NULL, NULL }, + { "quit", quit_activated, NULL, NULL, NULL } +}; + +static void +example_app_startup (GApplication *app) +{ + GtkBuilder *builder; + GMenuModel *app_menu; + const gchar *quit_accels[2] = { "<Ctrl>Q", NULL }; + + G_APPLICATION_CLASS (example_app_parent_class)->startup (app); + + g_action_map_add_action_entries (G_ACTION_MAP (app), + app_entries, G_N_ELEMENTS (app_entries), + app); + gtk_application_set_accels_for_action (GTK_APPLICATION (app), + "app.quit", + quit_accels); +} + +static void +example_app_class_init (ExampleAppClass *class) +{ + G_APPLICATION_CLASS (class)->startup = example_app_startup; + ... +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application4/exampleapp.c)) + +Our preferences menu item does not do anything yet, but the Quit menu item +is fully functional. Note that it can also be activated by the usual Ctrl-Q +shortcut. The shortcut was added with gtk_application_set_accels_for_action(). + +The application menu looks like this: + +![Application window](getting-started-app4.png) + +### A preference dialog + +A typical application will have a some preferences that should be remembered +from one run to the next. Even for our simple example application, we may +want to change the font that is used for the content. + +We are going to use GSettings to store our preferences. GSettings requires +a schema that describes our settings: + +``` {.xml source=examples/application5/org.gtk.exampleapp.gschema.xml } + + + + + 'Monospace 12' + Font + The font to be used for content. + + + + + + + + 'none' + Transition + The transition to use when switching tabs. + + + +``` + +Before we can make use of this schema in our application, we need to compile +it into the binary form that GSettings expects. GIO provides +[macros](https://developer.gnome.org/gio/2.36/ch31s06.html) to do this in +autotools-based projects. + +Next, we need to connect our settings to the widgets that they are supposed +to control. One convenient way to do this is to use GSettings bind +functionality to bind settings keys to object properties, as we do here +for the transition setting. + +``` +... + +static void +example_app_window_init (ExampleAppWindow *win) +{ + gtk_widget_init_template (GTK_WIDGET (win)); + win->settings = g_settings_new ("org.gtk.exampleapp"); + + g_settings_bind (win->settings, "transition", + win->stack, "transition-type", + G_SETTINGS_BIND_DEFAULT); +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application5/exampleappwin.c)) + +The code to connect the font setting is a little more involved, since there +is no simple object property that it corresponds to, so we are not going to +go into that here. + +At this point, the application will already react if you change one of the +settings, e.g. using the gsettings commandline tool. Of course, we expect +the application to provide a preference dialog for these. So lets do that +now. Our preference dialog will be a subclass of GtkDialog, and we'll use +the same techniques that we've already seen: templates, private structs, +settings bindings. + +Lets start with the template. + +``` {.xml source=examples/application6/prefs.ui } + + + + +``` + +Next comes the dialog subclass. + +``` {.c source=examples/application6/exampleappprefs.c } +#include + +#include "exampleapp.h" +#include "exampleappwin.h" +#include "exampleappprefs.h" + +struct _ExampleAppPrefs +{ + GtkDialog parent; + + GSettings *settings; + GtkWidget *font; + GtkWidget *transition; +}; + +G_DEFINE_TYPE (ExampleAppPrefs, example_app_prefs, GTK_TYPE_DIALOG) + +static void +example_app_prefs_init (ExampleAppPrefs *prefs) +{ + gtk_widget_init_template (GTK_WIDGET (prefs)); + prefs->settings = g_settings_new ("org.gtk.exampleapp"); + + g_settings_bind (prefs->settings, "font", + prefs->font, "font", + G_SETTINGS_BIND_DEFAULT); + g_settings_bind (prefs->settings, "transition", + prefs->transition, "active-id", + G_SETTINGS_BIND_DEFAULT); +} + +static void +example_app_prefs_dispose (GObject *object) +{ + ExampleAppPrefs *prefs; + + prefs = EXAMPLE_APP_PREFS (object); + + g_clear_object (&prefs->settings); + + G_OBJECT_CLASS (example_app_prefs_parent_class)->dispose (object); +} + +static void +example_app_prefs_class_init (ExampleAppPrefsClass *class) +{ + G_OBJECT_CLASS (class)->dispose = example_app_prefs_dispose; + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), + "/org/gtk/exampleapp/prefs.ui"); + gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppPrefs, font); + gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppPrefs, transition); +} + +ExampleAppPrefs * +example_app_prefs_new (ExampleAppWindow *win) +{ + return g_object_new (EXAMPLE_APP_PREFS_TYPE, "transient-for", win, "use-header-bar", TRUE, NULL); +} +``` + +Now we revisit the `preferences_activated()` function in our application +class, and make it open a new preference dialog. + +``` +... + +static void +preferences_activated (GSimpleAction *action, + GVariant *parameter, + gpointer app) +{ + ExampleAppPrefs *prefs; + GtkWindow *win; + + win = gtk_application_get_active_window (GTK_APPLICATION (app)); + prefs = example_app_prefs_new (EXAMPLE_APP_WINDOW (win)); + gtk_window_present (GTK_WINDOW (prefs)); +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application6/exampleapp.c)) + +After all this work, our application can now show a preference dialog +like this: + +![Preference dialog](getting-started-app6.png) + +### Adding a search bar + +We continue to flesh out the functionality of our application. For now, we +add search. GTK supports this with GtkSearchEntry and GtkSearchBar. The +search bar is a widget that can slide in from the top to present a search +entry. + +We add a toggle button to the header bar, which can be used to slide out +the search bar below the header bar. + +``` {.xml source=examples/application7/window.ui } + + + + +``` + +Implementing the search needs quite a few code changes that we are not +going to completely go over here. The central piece of the search +implementation is a signal handler that listens for text changes in +the search entry. + +``` +... + +static void +search_text_changed (GtkEntry *entry, + ExampleAppWindow *win) +{ + const gchar *text; + GtkWidget *tab; + GtkWidget *view; + GtkTextBuffer *buffer; + GtkTextIter start, match_start, match_end; + + text = gtk_editable_get_text (GTK_EDITABLE (entry)); + + if (text[0] == '\0') + return; + + tab = gtk_stack_get_visible_child (GTK_STACK (win->stack)); + view = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (tab)); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + /* Very simple-minded search implementation */ + gtk_text_buffer_get_start_iter (buffer, &start); + if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, + &match_start, &match_end, NULL)) + { + gtk_text_buffer_select_range (buffer, &match_start, &match_end); + gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start, + 0.0, FALSE, 0.0, 0.0); + } +} + +static void +example_app_window_init (ExampleAppWindow *win) +{ + +... + + gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed); + +... + +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application7/exampleappwin.c)) + +With the search bar, our application now looks like this: + +![A search bar](getting-started-app7.png) + +### Adding a side bar + +As another piece of functionality, we are adding a sidebar, which demonstrates +GtkMenuButton, GtkRevealer and GtkListBox. + +``` {.xml source=examples/application8/window.ui } + + + + +``` + +The code to populate the sidebar with buttons for the words found in each +file is a little too involved to go into here. But we'll look at the code +to add a checkbutton for the new feature to the menu. + +``` {.xml source=examples/application8/gears-menu.ui } + + + +
+ + _Words + win.show-words + + + _Preferences + app.preferences + +
+
+ + _Quit + app.quit + +
+
+
+``` + +To connect the menuitem to the show-words setting, we use +a GAction corresponding to the given GSettings key. + +``` +... + +static void +example_app_window_init (ExampleAppWindow *win) +{ + +... + + builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui"); + menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu")); + gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu); + g_object_unref (builder); + + action = g_settings_create_action (priv->settings, "show-words"); + g_action_map_add_action (G_ACTION_MAP (win), action); + g_object_unref (action); +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application8/exampleappwin.c)) + +What our application looks like now: + +![A sidebar](getting-started-app8.png) + +### Properties + +Widgets and other objects have many useful properties. + +Here we show some ways to use them in new and flexible ways, by wrapping +them in actions with GPropertyAction or by binding them with GBinding. + +To set this up, we add two labels to the header bar in our window template, +named `lines_label` and `lines`, and bind them to struct members in the +private struct, as we've seen a couple of times by now. + +We add a new "Lines" menu item to the gears menu, which triggers the +show-lines action: + +``` {.xml source=examples/application9/gears-menu.ui } + + + +
+ + _Words + win.show-words + + + _Lines + win.show-lines + + + _Preferences + app.preferences + +
+
+ + _Quit + app.quit + +
+
+
+``` + +To make this menu item do something, we create a property action for the +visible property of the `lines` label, and add it to the actions of the +window. The effect of this is that the visibility of the label gets toggled +every time the action is activated. + +Since we want both labels to appear and disappear together, we bind +the visible property of the `lines_label` widget to the same property +of the `lines` widget. + +``` +... + +static void +example_app_window_init (ExampleAppWindow *win) +{ + ... + + action = (GAction*) g_property_action_new ("show-lines", win->lines, "visible"); + g_action_map_add_action (G_ACTION_MAP (win), action); + g_object_unref (action); + + g_object_bind_property (win->lines, "visible", + win->lines_label, "visible", + G_BINDING_DEFAULT); +} + +... +``` +([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c)) + +We also need a function that counts the lines of the currently active tab, +and updates the `lines` label. See the [full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c) +if you are interested in the details. + +This brings our example application to this appearance: + +![Full application](getting-started-app9.png) diff --git a/docs/reference/gtk/getting_started.xml.in b/docs/reference/gtk/getting_started.xml.in deleted file mode 100644 index dba22cf0de..0000000000 --- a/docs/reference/gtk/getting_started.xml.in +++ /dev/null @@ -1,1048 +0,0 @@ - - - - Getting Started with GTK - - GTK is a - widget toolkit. Each user interface created by - GTK consists of widgets. This is implemented in C using - GObject, an object-oriented framework for C. - Widgets are organized in a hierarchy. The window widget is the main container. - The user interface is then built by adding buttons, drop-down menus, input - fields, and other widgets to the window. - If you are creating complex user interfaces it is recommended to - use GtkBuilder and its GTK-specific markup description language, instead of - assembling the interface manually. You can also use a visual user interface - editor, like Glade. - - GTK is event-driven. The toolkit listens for events such as - a click on a button, and passes the event to your application. - - This chapter contains some tutorial information to get you - started with GTK programming. It assumes that you have GTK, its - dependencies and a C compiler installed and ready to use. If you - need to build GTK itself first, refer to the - Compiling the GTK libraries - section in this reference. - -
- Basics - - To begin our introduction to GTK, we'll start with a very simple - application. This program will create an empty 200 × 200 pixel - window. - - - - - - - - - - - Create a new file with the following content named example-0.c. - MISSING XINCLUDE CONTENT - - - - You can compile the program above with GCC using: - - gcc `pkg-config --cflags gtk4` -o example-0 example-0.c `pkg-config --libs gtk4` - - - - For more information on how to compile a GTK application, please - refer to the Compiling GTK Applications - section in this reference. - - All GTK applications will, of course, include - gtk/gtk.h, which declares functions, types and - macros required by GTK applications. - - Even if GTK installs multiple header files, only the - top-level gtk/gtk.h header can be directly included - by third-party code. The compiler will abort with an error if any other - header is directly included. - - In a GTK application, the purpose of the main() function is - to create a GtkApplication object and run it. In this example a - GtkApplication pointer named app is declared - and then initialized using gtk_application_new(). - - When creating a GtkApplication, you need to pick an application - identifier (a name) and pass it to gtk_application_new() as parameter. - For this example org.gtk.example is used. For - choosing an identifier for your application, see - this guide. - Lastly gtk_application_new() takes GApplicationFlags as input for your - application, if your application would have special needs. - - - Next the - activate signal - is connected to the activate() function above the main() function. - The activate signal will be emitted when your application - is launched with g_application_run() on the line below. The g_application_run() - call also takes as arguments the command line arguments (the - argc count and the argv string array). - Your application can override the command line handling, e.g. to open - files passed on the commandline. - - - Within g_application_run() the activate signal is sent and we then - proceed into the activate() function of the application. This is where we - construct our GTK window, so that a window is shown when the application - is launched. The call to gtk_application_window_new() will create a new - GtkWindow and store it inside the window pointer. The - window will have a frame, a title bar, and window controls depending on - the platform. - - A window title is set using gtk_window_set_title(). This function - takes a GtkWindow* pointer and a string as input. As our - window pointer is a GtkWidget pointer, we need to cast - it to GtkWindow*. But instead of casting window via - (GtkWindow*), window can be cast - using the macro GTK_WINDOW(). GTK_WINDOW() - will check if the pointer is an instance of the GtkWindow class, before - casting, and emit a warning if the check fails. More information about - this convention can be found - - here. - - Finally the window size is set using gtk_window_set_default_size() - and the window is then shown by GTK via gtk_widget_show(). - - When you close the window, by for example pressing the X, the - g_application_run() call returns with a number which is saved inside - an integer variable named status. Afterwards, the - GtkApplication object is freed from memory with g_object_unref(). - Finally the status integer is returned and the application exits. - - While the program is running, GTK is receiving - events. These are typically input events caused by - the user interacting with your program, but also things like messages from - the window manager or other applications. GTK processes these and as a - result, signals may be emitted on your widgets. - Connecting handlers for these signals is how you normally make your - program do something in response to user input. - - The following example is slightly more complex, and tries to - showcase some of the capabilities of GTK. - -
-
- Hello, World - - In the long tradition of programming languages and libraries, - this example is called Hello, World. - - - - - - - - - - - Hello World in GTK - Create a new file with the following content named example-1.c. - - MISSING XINCLUDE CONTENT - - - - You can compile the program above with GCC using: - - gcc `pkg-config --cflags gtk4` -o example-1 example-1.c `pkg-config --libs gtk4` - - - - As seen above, example-1.c builds further upon example-0.c by adding a - button to our window, with the label "Hello World". Two new GtkWidget pointers - are declared to accomplish this, button and - box. The box variable is created to store a GtkBox, which - is GTK's way of controlling the size and layout of buttons. - - - The GtkBox is created with gtk_box_new() which takes a GtkOrientation - enum as parameter. The buttons which this box will contain can either be layed - out horizontally or vertically. This does not matter in this particular case, - as we are dealing with only one button. After initializing box with the newly - created GtkBox, the code adds the box widget to the window widget using - gtk_window_set_child(). - - Next the button variable is initialized in similar manner. - gtk_button_new_with_label() is called which returns a GtkButton to be stored in - button. Afterwards button is added to - our box. - - - - Using g_signal_connect(), the button is connected to a function in our app called - print_hello(), so that when the button is clicked, GTK will call this function. - As the print_hello() function does not use any data as input, NULL is passed - to it. print_hello() calls g_print() with the string "Hello World" - which will print Hello World in a terminal if the GTK application was started - from one. - - After connecting print_hello(), another signal is connected to the "clicked" - state of the button using g_signal_connect_swapped(). This functions is similar to - a g_signal_connect() with the difference lying in how the callback function is - treated. g_signal_connect_swapped() allows you to specify what the callback - function should take as parameter by letting you pass it as data. In this case - the function being called back is gtk_window_destroy() and the window - pointer is passed to it. This has the effect that when the button is clicked, - the whole GTK window is destroyed. In contrast if a normal g_signal_connect() were used - to connect the "clicked" signal with gtk_window_destroy(), then the function - would be called on button (which would not go well, since - the function expects a GtkWindow as argument). - More information about creating buttons can be found - here. - - - The rest of the code in example-1.c is identical to example-0.c. The next - section will elaborate further on how to add several GtkWidgets to your GTK - application. -
- -
- Packing - - When creating an application, you'll want to put more than one widget - inside a window. When you do so, it becomes important to control how each widget - is positioned and sized. This is where packing comes in. - - GTK comes with a large variety of layout containers - whose purpose it is to control the layout of the child widgets that are - added to them. See for an overview. - - The following example shows how the GtkGrid container lets you - arrange several buttons: - - - - - - - - - - - Packing buttons - Create a new file with the following content named example-2.c. - MISSING XINCLUDE CONTENT - - - You can compile the program above with GCC using: - - gcc `pkg-config --cflags gtk4` -o example-2 example-2.c `pkg-config --libs gtk4` - - -
- -
- Custom Drawing - - Many widgets, like buttons, do all their drawing themselves. You - just tell them the label you want to see, and they figure out what font - to use, draw the button outline and focus rectangle, etc. Sometimes, it - is necessary to do some custom drawing. In that case, a GtkDrawingArea - might be the right widget to use. It offers a canvas on which you can - draw by connecting to the ::draw signal. - - - The contents of a widget often need to be partially or fully redrawn, - e.g. when another window is moved and uncovers part of the widget, or - when the window containing it is resized. It is also possible to explicitly - cause part or all of the widget to be redrawn, by calling - gtk_widget_queue_draw() or its variants. GTK takes care of most of the - details by providing a ready-to-use cairo context to the ::draw signal - handler. - - The following example shows a ::draw signal handler. It is a bit - more complicated than the previous examples, since it also demonstrates - input event handling by means of event controllers. - - - - - - - - - - - Drawing in response to input - Create a new file with the following content named example-4.c. - MISSING XINCLUDE CONTENT - - - You can compile the program above with GCC using: - - gcc `pkg-config --cflags gtk4` -o example-4 example-4.c `pkg-config --libs gtk4` - - -
- -
- Building user interfaces - - When constructing a more complicated user interface, with dozens - or hundreds of widgets, doing all the setup work in C code is - cumbersome, and making changes becomes next to impossible. - - Thankfully, GTK supports the separation of user interface - layout from your business logic, by using UI descriptions in an - XML format that can be parsed by the GtkBuilder class. - - - Packing buttons with GtkBuilder - Create a new file with the following content named example-3.c. - MISSING XINCLUDE CONTENT - Create a new file with the following content named builder.ui. - MISSING XINCLUDE CONTENT - - - You can compile the program above with GCC using: - - gcc `pkg-config --cflags gtk4` -o example-3 example-3.c `pkg-config --libs gtk4` - - - - Note that GtkBuilder can also be used to construct objects - that are not widgets, such as tree models, adjustments, etc. - That is the reason the method we use here is called - gtk_builder_get_object() and returns a GObject* instead of a - GtkWidget*. - - Normally, you would pass a full path to - gtk_builder_add_from_file() to make the execution of your program - independent of the current directory. A common location to install - UI descriptions and similar data is - /usr/share/appname. - - - It is also possible to embed the UI description in the source - code as a string and use gtk_builder_add_from_string() to load it. - But keeping the UI description in a separate file has several - advantages: It is then possible to make minor adjustments to the UI - without recompiling your program, and, more importantly, graphical - UI editors such as glade - can load the file and allow you to create and modify your UI by - point-and-click. -
- -
- Building applications - - An application consists of a number of files: - - - The binary - This gets installed in /usr/bin. - - - A desktop file - The desktop file provides important information about the application to the desktop shell, such as its name, icon, D-Bus name, commandline to launch it, etc. It is installed in /usr/share/applications. - - - An icon - The icon gets installed in /usr/share/icons/hicolor/48x48/apps, where it will be found regardless of the current theme. - - - A settings schema - If the application uses GSettings, it will install its schema - in /usr/share/glib-2.0/schemas, so that tools - like dconf-editor can find it. - - - Other resources - Other files, such as GtkBuilder ui files, are best loaded from - resources stored in the application binary itself. This eliminates the - need for most of the files that would traditionally be installed in - an application-specific location in /usr/share. - - - - - GTK includes application support that is built on top of - GApplication. In this tutorial we'll build a simple application by - starting from scratch, adding more and more pieces over time. Along - the way, we'll learn about GtkApplication, templates, resources, - application menus, settings, GtkHeaderBar, GtkStack, GtkSearchBar, - GtkListBox, and more. - - The full, buildable sources for these examples can be found in the - examples/ directory of the GTK source distribution, or - online in the GTK git repository. - You can build each example separately by using make with the Makefile.example - file. For more information, see the README included in the - examples directory. - -
- A trivial application - - When using GtkApplication, the main() function can be very - simple. We just call g_application_run() and give it an instance - of our application class. - - - MISSING XINCLUDE CONTENT - - - All the application logic is in the application class, which - is a subclass of GtkApplication. Our example does not yet have any - interesting functionality. All it does is open a window when it is - activated without arguments, and open the files it is given, if it - is started with arguments. - - To handle these two cases, we override the activate() vfunc, - which gets called when the application is launched without commandline - arguments, and the open() vfunc, which gets called when the application - is launched with commandline arguments. - - To learn more about GApplication entry points, consult the - GIO documentation. - - - MISSING XINCLUDE CONTENT - - - Another important class that is part of the application support - in GTK is GtkApplicationWindow. It is typically subclassed as well. - Our subclass does not do anything yet, so we will just get an empty - window. - - - MISSING XINCLUDE CONTENT - - - As part of the initial setup of our application, we also - create an icon and a desktop file. - - - - - - - - - - - MISSING XINCLUDE CONTENT - - - Note that @bindir@ needs to be replaced - with the actual path to the binary before this desktop file can be used. - - Here is what we've achieved so far: - - - - - - - - - - This does not look very impressive yet, but our application - is already presenting itself on the session bus, it has single-instance - semantics, and it accepts files as commandline arguments. -
- -
- Populating the window - - In this step, we use a GtkBuilder template to associate a - GtkBuilder ui file with our application window class. - Our simple ui file gives the window a title, and puts a GtkStack - widget as the main content. - - - - MISSING XINCLUDE CONTENT - - - To make use of this file in our application, we revisit - our GtkApplicationWindow subclass, and call - gtk_widget_class_set_template_from_resource() from the class init - function to set the ui file as template for this class. We also - add a call to gtk_widget_init_template() in the instance init - function to instantiate the template for each instance of our - class. - - - - (full source) - - - You may have noticed that we used the _from_resource() - variant of the function that sets a template. Now we need to use - GLib's resource functionality - to include the ui file in the binary. This is commonly done by listing - all resources in a .gresource.xml file, such as this: - - - - MISSING XINCLUDE CONTENT - - - This file has to be converted into a C source file that will be - compiled and linked into the application together with the other source - files. To do so, we use the glib-compile-resources utility: - - - glib-compile-resources exampleapp.gresource.xml --target=resources.c --generate-source - - - Our application now looks like this: - - - - - - - - -
- -
- Opening files - - In this step, we make our application show the content of - all the files that it is given on the commandline. - - To this end, we add a member to the struct of our application - window subclass and keep a reference to the GtkStack there. The first - member of the struct should be the parent type from which the class is - derived. Here, ExampleAppWindow is derived from GtkApplicationWindow. - The gtk_widget_class_bind_template_child() function arranges things so - that after instantiating the template, the stack - member of the struct will point to the widget of the same name from - the template. - - - - (full source) - - - Now we revisit the example_app_window_open() function that - is called for each commandline argument, and construct a GtkTextView - that we then add as a page to the stack: - - - stack), scrolled, basename, basename); - - if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) - { - GtkTextBuffer *buffer; - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - gtk_text_buffer_set_text (buffer, contents, length); - g_free (contents); - } - - g_free (basename); -} - -... - ]]> - (full source) - - - Lastly, we add a GtkStackSwitcher to the titlebar area - in the ui file, and we tell it to display information about our - stack. - - The stack switcher gets all its information it needs to - display tabs from the stack that it belongs to. Here, we are - passing the label to show for each file as the last argument to - the gtk_stack_add_titled() function. - - Our application is beginning to take shape: - - - - - - - - -
- -
- A menu - - The menu is shown at the right side of the headerbar. - It is meant to collect infrequently used actions that affect - the whole application. - - Just like the window template, we specify our menu - in a ui file, and add it as a resource to our binary. - - - MISSING XINCLUDE CONTENT - - - To make the menu appear, we have to load the ui file and - associate the resulting menu model with the menu button that we've - added to the headerbar. Since menus work by activating GActions, - we also have to add a suitable set of actions to our application. - - Adding the actions is best done in the startup() vfunc, - which is guaranteed to be called once for each primary application - instance: - - -... - -static void -preferences_activated (GSimpleAction *action, - GVariant *parameter, - gpointer app) -{ -} - -static void -quit_activated (GSimpleAction *action, - GVariant *parameter, - gpointer app) -{ - g_application_quit (G_APPLICATION (app)); -} - -static GActionEntry app_entries[] = -{ - { "preferences", preferences_activated, NULL, NULL, NULL }, - { "quit", quit_activated, NULL, NULL, NULL } -}; - -static void -example_app_startup (GApplication *app) -{ - GtkBuilder *builder; - GMenuModel *app_menu; - const gchar *quit_accels[2] = { "<Ctrl>Q", NULL }; - - G_APPLICATION_CLASS (example_app_parent_class)->startup (app); - - g_action_map_add_action_entries (G_ACTION_MAP (app), - app_entries, G_N_ELEMENTS (app_entries), - app); - gtk_application_set_accels_for_action (GTK_APPLICATION (app), - "app.quit", - quit_accels); -} - -static void -example_app_class_init (ExampleAppClass *class) -{ - G_APPLICATION_CLASS (class)->startup = example_app_startup; - ... -} - -... - - (full source) - - - Our preferences menu item does not do anything yet, - but the Quit menu item is fully functional. Note that it - can also be activated by the usual Ctrl-Q shortcut. The - shortcut was added with gtk_application_set_accels_for_action(). - - - The application menu looks like this: - - - - - - - - -
- -
- A preference dialog - - A typical application will have a some preferences that - should be remembered from one run to the next. Even for our - simple example application, we may want to change the font - that is used for the content. - - We are going to use GSettings to store our preferences. - GSettings requires a schema that describes our settings: - - - MISSING XINCLUDE CONTENT - - - Before we can make use of this schema in our application, - we need to compile it into the binary form that GSettings - expects. GIO provides macros - to do this in autotools-based projects. - - Next, we need to connect our settings to the widgets - that they are supposed to control. One convenient way to do - this is to use GSettings bind functionality to bind settings - keys to object properties, as we do here for the transition - setting. - - - settings = g_settings_new ("org.gtk.exampleapp"); - - g_settings_bind (win->settings, "transition", - win->stack, "transition-type", - G_SETTINGS_BIND_DEFAULT); -} - -... - ]]> - (full source) - - - The code to connect the font setting is a little more involved, - since there is no simple object property that it corresponds to, so - we are not going to go into that here. - - At this point, the application will already react if you - change one of the settings, e.g. using the gsettings commandline - tool. Of course, we expect the application to provide a preference - dialog for these. So lets do that now. Our preference dialog will - be a subclass of GtkDialog, and we'll use the same techniques that - we've already seen: templates, private structs, settings - bindings. - - Lets start with the template. - - - MISSING XINCLUDE CONTENT - - - Next comes the dialog subclass. - - - MISSING XINCLUDE CONTENT - - - Now we revisit the preferences_activated() - function in our application class, and make it open a new preference - dialog. - - - - (full source) - - - After all this work, our application can now show - a preference dialog like this: - - - - - - - - -
- -
- Adding a search bar - - We continue to flesh out the functionality of our application. - For now, we add search. GTK supports this with GtkSearchEntry and - GtkSearchBar. The search bar is a widget that can slide in from the - top to present a search entry. - - We add a toggle button to the header bar, which can be used - to slide out the search bar below the header bar. - - - MISSING XINCLUDE CONTENT - - - Implementing the search needs quite a few code changes that - we are not going to completely go over here. The central piece of - the search implementation is a signal handler that listens for - text changes in the search entry. - - - stack)); - view = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (tab)); - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - /* Very simple-minded search implementation */ - gtk_text_buffer_get_start_iter (buffer, &start); - if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, - &match_start, &match_end, NULL)) - { - gtk_text_buffer_select_range (buffer, &match_start, &match_end); - gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start, - 0.0, FALSE, 0.0, 0.0); - } -} - -static void -example_app_window_init (ExampleAppWindow *win) -{ - -... - - gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed); - -... - -} - -... - ]]> - (full source) - - - With the search bar, our application now looks like this: - - - - - - - - -
- -
- Adding a side bar - - As another piece of functionality, we are adding a sidebar, - which demonstrates GtkMenuButton, GtkRevealer and GtkListBox. - - - MISSING XINCLUDE CONTENT - - - The code to populate the sidebar with buttons for the words - found in each file is a little too involved to go into here. But we'll - look at the code to add a checkbutton for the new feature to the menu. - - - MISSING XINCLUDE CONTENT - - - To connect the menuitem to the show-words setting, we use - a GAction corresponding to the given GSettings key. - - - gears), menu); - g_object_unref (builder); - - action = g_settings_create_action (priv->settings, "show-words"); - g_action_map_add_action (G_ACTION_MAP (win), action); - g_object_unref (action); -} - -... - ]]> - (full source) - - - What our application looks like now: - - - - - - - - -
-
- Properties - - Widgets and other objects have many useful properties. - - Here we show some ways to use them in new and flexible ways, - by wrapping them in actions with GPropertyAction or by binding them - with GBinding. - - To set this up, we add two labels to the header bar in our - window template, named lines_label and - lines, and bind them to struct members in the - private struct, as we've seen a couple of times by now. - - We add a new "Lines" menu item to the gears menu, which - triggers the show-lines action: - - - MISSING XINCLUDE CONTENT - - - To make this menu item do something, we create a property - action for the visible property of the lines label, - and add it to the actions of the window. The effect of this is that the - visibility of the label gets toggled every time the action is activated. - - - Since we want both labels to appear and disappear together, we bind - the visible property of the lines_label widget to the - same property of the lines widget. - - - -... - -static void -example_app_window_init (ExampleAppWindow *win) -{ - ... - - action = (GAction*) g_property_action_new ("show-lines", win->lines, "visible"); - g_action_map_add_action (G_ACTION_MAP (win), action); - g_object_unref (action); - - g_object_bind_property (win->lines, "visible", - win->lines_label, "visible", - G_BINDING_DEFAULT); -} - -... - - (full source) - - - We also need a function that counts the lines of the currently - active tab, and updates the lines label. See the - full source - if you are interested in the details. - - This brings our example application to this appearance: - - - - - - - - -
-
- -
diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 18dd293336..1d13375288 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -21,7 +21,7 @@ Introduction - + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index dadb199297..c3535b1e21 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -373,6 +373,7 @@ expand_content_files = [ ] expand_content_md_files = [ + 'getting_started.md', 'building.md', 'compiling.md', 'running.md', @@ -400,7 +401,6 @@ endif if get_option('gtk_doc') configure_file(input: 'version.xml.in', output: 'version.xml', configuration: version_conf) - configure_file(input: 'getting_started.xml.in', output: 'getting_started.xml', configuration: src_dir_conf) # gtk-markdown-to-docbook uses pandoc pandoc = find_program('pandoc', required: true) From 29fb9ae2d67683f9373629aa0b815192d7621797 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 10:55:02 -0400 Subject: [PATCH 08/18] docs: Convert platform sections to markdown --- docs/reference/gtk/broadway.md | 36 +++++++++ docs/reference/gtk/broadway.xml | 71 ----------------- docs/reference/gtk/meson.build | 10 +-- docs/reference/gtk/osx.md | 10 +++ docs/reference/gtk/osx.xml | 39 ---------- docs/reference/gtk/wayland.md | 10 +++ docs/reference/gtk/wayland.xml | 39 ---------- docs/reference/gtk/windows.md | 46 +++++++++++ docs/reference/gtk/windows.xml | 93 ---------------------- docs/reference/gtk/x11.md | 66 ++++++++++++++++ docs/reference/gtk/x11.xml | 132 -------------------------------- 11 files changed, 173 insertions(+), 379 deletions(-) create mode 100644 docs/reference/gtk/broadway.md delete mode 100644 docs/reference/gtk/broadway.xml create mode 100644 docs/reference/gtk/osx.md delete mode 100644 docs/reference/gtk/osx.xml create mode 100644 docs/reference/gtk/wayland.md delete mode 100644 docs/reference/gtk/wayland.xml create mode 100644 docs/reference/gtk/windows.md delete mode 100644 docs/reference/gtk/windows.xml create mode 100644 docs/reference/gtk/x11.md delete mode 100644 docs/reference/gtk/x11.xml diff --git a/docs/reference/gtk/broadway.md b/docs/reference/gtk/broadway.md new file mode 100644 index 0000000000..53d6d3d334 --- /dev/null +++ b/docs/reference/gtk/broadway.md @@ -0,0 +1,36 @@ +# Using GTK with Broadway {#gtk-broadway} + +The GDK Broadway backend provides support for displaying GTK +applications in a web browser, using HTML5 and web sockets. To run +your application in this way, select the Broadway backend by setting +`GDK_BACKEND=broadway`. Then you can make your application appear in +a web browser by pointing it at `http://127.0.0.1:8080`. Note that +you need to enable web sockets in your web browser. + +You can choose a different port from the default 8080 by setting +the `BROADWAY_DISPLAY` environment variable to the port that you +want to use. + +It is also possible to use multiple GTK applications in the same +web browser window, by using the Broadway server, `broadwayd`, that +ships with GTK. To use broadwayd, start it like this: + +``` +broadwayd :5 +``` + +Then point your web browser at `http://127.0.0.1:8085`. +Start your applications like this: + +``` +GDK_BACKEND=broadway BROADWAY_DISPLAY=:5 gtk4-demo +``` + +## Broadway-specific environment variables {#broadway-envar} + +### BROADWAY_DISPLAY + +Specifies the Broadway display number. The default display is 0. +The display number determines the port to use when connecting +to a Broadway application via the following formula: +`port = 8080 + display` diff --git a/docs/reference/gtk/broadway.xml b/docs/reference/gtk/broadway.xml deleted file mode 100644 index 4195e0e4ec..0000000000 --- a/docs/reference/gtk/broadway.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - -Using GTK with Broadway -3 -GTK Library - - - -Using GTK with Broadway - -HTML-specific aspects of using GTK - - - - -Using GTK with Broadway - - -The GDK Broadway backend provides support for displaying GTK -applications in a web browser, using HTML5 and web sockets. To run -your application in this way, select the Broadway backend by setting -GDK_BACKEND=broadway. Then you can make -your application appear in a web browser by pointing it at -http://127.0.0.1:8080. Note that you need -to enable web sockets in your web browser. - - - -You can choose a different port from the default 8080 by setting -the BROADWAY_DISPLAY environment variable to the -port that you want to use. - - - -It is also possible to use multiple GTK applications in the same -web browser window, by using the Broadway server, -broadwayd, that ships with GTK. -To use broadwayd, start it like this: - -broadwayd :5 - -Then point your web browser at http://127.0.0.1:8085. -Start your applications like this: - -GDK_BACKEND=broadway BROADWAY_DISPLAY=:5 gtk4-demo - - - - -Broadway-specific environment variables - - - - <envar>BROADWAY_DISPLAY</envar> - - - Specifies the Broadway display number. The default display is 0. - The display number determines the port to use when connecting - to a Broadway application via the following formula: - - port = 8080 + display - - - - - - diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index c3535b1e21..536d5a90f6 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -340,7 +340,6 @@ images = [ ] content_files = [ - 'broadway.xml', 'glossary.xml', 'gtk4-broadwayd.xml', 'gtk4-builder-tool.xml', @@ -352,7 +351,6 @@ content_files = [ 'gtk4-query-settings.xml', 'gtk4-update-icon-cache.xml', 'gtk4-widget-factory.xml', - 'osx.xml', 'other_software.xml', 'overview.xml', 'question_index.xml', @@ -360,9 +358,6 @@ content_files = [ 'text_widget.xml', 'tree_widget.xml', 'visual_index.xml', - 'wayland.xml', - 'windows.xml', - 'x11.xml', ] expand_content_files = [ @@ -373,6 +368,11 @@ expand_content_files = [ ] expand_content_md_files = [ + 'broadway.md', + 'osx.md', + 'wayland.md', + 'windows.md', + 'x11.md', 'getting_started.md', 'building.md', 'compiling.md', diff --git a/docs/reference/gtk/osx.md b/docs/reference/gtk/osx.md new file mode 100644 index 0000000000..2ee5c02bb1 --- /dev/null +++ b/docs/reference/gtk/osx.md @@ -0,0 +1,10 @@ +# Using GTK on Apple macOS {#gtk-osx} + +The Apple macOS port of GTK is an implementation of GDK (and therefore GTK) +on top of the Quartz API. + +Currently, the macOS port does not use any additional commandline options +or environment variables. + +For up-to-date information about the current status of this port, see the +[project page](https://wiki.gnome.org/Projects/GTK/OSX). diff --git a/docs/reference/gtk/osx.xml b/docs/reference/gtk/osx.xml deleted file mode 100644 index 306f5fe6f3..0000000000 --- a/docs/reference/gtk/osx.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - -Using GTK on Apple macOS -3 -GTK Library - - - -Using GTK on Apple macOS - -MacOS-specific aspects of using GTK - - - - -Using GTK on Apple macOS - - -The Apple macOS port of GTK is an implementation of GDK (and therefore GTK) -on top of the Quartz API. - - - -Currently, the macOS port does not use any additional commandline options -or environment variables. - - - -For up-to-date information about the current status of this port, see the -project page. - - - - - diff --git a/docs/reference/gtk/wayland.md b/docs/reference/gtk/wayland.md new file mode 100644 index 0000000000..7c3f708cfe --- /dev/null +++ b/docs/reference/gtk/wayland.md @@ -0,0 +1,10 @@ +# Using GTK with Wayland {#gtk-wayland} + +The GDK Wayland backend provides support for running GTK applications +under a Wayland compositor. To run your application in this way, select +the Wayland backend by setting `GDK_BACKEND=wayland`. + +On UNIX, the Wayland backend is enabled by default, so you don't need to +do anything special when compiling it, and everything should "just work." + +Currently, the Wayland backend does not use any additional environment variables. diff --git a/docs/reference/gtk/wayland.xml b/docs/reference/gtk/wayland.xml deleted file mode 100644 index 7d38342ebd..0000000000 --- a/docs/reference/gtk/wayland.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - -Using GTK with Wayland -3 -GTK Library - - - -Using GTK with Wayland - -Wayland-specific aspects of using GTK - - - - -Using GTK with Wayland - - -The GDK Wayland backend provides support for running GTK applications -under a Wayland compositor. To run your application in this way, select -the Wayland backend by setting GDK_BACKEND=wayland. - - - -On UNIX, the Wayland backend is enabled by default, so you don't need to -do anything special when compiling it, and everything should "just work." - - - -Currently, the Wayland backend does not use any additional environment variables. - - - - - diff --git a/docs/reference/gtk/windows.md b/docs/reference/gtk/windows.md new file mode 100644 index 0000000000..b432175094 --- /dev/null +++ b/docs/reference/gtk/windows.md @@ -0,0 +1,46 @@ +# Using GTK on Windows {#gtk-windows} + +The Windows port of GTK is an implementation of GDK (and therefore GTK) +on top of the Win32 API. When compiling GTK on Windows, this backend is +the default. + +More information about GTK on Windows, including detailed build +instructions, binary downloads, etc, can be found +[online](https://wiki.gnome.org/Projects/GTK/Win32). + +## Windows-specific environment variables {#win32-envar} + +The Win32 GDK backend can be influenced with some additional environment +variables. + +### GDK_IGNORE_WINTAB + +If this variable is set, GTK doesn't use the Wintab API for tablet support. + + +### GDK_USE_WINTAB + +If this variable is set, GTK uses the Wintab API for tablet support. +This is the default. + +## Windows-specific handling of cursors {#win32-cursors} + +By default the "system" cursor theme is used. This makes GTK prefer cursors +that Windows currently uses, falling back to Adwaita cursors and (as the last +resort) built-in X cursors. + +When any other cursor theme is used, GTK will prefer cursors from that theme, +falling back to Windows cursors and built-in X cursors. + +Theme can be changed by setting `gtk-cursor-theme-name` GTK setting. Users +can override GTK settings in the `settings.ini` file or at runtime in the +GTK Inspector. + +Themes are loaded from normal Windows variants of the XDG locations: +`%HOME%/icons/THEME/cursors`, +`%APPDATA%/icons/THEME/cursors`, +`RUNTIME_PREFIX/share/icons/THEME/cursors` + +The `gtk-cursor-theme-size`> setting is ignored, GTK will use +the cursor size that Windows tells it to use. + diff --git a/docs/reference/gtk/windows.xml b/docs/reference/gtk/windows.xml deleted file mode 100644 index ca25b9c892..0000000000 --- a/docs/reference/gtk/windows.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - -Using GTK on Windows -3 -GTK Library - - - -Using GTK on Windows - -Windows-specific aspects of using GTK - - - - -Using GTK on Windows - - -The Windows port of GTK is an implementation of GDK (and therefore GTK) -on top of the Win32 API. When compiling GTK on Windows, this backend is -the default. - - - -Windows-specific environment variables - - -The Win32 GDK backend can be influenced with some additional environment -variables. - - - -<envar>GDK_IGNORE_WINTAB</envar> - - -If this variable is set, GTK doesn't use the Wintab API for tablet support. - - - - -<envar>GDK_USE_WINTAB</envar> - - -If this variable is set, GTK uses the Wintab API for tablet support. -This is the default. - - - - - - -Windows-specific handling of cursors - - -By default the "system" cursor theme is used. This makes GTK prefer cursors -that Windows currently uses, falling back to Adwaita cursors and (as the last -resort) built-in X cursors. - - -When any other cursor theme is used, GTK will prefer cursors from that theme, -falling back to Windows cursors and built-in X cursors. - - -Theme can be changed by setting gtk-cursor-theme-name GTK -setting. Users can override GTK settings in the settings.ini -file or at runtime in the GTK Inspector. - - -Themes are loaded from normal Windows variants of the XDG locations: -%HOME%/icons/THEME/cursors, -%APPDATA%/icons/THEME/cursors, -RUNTIME_PREFIX/share/icons/THEME/cursors. - - -The gtk-cursor-theme-size setting is ignored, GTK will use -the cursor size that Windows tells it to use. - - - - - -More information about GTK on Windows, including detailed build -instructions, binary downloads, etc, can be found -online. - - - - - diff --git a/docs/reference/gtk/x11.md b/docs/reference/gtk/x11.md new file mode 100644 index 0000000000..be2b74dc8e --- /dev/null +++ b/docs/reference/gtk/x11.md @@ -0,0 +1,66 @@ +# GTK for the X Window System {#gtk-x11} + +On UNIX, the X backend is enabled by default, so you don't need to do anything +special when compiling it, and everything should "just work." + +To mix low-level Xlib routines into a GTK program, see +[GDK X Window System interaction](#gdk-X-Window-System-Interaction) +in the GDK manual. + +## X11-specific environment variables {#x11-envar} +: +The X11 GDK backend can be influenced with some additional environment variables. + +### GDK_SYNCHRONIZE + +If set, GDK makes all X requests synchronously. This is a useful +option for debugging, but it will slow down the performance considerably. + +### GDK_SCALE + +Must be set to an integer, typically 2. If set, GDK will scale all +windows by the specified factor. Scaled output is meant to be used on +high-dpi displays. Normally, GDK will pick up a suitable scale factor +for each monitor from the display system. This environment variable +allows to override that. + +## Understanding the X11 architecture {#gtk-X11-arch} + +People coming from a Windows or MacOS background often find certain +aspects of the X Window System surprising. This section introduces +some basic X concepts at a high level. For more details, the book most +people use is called the "Xlib Programming Manual" by Adrian Nye; this +book is volume one in the O'Reilly X Window System series. + +Standards are another important resource if you're poking in low-level +X11 details, in particular the ICCCM and the Extended Window Manager +Hints specifications. [freedesktop.org](http://www.freedesktop.org/standards/) +has links to many relevant specifications. + +The GDK manual covers [using Xlib in a GTK program](#gdk-X-Window-System-Interaction). + +### Server, client, window manager + +Other window systems typically put all their functionality in the +application itself. With X, each application involves three different +programs: the _X server_, the application (called a _client_ because +it's a client of the X server), and a special client called the +_window manager_. + +The X server is in charge of managing resources, processing drawing +requests, and dispatching events such as keyboard and mouse events to +interested applications. So client applications can ask the X server +to create a window, draw a circle, or move windows around. + +The window manager is in charge of rendering the frame or borders +around windows; it also has final say on the size of each window, +and window states such as minimized, maximized, and so forth. +On Windows and MacOS the application handles most of this. +On X11, if you wish to modify the window's state, or change its frame, +you must ask the window manager to do so on your behalf, using an +established [convention](http://www.freedesktop.org/standards/). + +GTK has functions for asking the window manager to do various things; +see for example gtk_window_minimize() or gtk_window_maximize(). +Keep in mind that most window managers *will* ignore certain requests +from time to time, in the interests of good user interface. diff --git a/docs/reference/gtk/x11.xml b/docs/reference/gtk/x11.xml deleted file mode 100644 index c2f73e444f..0000000000 --- a/docs/reference/gtk/x11.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - -Using GTK on the X Window System -3 -GTK Library - - - -Using GTK on the X Window System - -X11-specific aspects of using GTK - - - - -GTK for the X Window System - - -On UNIX, the X backend is enabled by default, so you don't need to do anything -special when compiling it, and everything should "just work." - - - -To mix low-level Xlib routines into a GTK program, see -GDK X Window System -interaction in the GDK manual. - - - -X11-specific environment variables - - -The X11 GDK backend can be influenced with some additional environment variables. - - - - <envar>GDK_SYNCHRONIZE</envar> - - - If set, GDK makes all X requests synchronously. This is a useful - option for debugging, but it will slow down the performance considerably. - - - - - <envar>GDK_SCALE</envar> - - - Must be set to an integer, typically 2. If set, GDK will scale all - windows by the specified factor. Scaled output is meant to be used on - high-dpi displays. Normally, GDK will pick up a suitable scale factor - for each monitor from the display system. This environment variable - allows to override that. - - - - - - - - -Understanding the X11 architecture - - -People coming from a Windows or MacOS background often find certain -aspects of the X Window System surprising. This section introduces -some basic X concepts at a high level. For more details, the book most -people use is called the Xlib Programming -Manual by Adrian Nye; this book is volume one in the -O'Reilly X Window System series. - - -Standards are another important resource if you're poking in low-level -X11 details, in particular the ICCCM and the Extended Window Manager -Hints specifications. freedesktop.org -has links to many relevant specifications. - - -The GDK manual covers using Xlib in a GTK -program. - - - -Server, client, window manager - - -Other window systems typically put all their functionality in the -application itself. With X, each application involves three different -programs: the X server, the application (called -a client because it's a client of the X -server), and a special client called the window -manager. - - - -The X server is in charge of managing resources, processing drawing -requests, and dispatching events such as keyboard and mouse events to -interested applications. So client applications can ask the X server -to create a window, draw a circle, or move windows around. - - - -The window manager is in charge of rendering the frame or borders -around windows; it also has final say on the size of each window, -and window states such as minimized, maximized, and so forth. -On Windows and MacOS the application handles most of this. -On X11, if you wish to modify the window's state, or change its frame, -you must ask the window manager to do so on your behalf, using an -established convention. - - - -GTK has functions for asking the window manager to do various things; -see for example gtk_window_minimize() or gtk_window_maximize(). -Keep in mind that most window managers will ignore -certain requests from time to time, in the interests of good user interface. - - - - - - - From f44d611f77cbf792439c2d07b52ab98842f9a811 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 10:57:52 -0400 Subject: [PATCH 09/18] docs: Drop a long-stale file The other_software.xml section has not been included in the docs since 2.x, as far as I can tell. It was clearly written before GIO existed. --- docs/reference/gtk/meson.build | 1 - docs/reference/gtk/other_software.xml | 211 -------------------------- 2 files changed, 212 deletions(-) delete mode 100644 docs/reference/gtk/other_software.xml diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index 536d5a90f6..9f7ac480d4 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -351,7 +351,6 @@ content_files = [ 'gtk4-query-settings.xml', 'gtk4-update-icon-cache.xml', 'gtk4-widget-factory.xml', - 'other_software.xml', 'overview.xml', 'question_index.xml', 'resources.xml', diff --git a/docs/reference/gtk/other_software.xml b/docs/reference/gtk/other_software.xml deleted file mode 100644 index fc10327e5d..0000000000 --- a/docs/reference/gtk/other_software.xml +++ /dev/null @@ -1,211 +0,0 @@ - - - - -Mixing GTK with other software -3 -Mixing GTK with other software - - - -Mixing GTK with other software - -How to combine GTK with other code and event loops - - - - -Overview - - -Often people want to use GTK in combination with another library or existing -body of code that is not GTK-aware. The general problem people encounter -is that the control flow of the other code does not return to GTK, so -widgets do not repaint, mouse and keyboard events are ignored, and so forth. - - - -This section describes some approaches to solving this problem. The most -suitable approach depends on the code that's involved, the platforms you're -targetting, and your own familiarity with each approach. - - - - - -Periodically yield to GTK main loop - - -This is the simplest method, but requires you to modify the non-GTK code. -Say you have a function that does some kind of lengthy task: - - - void - do_lengthy_task (void) - { - int i; - for (i = 0; i < BIG_NUMBER; ++i) - { - do_small_part_of_task (); - } - } - - -You simply insert code into this function that processes pending main loop tasks, if any: - - - void - do_lengthy_task (void) - { - int i; - for (i = 0; i < BIG_NUMBER; ++i) - { - do_small_part_of_task (); - - /* allow main loop to process pending events; NULL - * means the default context. - */ - while (g_main_context_pending (NULL)) - g_main_context_iteration (NULL, FALSE); - } - } - - - - - -The primary disadvantage of this approach is that you have to trade off UI -responsiveness and the performance of the task. That is, if -do_small_part_of_task() does very little of the task, you'll spend lots of CPU -time on g_main_context_iteration(). While if -do_small_part_of_task() does a lot of work, the GUI will seem noticeably -"chunky" to the user. - - - -Another disadvantage to this approach is that you can't have more than one -lengthy task at the same time, unless you manually integrate them. - - - -The big advantage of this approach is that it's simple and straightforward, and -works fine for simple applications such as tossing up a progress bar during the -lengthy task. - - - - - -Run the other code as a slave of the GTK main loop - - -As a slightly cleaner solution, you can ask the main loop to run a small part of your -task whenever it isn't busy — that is, when it's idle. -GLib provides a function g_idle_add() that's useful -for this. An "idle handler" added with g_idle_add() -will be run continuously as long as it returns TRUE. However, -the main loop gives higher priority to GUI-related tasks, so will run those instead -when appropriate. - - - -Here's a simple example: - - - gboolean - my_idle_handler (gpointer user_data) - { - do_small_part_of_task (); - - if (task_complete) - return G_SOURCE_REMOVE; /* removes the idle handler */ - else - return G_SOURCE_CONTINUE; /* runs the idle handler again */ - } - - g_idle_add (my_idle_handler, NULL); - - - - - -If your task involves reading data from the network, you should instead use -g_input_add(); this will allow the -main loop to sleep until data is available on a file descriptor, then -wake up to read that data. - - - -g_idle_add() returns a main loop source ID you can -use to remove the idle handler with g_source_remove(). -This is useful for cancelling a task, for example. Another approach is to keep a flag -variable and have the idle handler itself return FALSE when appropriate. - - - - - -Use multiple processes - - -If you can't break a task into small chunks — the -"do_small_part_of_task()" function in the above examples — you'll have to -separate your program into two parts, by spawning a child thread or process. -A process does not share the same address space (variables and data) with its parent. -A thread does share the same address space, so a change made to a variable in -one thread will be visible to other threads as well. - - - -This manual can't go into full detail on processes, threads, and other UNIX -programming topics. You may wish to get a book or two — two I'm familiar -with are Beginning Linux Programming (WROX Press) and Advanced Programming in -the UNIX Environment (by Richard Stevens. - - - -Those books also cover the central issue you'll need to address in order to have -a multi-process application: how to communicate between the processes. The -simplest solution is to use pipes; g_input_add() in combination with g_spawn_async_with_pipes() should make -this reasonably convenient. There are other possibilities, of course, such as -sockets, shared memory, and X Window System client message events, depending on -your needs. - - - - - - -Use multiple threads - - - - - - - - -Integrate the GTK main loop with another main loop - - - - - - - - -Things that won't work - - -signals - - - - - - From 5a3fe9e5f5dc9a4acc36d6a5bbec20c7db3b6a0b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 11:07:45 -0400 Subject: [PATCH 10/18] docs: Convert resources section to markdown --- docs/reference/gtk/meson.build | 4 +- .../gtk/{resources.xml => resources.md} | 55 +++---------------- 2 files changed, 11 insertions(+), 48 deletions(-) rename docs/reference/gtk/{resources.xml => resources.md} (52%) diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index 9f7ac480d4..6c2170a115 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -353,7 +353,6 @@ content_files = [ 'gtk4-widget-factory.xml', 'overview.xml', 'question_index.xml', - 'resources.xml', 'text_widget.xml', 'tree_widget.xml', 'visual_index.xml', @@ -371,8 +370,9 @@ expand_content_md_files = [ 'osx.md', 'wayland.md', 'windows.md', - 'x11.md', +: 'x11.md', 'getting_started.md', + 'resources.md', 'building.md', 'compiling.md', 'running.md', diff --git a/docs/reference/gtk/resources.xml b/docs/reference/gtk/resources.md similarity index 52% rename from docs/reference/gtk/resources.xml rename to docs/reference/gtk/resources.md index 1743708e2e..c2cc2d36a5 100644 --- a/docs/reference/gtk/resources.xml +++ b/docs/reference/gtk/resources.md @@ -1,76 +1,39 @@ - - - - -Contact information and bug reports -3 -Contact information and bug reports - +# Contact information and bug reports {#gtk-resources} - -Contact information and bug reports - -Getting help with GTK - - +## Opening a bug or feature request - -Opening a bug or feature request - - If you encounter a bug, misfeature, or missing feature in GTK, please -file a bug report on our -GitLab project. +file a bug report on our [GitLab project](https://gitlab.gnome.org/GNOME/gtk/issues/new). You should also file issues if the documentation is out of date with the existing API, or unclear. - - Don't hesitate to file a bug report, even if you think we may know about it already, or aren't sure of the details. Just give us as much information as you have, and if it's already fixed or has already been discussed, we'll add a note to that effect in the report. - - The issue tracker should definitely be used for feature requests, it's not only for bugs. We track all GTK development in GitLab, to ensure that nothing gets lost. - - +## Working on GTK - -Working on GTK - - If you develop a bugfix or enhancement for GTK, please open a merge request in GitLab as well. You should not attach patches to an issue, or describe the fix as a comment. Merge requests allow us to build GTK with your code applied, and run the test suite, on multiple platforms and architectures, and verify that nothing breaks. They also allow us to do proper code reviews, so we can iterate over the changes. - - -You should follow the contribution guide +You should follow the [contribution guide](https://gitlab.gnome.org/GNOME/gtk/blob/master/CONTRIBUTING.md) for GTK, available on GitLab. - - If you want to discuss your approach before or after working on it, good ways to contact the GTK developers, apart from GitLab issues, are - -the #gtk IRC channel on irc.gnome.org -the gtk tag on the GNOME Discourse instance - + +- the #gtk IRC channel on irc.gnome.org +- the gtk tag on the <[GNOME Discourse instance](https://discourse.gnome.org/tag/gtk) + You should not send patches by email, as they will inevitably get lost, or forgotten. Always open a merge request. - - - - - From a37b9d757854cbdac2ddbb86b703f45415e1cf69 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 11:12:56 -0400 Subject: [PATCH 11/18] docs: Drop the glossary It is very incomplete, somewhat outdated, and we lack the resources to maintain it properly. --- docs/reference/gtk/glossary.xml | 366 ------------------------------- docs/reference/gtk/gtk4-docs.xml | 2 - docs/reference/gtk/meson.build | 2 - 3 files changed, 370 deletions(-) delete mode 100644 docs/reference/gtk/glossary.xml diff --git a/docs/reference/gtk/glossary.xml b/docs/reference/gtk/glossary.xml deleted file mode 100644 index 38deb7341b..0000000000 --- a/docs/reference/gtk/glossary.xml +++ /dev/null @@ -1,366 +0,0 @@ - - - - Glossary - - - allocation - - - The final size of a widget within its parent. For example, a widget - may request a minimum size of 20×20 pixels, but its - parent may decide to allocate 50×20 pixels for it - instead. - - - requisition - - - - - - bin - - - A container that - can hold at most one child widget. The base class for bins is - #GtkBin. - - - container - - - - - - child - - - A container's child - is a widget contained - inside it. - - - - - - column - - - GTK contains several widgets which display data in columns, - e.g. the #GtkTreeView. - These view columns in - the tree view are represented by #GtkTreeViewColumn - objects inside GTK. They should not be confused with - model columns which - are used to organize the data in tree models. - - model-view widget - - - - - container - - - A widget that contains - other widgets; in that case, the container is the - parent of the child - widgets. Some containers don't draw anything on their own, - but rather just organize their children's geometry; for example, #GtkVBox lays out - its children vertically without painting anything on its own. Other - containers include decorative elements; for example, #GtkFrame contains - the frame's child and a label in addition to the shaded frame it draws. - The base class for containers is #GtkContainer. - - - widget - geometry - - - - - - display - - - GDK inherited the concept of display from the X window system, - which considers a display to be the combination - of a keyboard, a pointing device and one or more - screens. - Applications open a display to show windows and interact with the user. - In GDK, a display is represented by a #GdkDisplay. - - - - - - - - Ellipsization is the process of replacing some part - of a text by an ellipsis (usually "...") to make the - text fit in a smaller space. Pango can ellipsize text - at the beginning, at the end or in the middle. - - - - - - event - - - Events are the way in which GDK informs GTK about external events - like pointer motion, button clicks, key presses, etc. - - - - - - geometry - - - A widget's position - and size. Within its parent, this is called the widget's - allocation. - - - - - - mapping - - - This is the step in a widget's life cycle where it - actually shows the GdkSurfaces it created when it was - realized. When a - widget is mapped, it must turn on its - %GTK_MAPPED flag. - - - - Note that due to the asynchronous nature of the X window - system, a widget's window may not appear on the screen - immediatly after one calls gdk_surface_show(): - you must wait for the corresponding map event to be received. You can do - this with the GtkWidget::map-event - signal. - - - - - - model column - - - A column in a tree model, holding data of a certain type. - The types which can be stored in the columns of a model - have to be specified when the model is constructed, see - e.g. gtk_list_store_new(). - - - view column - - - - - - model-view widget - - - These widgets follow the well-known model-view pattern, which separates - the data (the model) to be displayed from the component which does the - actual visualization (the view). Examples of this pattern in GTK are - the #GtkTreeView/#GtkTreeModel and #GtkTextView/#GtkTextBuffer - - - One important advantage of this pattern is that it is possible to - display the same model in multiple views; another one that the - separation of the model allows a great deal of flexibility, as - demonstrated by e.g. #GtkTreeModelSort or #GtkTreeModelFilter. - - - - - - no-window widget - - - A widget that does not have a GdkSurface of its own on which to - draw its contents, but rather shares its parent's. This can be tested with - the gtk_widget_get_has_surface() function. - - - - - - parent - - - A widget's parent is - the container - inside which it resides. - - - - - - realization - - - This is the step in a widget's life cycle where it - creates its own GdkSurface, or otherwise associates itself with - its parent's - GdkSurface. If the widget has its own window, then it must - also attach a style to - it. A widget becomes unrealized by destroying its associated - GdkSurface. When a widget is realized, it must turn on its - %GTK_REALIZED flag. - - - - Widgets that don't own the GdkSurface on which they draw are - called no-window widgets. - This can be tested with the gtk_widget_get_has_surface() function. Normally, - these widgets draw on their parent's GdkSurface. - - - - Note that when a #GtkWidget creates a window in its #GtkWidget::realize - handler, it does not actually show the window. That is, the - window's structure is just created in memory. The widget - actually shows the window when it gets mapped. - - - - - - requisition - - - The size requisition of a widget is the minimum amount of - space it requests from its parent. Once the parent computes - the widget's final size, it gives it its size allocation. - - - allocation - - - - - - screen - - - GDK inherited the concept of screen from the X window system, - which considers a screen to be a rectangular area, on which - applications may place their windows. Screens under X may have - quite dissimilar visuals. - Each screen can stretch across multiple physical monitors. - - - In GDK, screens are represented by #GdkScreen objects. - - - - - - style - - - A style encapsulates what GTK needs to know in order to draw - a widget. Styles can be modified with - resource files. - - - - - - toplevel - - - A widget that does not - require a parent container. - The only toplevel widgets in GTK are #GtkWindow and widgets derived from it. - - - container - - - - - - unmap - mapping - - - - unrealize - realization - - - - view column - - - A displayed column in a tree view, represented by a - #GtkTreeViewColumn object. - - - model column - - - - - - visual - - - A visual describes how color information is stored in pixels. - A screen may support - multiple visuals. On modern hardware, the most common visuals - are truecolor visuals, which store a fixed number of bits - (typically 8) for the red, green and blue components of a color. - - - On ancient hardware, one may still meet indexed visuals, which - store color information as an index into a color map, or even - monochrome visuals. - - - - - - widget - - - A control in a graphical user interface. Widgets can draw - themselves and process events from the mouse and keyboard. - Widget types include buttons, menus, text entry lines, and - lists. Widgets can be arranged into containers, and these take - care of assigning the geometry of the widgets: every - widget thus has a parent except those widgets which are - toplevels. The base - class for widgets is #GtkWidget. - - - container - - - - diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 1d13375288..9e12315a03 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -428,8 +428,6 @@ - - Index of all symbols diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index 6c2170a115..cdeaeec609 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -340,7 +340,6 @@ images = [ ] content_files = [ - 'glossary.xml', 'gtk4-broadwayd.xml', 'gtk4-builder-tool.xml', 'gtk4-demo-application.xml', @@ -359,7 +358,6 @@ content_files = [ ] expand_content_files = [ - 'glossary.xml', 'question_index.xml', 'text_widget.xml', 'tree_widget.xml', From 581b39a38d803b0b0137eed0ef9a6bea80cda11b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 12:04:15 -0400 Subject: [PATCH 12/18] docs: Allow influencing pandoc divisons Arrange for files named section-foo.md to be turned into docbook sections, while others get turned into chapters. This is necessary to allow including such content in chapters, since chapters in docbook don't nest. --- docs/reference/gtk/gtk-markdown-to-docbook | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/reference/gtk/gtk-markdown-to-docbook b/docs/reference/gtk/gtk-markdown-to-docbook index 247c613dce..aa5dd53e61 100755 --- a/docs/reference/gtk/gtk-markdown-to-docbook +++ b/docs/reference/gtk/gtk-markdown-to-docbook @@ -167,12 +167,16 @@ MarkdownExtensions = { def ConvertToDocbook(infile, outfile): basename = os.path.basename(infile) + if basename.startswith('section'): + division='section' + else: + division='chapter' input_format = "markdown" + "".join(MarkdownExtensions) output_format = "docbook" subprocess.check_call(["pandoc", infile, "-o", outfile, "--from=" + input_format, "--to=" + output_format, - "--top-level-division=chapter"]) + "--top-level-division=" + division]) def ExpandGtkDocAbbreviations(infile, outfile): contents = open(infile, 'r', encoding='utf-8').read() From dc858c968675bc12534c719a380ec8803b5d22be Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 11:42:52 -0400 Subject: [PATCH 13/18] docs: Convert treeview and textview overview to markdown --- docs/reference/gtk/gtk4-docs.xml | 4 +- docs/reference/gtk/meson.build | 10 +- docs/reference/gtk/section-text-widget.md | 157 +++++++++++ docs/reference/gtk/section-tree-widget.md | 282 +++++++++++++++++++ docs/reference/gtk/text_widget.xml | 215 --------------- docs/reference/gtk/tree_widget.xml | 321 ---------------------- 6 files changed, 445 insertions(+), 544 deletions(-) create mode 100644 docs/reference/gtk/section-text-widget.md create mode 100644 docs/reference/gtk/section-tree-widget.md delete mode 100644 docs/reference/gtk/text_widget.xml delete mode 100644 docs/reference/gtk/tree_widget.xml diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 9e12315a03..6629c184ec 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -178,7 +178,7 @@ Multiline Text Editor - + @@ -189,7 +189,7 @@ Tree, List and Icon Grid Widgets - + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index cdeaeec609..e96c7b6fee 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -352,15 +352,11 @@ content_files = [ 'gtk4-widget-factory.xml', 'overview.xml', 'question_index.xml', - 'text_widget.xml', - 'tree_widget.xml', 'visual_index.xml', ] expand_content_files = [ 'question_index.xml', - 'text_widget.xml', - 'tree_widget.xml', ] expand_content_md_files = [ @@ -368,7 +364,7 @@ expand_content_md_files = [ 'osx.md', 'wayland.md', 'windows.md', -: 'x11.md', + 'x11.md', 'getting_started.md', 'resources.md', 'building.md', @@ -380,7 +376,9 @@ expand_content_md_files = [ 'input-handling.md', 'drawing-model.md', 'css-overview.md', - 'css-properties.md' + 'css-properties.md', + 'section-text-widget.md', + 'section-tree-widget.md', ] types_conf = configuration_data() diff --git a/docs/reference/gtk/section-text-widget.md b/docs/reference/gtk/section-text-widget.md new file mode 100644 index 0000000000..4184b88878 --- /dev/null +++ b/docs/reference/gtk/section-text-widget.md @@ -0,0 +1,157 @@ +# Text Widget Overview {#TextWidget} + +GTK has an extremely powerful framework for multiline text editing. The +primary objects involved in the process are #GtkTextBuffer, which represents the +text being edited, and #GtkTextView, a widget which can display a #GtkTextBuffer. +Each buffer can be displayed by any number of views. + +One of the important things to remember about text in GTK is that it's in +the UTF-8 encoding. This means that one character can be encoded as multiple +bytes. Character counts are usually referred to as _offsets_, while byte +counts are called _indexes_. If you confuse these two, things will work fine +with ASCII, but as soon as your buffer contains multibyte characters, bad +things will happen. + +Text in a buffer can be marked with _tags_. A tag is an attribute that can +be applied to some range of text. For example, a tag might be called "bold" +and make the text inside the tag bold. However, the tag concept is more +general than that; tags don't have to affect appearance. They can instead +affect the behavior of mouse and key presses, "lock" a range of text so the +user can't edit it, or countless other things. A tag is represented by a +#GtkTextTag object. One #GtkTextTag can be applied to any number of text +ranges in any number of buffers. + +Each tag is stored in a #GtkTextTagTable. A tag table defines a set of +tags that can be used together. Each buffer has one tag table associated with +it; only tags from that tag table can be used with the buffer. A single tag +table can be shared between multiple buffers, however. + +Tags can have names, which is convenient sometimes (for example, you can name +your tag that makes things bold "bold"), but they can also be anonymous (which +is convenient if you're creating tags on-the-fly). + +Most text manipulation is accomplished with _iterators_, represented by a +#GtkTextIter. An iterator represents a position between two characters in +the text buffer. #GtkTextIter is a struct designed to be allocated on the +stack; it's guaranteed to be copiable by value and never contain any +heap-allocated data. Iterators are not valid indefinitely; whenever the +buffer is modified in a way that affects the number of characters in the +buffer, all outstanding iterators become invalid. (Note that deleting 5 +characters and then reinserting 5 still invalidates iterators, though you +end up with the same number of characters you pass through a state with a +different number). + +Because of this, iterators can't be used to preserve positions across buffer +modifications. To preserve a position, the #GtkTextMark object is ideal. You +can think of a mark as an invisible cursor or insertion point; it floats in +the buffer, saving a position. If the text surrounding the mark is deleted, +the mark remains in the position the text once occupied; if text is inserted +at the mark, the mark ends up either to the left or to the right of the new +text, depending on its _gravity_. The standard text cursor in left-to-right +languages is a mark with right gravity, because it stays to the right of +inserted text. + +Like tags, marks can be either named or anonymous. There are two marks +built-in to #GtkTextBuffer; these are named "insert" and "selection_bound" +and refer to the insertion point and the boundary of the selection which +is not the insertion point, respectively. If no text is selected, these +two marks will be in the same position. You can manipulate what is selected +and where the cursor appears by moving these marks around. + +If you want to place the cursor in response to a user action, be sure to use +gtk_text_buffer_place_cursor(), which moves both at once without causing a +temporary selection (moving one then the other temporarily selects the range in +between the old and new positions). + +Text buffers always contain at least one line, but may be empty (that +is, buffers can contain zero characters). The last line in the text +buffer never ends in a line separator (such as newline); the other +lines in the buffer always end in a line separator. Line separators +count as characters when computing character counts and character +offsets. Note that some Unicode line separators are represented with +multiple bytes in UTF-8, and the two-character sequence "\r\n" is also +considered a line separator. + +Text buffers support undo and redo if gtk_text_buffer_set_enable_undo() +has been set to %TRUE. Use gtk_text_buffer_undo() or gtk_text_buffer_redo() +to perform the necessary action. Note that these operations are ignored if +the buffer is not editable. Developers may want some operations to not be +undoable. To do this, wrap your changes in +gtk_text_buffer_begin_irreversible_action() and +gtk_text_buffer_end_irreversible_action(). + +## Simple Example + +The simplest usage of #GtkTextView might look like this: + +``` {.c} +GtkWidget *view; +GtkTextBuffer *buffer; + +view = gtk_text_view_new (); + +buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + +gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); + +/* Now you might put the view in a container and display it on the + * screen; when the user edits the text, signals on the buffer + * will be emitted, such as "changed", "insert_text", and so on. + */ +``` + +In many cases it's also convenient to first create the buffer with +gtk_text_buffer_new(), then create a widget for that buffer with +gtk_text_view_new_with_buffer(). Or you can change the buffer the widget +displays after the widget is created with gtk_text_view_set_buffer(). + +## Example of Changing Text Attributes + +The way to affect text attributes in #GtkTextView is to +apply tags that change the attributes for a region of text. +For text features that come from the theme — such as font and +foreground color -- use CSS to override their default values. + +``` +GtkWidget *view; +GtkTextBuffer *buffer; +GtkTextIter start, end; +PangoFontDescription *font_desc; +GdkRGBA rgba; +GtkTextTag *tag; +GtkCssProvider *provider; +GtkStyleContext *context; + +view = gtk_text_view_new (); + +buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + +gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); + +/* Change default font and color throughout the widget */ +provider = gtk_css_provider_new (); +gtk_css_provider_load_from_data (provider, + "textview {" + " font: 15 serif;" + " color: green;" + "}", + -1); +context = gtk_widget_get_style_context (view); +gtk_style_context_add_provider (context, + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + +/* Change left margin throughout the widget */ +gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 30); + +/* Use a tag to change the color for just one part of the widget */ +tag = gtk_text_buffer_create_tag (buffer, "blue_foreground", + "foreground", "blue", + NULL); +gtk_text_buffer_get_iter_at_offset (buffer, &start, 7); +gtk_text_buffer_get_iter_at_offset (buffer, &end, 12); +gtk_text_buffer_apply_tag (buffer, tag, &start, &end); +``` + +The `gtk4-demo` application that comes with +GTK contains more example code for #GtkTextView. diff --git a/docs/reference/gtk/section-tree-widget.md b/docs/reference/gtk/section-tree-widget.md new file mode 100644 index 0000000000..b51cd3fb7a --- /dev/null +++ b/docs/reference/gtk/section-tree-widget.md @@ -0,0 +1,282 @@ +# Tree and List Widget Overview {#TreeWidget} + +To create a tree or list in GTK, use the #GtkTreeModel interface in +conjunction with the #GtkTreeView widget. This widget is designed around +a _Model/View/Controller_ design and consists of four major parts: + +- The tree view widget (GtkTreeView) +- The view column (GtkTreeViewColumn) +- The cell renderers (GtkCellRenderer etc.) +- The model interface (GtkTreeModel) + +The _View_ is composed of the first three objects, while the last is the +_Model_. One of the prime benefits of the MVC design is that multiple views +can be created of a single model. For example, a model mapping the file +system could be created for a file manager. Many views could be created +to display various parts of the file system, but only one copy need be +kept in memory. + +The purpose of the cell renderers is to provide extensibility to the +widget and to allow multiple ways of rendering the same type of data. +For example, consider how to render a boolean variable. Should it +render it as a string of "True" or "False", "On" or "Off", or should +it be rendered as a checkbox? + +## Creating a model + +GTK provides two simple models that can be used: the #GtkListStore +and the #GtkTreeStore. GtkListStore is used to model list widgets, +while the GtkTreeStore models trees. It is possible to develop a new +type of model, but the existing models should be satisfactory for all +but the most specialized of situations. Creating the model is quite + +``` {.c} +GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); +``` + +This creates a list store with two columns: a string column and a boolean +column. Typically the 2 is never passed directly like that; usually an +enum is created wherein the different columns are enumerated, followed by +a token that represents the total number of columns. The next example will +illustrate this, only using a tree store instead of a list store. Creating +a tree store operates almost exactly the same. + +``` {.c} +enum +{ + TITLE_COLUMN, + AUTHOR_COLUMN, + CHECKED_COLUMN, + N_COLUMNS +}; + +GtkTreeStore *store = gtk_tree_store_new (N_COLUMNS, /* Total number of columns */ + G_TYPE_STRING, /* Book title */ + G_TYPE_STRING, /* Author */ + G_TYPE_BOOLEAN); /* Is checked out? */ +``` + +Adding data to the model is done using gtk_tree_store_set() or +gtk_list_store_set(), depending upon which sort of model was +created. To do this, a #GtkTreeIter must be acquired. The iterator +points to the location where data will be added. + +Once an iterator has been acquired, gtk_tree_store_set() is used to +apply data to the part of the model that the iterator points to. +Consider the following example: + +``` {.c} +GtkTreeIter iter; + +gtk_tree_store_append (store, &iter, NULL); /* Acquire an iterator */ + +gtk_tree_store_set (store, &iter, + TITLE_COLUMN, "The Principle of Reason", + AUTHOR_COLUMN, "Martin Heidegger", + CHECKED_COLUMN, FALSE, + -1); +``` + +Notice that the last argument is -1. This is always done because +this is a variable-argument function and it needs to know when to stop +processing arguments. It can be used to set the data in any or all +columns in a given row. + +The third argument to gtk_tree_store_append() is the parent iterator. +It is used to add a row to a GtkTreeStore as a child of an existing row. +This means that the new row will only be visible when its parent is visible +and in its expanded state. Consider the following example: + +``` {.c} +GtkTreeIter iter1; /* Parent iter */ +GtkTreeIter iter2; /* Child iter */ + +gtk_tree_store_append (store, &iter1, NULL); /* Acquire a top-level iterator */ +gtk_tree_store_set (store, &iter1, + TITLE_COLUMN, "The Art of Computer Programming", + AUTHOR_COLUMN, "Donald E. Knuth", + CHECKED_COLUMN, FALSE, + -1); + +gtk_tree_store_append (store, &iter2, &iter1); /* Acquire a child iterator */ +gtk_tree_store_set (store, &iter2, + TITLE_COLUMN, "Volume 1: Fundamental Algorithms", + -1); + +gtk_tree_store_append (store, &iter2, &iter1); +gtk_tree_store_set (store, &iter2, + TITLE_COLUMN, "Volume 2: Seminumerical Algorithms", + -1); + +gtk_tree_store_append (store, &iter2, &iter1); +gtk_tree_store_set (store, &iter2, + TITLE_COLUMN, "Volume 3: Sorting and Searching", + -1); +``` + +## Creating the view component + +While there are several different models to choose from, there is +only one view widget to deal with. It works with either the list +or the tree store. Setting up a #GtkTreeView is not a difficult +matter. It needs a #GtkTreeModel to know where to retrieve its data +from. + +``` {.c} +GtkWidget *tree; + +tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); +``` + +## Colums and cell renderers + +Once the #GtkTreeView widget has a model, it will need to know how +to display the model. It does this with columns and cell renderers. + +Cell renderers are used to draw the data in the tree model in a +way. There are a number of cell renderers that come with GTK, +including the #GtkCellRendererText, #GtkCellRendererPixbuf and +the #GtkCellRendererToggle. It is relatively easy to write a +custom renderer. + +A #GtkTreeViewColumn is the object that GtkTreeView uses to organize +the vertical columns in the tree view. It needs to know the name of +the column to label for the user, what type of cell renderer to use, +and which piece of data to retrieve from the model for a given row. + +``` {.c} +GtkCellRenderer *renderer; +GtkTreeViewColumn *column; + +renderer = gtk_cell_renderer_text_new (); +column = gtk_tree_view_column_new_with_attributes ("Author", + renderer, + "text", AUTHOR_COLUMN, + NULL); +gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); +``` + +At this point, all the steps in creating a displayable tree have been +covered. The model is created, data is stored in it, a tree view is +created and columns are added to it. + +## Selection handling + +Most applications will need to not only deal with displaying data, +but also receiving input events from users. To do this, simply get +a reference to a selection object and connect to the +#GtkTreeSelection::changed signal. + +``` {.c} +/* Prototype for selection handler callback */ +static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data); + +/* Setup the selection handler */ +GtkTreeSelection *select; + +select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); +gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); +g_signal_connect (G_OBJECT (select), "changed", + G_CALLBACK (tree_selection_changed_cb), + NULL); +``` + +Then to retrieve data for the row selected: + +``` {.c} +static void +tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + gchar *author; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, AUTHOR_COLUMN, &author, -1); + + g_print ("You selected a book by %s\n", author); + + g_free (author); + } +} +``` + +## Simple Example + +Here is a simple example of using a #GtkTreeView widget in context +of the other widgets. It simply creates a simple model and view, +and puts them together. Note that the model is never populated +with data — that is left as an exercise for the reader. +More information can be found on this in the #GtkTreeModel section. + +``` {.c} +enum +{ + TITLE_COLUMN, + AUTHOR_COLUMN, + CHECKED_COLUMN, + N_COLUMNS +}; + +void +setup_tree (void) +{ + GtkTreeStore *store; + GtkWidget *tree; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + /* Create a model. We are using the store model for now, though we + * could use any other GtkTreeModel */ + store = gtk_tree_store_new (N_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN); + + /* custom function to fill the model with data */ + populate_tree_model (store); + + /* Create a view */ + tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + + /* The view now holds a reference. We can get rid of our own + * reference */ + g_object_unref (G_OBJECT (store)); + + /* Create a cell render and arbitrarily make it red for demonstration + * purposes */ + renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), + "foreground", "red", + NULL); + + /* Create a column, associating the "text" attribute of the + * cell_renderer to the first column of the model */ + column = gtk_tree_view_column_new_with_attributes ("Author", renderer, + "text", AUTHOR_COLUMN, + NULL); + + /* Add the column to the view. */ + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + /* Second column.. title of the book. */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Title", + renderer, + "text", TITLE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + /* Last column.. whether a book is checked out. */ + renderer = gtk_cell_renderer_toggle_new (); + column = gtk_tree_view_column_new_with_attributes ("Checked out", + renderer, + "active", CHECKED_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + /* Now we can manipulate the view just like any other GTK widget */ + ... +} +``` diff --git a/docs/reference/gtk/text_widget.xml b/docs/reference/gtk/text_widget.xml deleted file mode 100644 index bea104ac9d..0000000000 --- a/docs/reference/gtk/text_widget.xml +++ /dev/null @@ -1,215 +0,0 @@ - - - - -Text Widget Overview -3 -GTK Library - - - -Text Widget Overview -Overview of GtkTextBuffer, GtkTextView, and friends - - - -Conceptual Overview - - -GTK has an extremely powerful framework for multiline text editing. The -primary objects involved in the process are #GtkTextBuffer, which represents the -text being edited, and #GtkTextView, a widget which can display a #GtkTextBuffer. -Each buffer can be displayed by any number of views. - - - -One of the important things to remember about text in GTK is that it's in the -UTF-8 encoding. This means that one character can be encoded as multiple -bytes. Character counts are usually referred to as -offsets, while byte counts are called -indexes. If you confuse these two, things will work fine -with ASCII, but as soon as your buffer contains multibyte characters, bad -things will happen. - - - -Text in a buffer can be marked with tags. A tag is an -attribute that can be applied to some range of text. For example, a tag might -be called "bold" and make the text inside the tag bold. However, the tag -concept is more general than that; tags don't have to affect appearance. They -can instead affect the behavior of mouse and key presses, "lock" a range of -text so the user can't edit it, or countless other things. A tag is -represented by a #GtkTextTag object. One #GtkTextTag can be applied to any -number of text ranges in any number of buffers. - - - -Each tag is stored in a #GtkTextTagTable. A tag table defines a set of -tags that can be used together. Each buffer has one tag table associated with -it; only tags from that tag table can be used with the buffer. A single tag -table can be shared between multiple buffers, however. - - - -Tags can have names, which is convenient sometimes (for example, you can name -your tag that makes things bold "bold"), but they can also be anonymous (which -is convenient if you're creating tags on-the-fly). - - - -Most text manipulation is accomplished with iterators, -represented by a #GtkTextIter. An iterator represents a position between two -characters in the text buffer. #GtkTextIter is a struct designed to be -allocated on the stack; it's guaranteed to be copiable by value and never -contain any heap-allocated data. Iterators are not valid indefinitely; -whenever the buffer is modified in a way that affects the number of characters -in the buffer, all outstanding iterators become invalid. (Note that deleting -5 characters and then reinserting 5 still invalidates iterators, though you -end up with the same number of characters you pass through a state with a -different number). - - - -Because of this, iterators can't be used to preserve positions across buffer -modifications. To preserve a position, the #GtkTextMark object is ideal. You -can think of a mark as an invisible cursor or insertion point; it floats in -the buffer, saving a position. If the text surrounding the mark is deleted, -the mark remains in the position the text once occupied; if text is inserted -at the mark, the mark ends up either to the left or to the right of the new -text, depending on its gravity. The standard text -cursor in left-to-right languages is a mark with right gravity, because it -stays to the right of inserted text. - - - -Like tags, marks can be either named or anonymous. There are two marks built-in -to #GtkTextBuffer; these are named "insert" and -"selection_bound" and refer to the insertion point and the -boundary of the selection which is not the insertion point, respectively. If -no text is selected, these two marks will be in the same position. You can -manipulate what is selected and where the cursor appears by moving these -marks around. - - -If you want to place the cursor in response to a user action, be sure to use -gtk_text_buffer_place_cursor(), which moves both at once without causing a -temporary selection (moving one then the other temporarily selects the range in -between the old and new positions). - - - - - -Text buffers always contain at least one line, but may be empty (that -is, buffers can contain zero characters). The last line in the text -buffer never ends in a line separator (such as newline); the other -lines in the buffer always end in a line separator. Line separators -count as characters when computing character counts and character -offsets. Note that some Unicode line separators are represented with -multiple bytes in UTF-8, and the two-character sequence "\r\n" is also -considered a line separator. - - - -Text buffers support undo and redo if gtk_text_buffer_set_enable_undo() -has been set to %TRUE. Use gtk_text_buffer_undo() or gtk_text_buffer_redo() -to perform the necessary action. Note that these operations are ignored if -the buffer is not editable. Developers may want some operations to not be -undoable. To do this, wrap your changes in -gtk_text_buffer_begin_irreversible_action() and -gtk_text_buffer_end_irreversible_action(). - - - - - - -Simple Example - - -The simplest usage of #GtkTextView might look like this: - - GtkWidget *view; - GtkTextBuffer *buffer; - - view = gtk_text_view_new (); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); - - /* Now you might put the view in a container and display it on the - * screen; when the user edits the text, signals on the buffer - * will be emitted, such as "changed", "insert_text", and so on. - */ - -In many cases it's also convenient to first create the buffer with -gtk_text_buffer_new(), then create a widget for that buffer with -gtk_text_view_new_with_buffer(). Or you can change the buffer the widget -displays after the widget is created with gtk_text_view_set_buffer(). - - - - - -Example of Changing Text Attributes - - - -The way to affect text attributes in #GtkTextView is to -apply tags that change the attributes for a region of text. -For text features that come from the theme — such as font and -foreground color — use CSS to override their default values. - - - GtkWidget *view; - GtkTextBuffer *buffer; - GtkTextIter start, end; - PangoFontDescription *font_desc; - GdkRGBA rgba; - GtkTextTag *tag; - GtkCssProvider *provider; - GtkStyleContext *context; - - view = gtk_text_view_new (); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); - - /* Change default font and color throughout the widget */ - provider = gtk_css_provider_new (); - gtk_css_provider_load_from_data (provider, - "textview {" - " font: 15 serif;" - " color: green;" - "}", - -1); - context = gtk_widget_get_style_context (view); - gtk_style_context_add_provider (context, - GTK_STYLE_PROVIDER (provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - - /* Change left margin throughout the widget */ - gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 30); - - /* Use a tag to change the color for just one part of the widget */ - tag = gtk_text_buffer_create_tag (buffer, "blue_foreground", - "foreground", "blue", NULL); - gtk_text_buffer_get_iter_at_offset (buffer, &start, 7); - gtk_text_buffer_get_iter_at_offset (buffer, &end, 12); - gtk_text_buffer_apply_tag (buffer, tag, &start, &end); - - - - - -The gtk-demo application that comes with -GTK contains more example code for #GtkTextView. - - - - - diff --git a/docs/reference/gtk/tree_widget.xml b/docs/reference/gtk/tree_widget.xml deleted file mode 100644 index 1789b42bd3..0000000000 --- a/docs/reference/gtk/tree_widget.xml +++ /dev/null @@ -1,321 +0,0 @@ - - - - - Tree and List Widget Overview - 3 - GTK Library - - - - Tree and List Widget Overview - Overview of GtkTreeModel, GtkTreeView, and friends - - - - Overview - - To create a tree or list in GTK, use the #GtkTreeModel interface in - conjunction with the #GtkTreeView widget. This widget is - designed around a Model/View/Controller - design and consists of four major parts: - - The tree view widget (GtkTreeView) - The view column (GtkTreeViewColumn) - The cell renderers (GtkCellRenderer etc.) - The model interface (GtkTreeModel) - - The View is composed of the first three - objects, while the last is the Model. One - of the prime benefits of the MVC design is that multiple views - can be created of a single model. For example, a model mapping - the file system could be created for a file manager. Many views - could be created to display various parts of the file system, - but only one copy need be kept in memory. - - - The purpose of the cell renderers is to provide extensibility to the - widget and to allow multiple ways of rendering the same type of data. - For example, consider how to render a boolean variable. Should it - render it as a string of "True" or "False", "On" or "Off", or should - it be rendered as a checkbox? - - - - Creating a model - - GTK provides two simple models that can be used: the #GtkListStore - and the #GtkTreeStore. GtkListStore is used to model list widgets, - while the GtkTreeStore models trees. It is possible to develop a new - type of model, but the existing models should be satisfactory for all - but the most specialized of situations. Creating the model is quite - simple: - - - - This creates a list store with two columns: a string column and a boolean - column. Typically the 2 is never passed directly like that; usually an - enum is created wherein the different columns are enumerated, followed by - a token that represents the total number of columns. The next example will - illustrate this, only using a tree store instead of a list store. Creating - a tree store operates almost exactly the same. - - - - Adding data to the model is done using gtk_tree_store_set() or - gtk_list_store_set(), depending upon which sort of model was - created. To do this, a #GtkTreeIter must be acquired. The iterator - points to the location where data will be added. - - - Once an iterator has been acquired, gtk_tree_store_set() is used to - apply data to the part of the model that the iterator points to. - Consider the following example: - - - - - Notice that the last argument is -1. This is always done because - this is a variable-argument function and it needs to know when to stop - processing arguments. It can be used to set the data in any or all - columns in a given row. - - - The third argument to gtk_tree_store_append() is the parent iterator. It - is used to add a row to a GtkTreeStore as a child of an existing row. This - means that the new row will only be visible when its parent is visible and - in its expanded state. Consider the following example: - - - - - - Creating the view component - - While there are several different models to choose from, there is - only one view widget to deal with. It works with either the list - or the tree store. Setting up a #GtkTreeView is not a difficult - matter. It needs a #GtkTreeModel to know where to retrieve its data - from. - - - - - Columns and cell renderers - - Once the #GtkTreeView widget has a model, it will need to know how - to display the model. It does this with columns and cell renderers. - - - Cell renderers are used to draw the data in the tree model in a - way. There are a number of cell renderers that come with GTK, - including the #GtkCellRendererText, #GtkCellRendererPixbuf and - the #GtkCellRendererToggle. - It is relatively easy to write a custom renderer. - - - A #GtkTreeViewColumn is the object that GtkTreeView uses to organize - the vertical columns in the tree view. It needs to know the name of - the column to label for the user, what type of cell renderer to use, - and which piece of data to retrieve from the model for a given row. - - -GtkCellRenderer *renderer; -GtkTreeViewColumn *column; - -renderer = gtk_cell_renderer_text_new (); -column = gtk_tree_view_column_new_with_attributes ("Author", - renderer, - "text", AUTHOR_COLUMN, - NULL); -gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - - At this point, all the steps in creating a displayable tree have been - covered. The model is created, data is stored in it, a tree view is - created and columns are added to it. - - - - - Selection handling - - Most applications will need to not only deal with displaying data, but - also receiving input events from users. To do this, simply get a - reference to a selection object and connect to the - #GtkTreeSelection::changed signal. - - - - Then to retrieve data for the row selected: - - - - - - - Simple Example - - Here is a simple example of using a #GtkTreeView widget in context - of the other widgets. It simply creates a simple model and view, - and puts them together. Note that the model is never populated - with data — that is left as an exercise for the reader. - More information can be found on this in the #GtkTreeModel section. - -enum -{ - TITLE_COLUMN, - AUTHOR_COLUMN, - CHECKED_COLUMN, - N_COLUMNS -}; - -void -setup_tree (void) -{ - GtkTreeStore *store; - GtkWidget *tree; - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - - /* Create a model. We are using the store model for now, though we - * could use any other GtkTreeModel */ - store = gtk_tree_store_new (N_COLUMNS, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_BOOLEAN); - - /* custom function to fill the model with data */ - populate_tree_model (store); - - /* Create a view */ - tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); - - /* The view now holds a reference. We can get rid of our own - * reference */ - g_object_unref (G_OBJECT (store)); - - /* Create a cell render and arbitrarily make it red for demonstration - * purposes */ - renderer = gtk_cell_renderer_text_new (); - g_object_set (G_OBJECT (renderer), - "foreground", "red", - NULL); - - /* Create a column, associating the "text" attribute of the - * cell_renderer to the first column of the model */ - column = gtk_tree_view_column_new_with_attributes ("Author", renderer, - "text", AUTHOR_COLUMN, - NULL); - - /* Add the column to the view. */ - gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - /* Second column.. title of the book. */ - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Title", - renderer, - "text", TITLE_COLUMN, - NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - /* Last column.. whether a book is checked out. */ - renderer = gtk_cell_renderer_toggle_new (); - column = gtk_tree_view_column_new_with_attributes ("Checked out", - renderer, - "active", CHECKED_COLUMN, - NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - /* Now we can manipulate the view just like any other GTK widget */ - ... -} - - - - From 84437ee0e0443e648cc3575a5f438c358f125d95 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 14:20:42 -0400 Subject: [PATCH 14/18] docs: Allow interrupted lists We use such an interrupted, numbered list in the q&a section, so tell pandoc to pay attention to start numbers. --- docs/reference/gtk/gtk-markdown-to-docbook | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/gtk/gtk-markdown-to-docbook b/docs/reference/gtk/gtk-markdown-to-docbook index aa5dd53e61..5830d64d78 100755 --- a/docs/reference/gtk/gtk-markdown-to-docbook +++ b/docs/reference/gtk/gtk-markdown-to-docbook @@ -163,6 +163,7 @@ MarkdownExtensions = { '+backtick_code_blocks', # to replace |[ ]| '+fenced_code_attributes', # to add language annotations '-raw_html', # to escape literal tags like in input + '+startnum', # to have interrupted lists in the q&a part } def ConvertToDocbook(infile, outfile): From aca8090a096963329f0e66a35ab9b9c227cc2ec1 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 May 2020 14:25:25 -0400 Subject: [PATCH 15/18] docs: Convert question index to markdown With this, the expand_content_files list has been completely converted to markdown files. Whats left in content_files is man pages, and a few special cases. --- docs/reference/gtk/gtk4-docs.xml | 2 +- docs/reference/gtk/meson.build | 7 +- docs/reference/gtk/question_index.md | 557 +++++++++++++++ docs/reference/gtk/question_index.xml | 973 -------------------------- 4 files changed, 559 insertions(+), 980 deletions(-) create mode 100644 docs/reference/gtk/question_index.md delete mode 100644 docs/reference/gtk/question_index.xml diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 6629c184ec..2943a6286a 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -23,7 +23,7 @@ - + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index e96c7b6fee..07536264eb 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -351,14 +351,9 @@ content_files = [ 'gtk4-update-icon-cache.xml', 'gtk4-widget-factory.xml', 'overview.xml', - 'question_index.xml', 'visual_index.xml', ] -expand_content_files = [ - 'question_index.xml', -] - expand_content_md_files = [ 'broadway.md', 'osx.md', @@ -379,6 +374,7 @@ expand_content_md_files = [ 'css-properties.md', 'section-text-widget.md', 'section-tree-widget.md', + 'question_index.md', ] types_conf = configuration_data() @@ -438,7 +434,6 @@ if get_option('gtk_doc') '--extra-dir=../gsk', ], content_files: content_files + expand_md_targets, - expand_content_files: expand_content_files, html_assets: images, install: true) endif diff --git a/docs/reference/gtk/question_index.md b/docs/reference/gtk/question_index.md new file mode 100644 index 0000000000..dcc7d92c55 --- /dev/null +++ b/docs/reference/gtk/question_index.md @@ -0,0 +1,557 @@ +# Common Questions {#gtk-question-index} + +This is an "index" of the reference manual organized by common "How do +I..." questions. If you aren't sure which documentation to read for +the question you have, this list is a good place to start. + +## General Questions + +1. How do I get started with GTK? + + The GTK [website](https://www.gtk.org) offers some + [tutorials](https://www.gtk.org/documentation.php) and other documentation + (most of it about GTK 2.x and 3.x, but still somewhat applicable). This + reference manual also contains a introductory + [Getting Started](#gtk-getting-started) part. + + More documentation ranging from whitepapers to online books can be found at + the [GNOME developer's site](https://developer.gnome.org). After studying these + materials you should be well prepared to come back to this reference manual for details. + +2. Where can I get help with GTK, submit a bug report, or make a feature request? + + See the [documentation](#gtk-resources) on this topic. + +3. How do I port from one GTK version to another? + + See the [migration guide](#migrating). You may also find useful information in + the documentation for specific widgets and functions. If you have a question not + covered in the manual, feel free to ask, and please + [file a bug report](https://gitlab.gnome.org/GNOME/gtk/issues/new) against the + documentation. + +4. How does memory management work in GTK? Should I free data returned from functions? + + See the documentation for #GObject and #GInitiallyUnowned. For #GObject note + specifically g_object_ref() and g_object_unref(). #GInitiallyUnowned is a + subclass of #GObject so the same points apply, except that it has a "floating" + state (explained in its documentation). + + For strings returned from functions, they will be declared "const" if they should + not be freed. Non-const strings should be freed with g_free(). Arrays follow the + same rule. If you find an undocumented exception to the rules, please + [file a bug report.](https://gitlab.gnome.org/GNOME/gtk/issues/new). + + The transfer annotations for gobject-introspection that are part of the + documentation can provide useful hints for memory handling semantics as well. + +5. Why does my program leak memory, if I destroy a widget immediately + after creating it ? + + If `GtkFooi` isn't a toplevel window, then + + foo = gtk_foo_new (); + g_object_unref (foo); + + is a memory leak, because no one assumed the initial floating reference + (you will get a warning about this too). If you are using a widget and + you aren't immediately packing it into a container, then you probably + want standard reference counting, not floating reference counting. + + To get this, you must acquire a reference to the widget and drop the + floating reference (_ref and sink_ in GObject parlance) after creating it: + + foo = gtk_foo_new (); + g_object_ref_sink (foo); + + When you immediately add a widget to a container, it takes care of assuming + the initial floating reference and you don't have to worry about reference + counting at all ... just remove the widget from the container to get rid of it. + +6. How do I use GTK with threads? + + GTK requires that all GTK API calls are made from the same thread in which + gtk_init() was called (the _main thread_). + + If you want to take advantage of multi-threading in a GTK application, + it is usually best to send long-running tasks to worker threads, and feed + the results back to the main thread using g_idle_add() or GAsyncQueue. GIO + offers useful tools for such an approach such as GTask. + +7. How do I internationalize a GTK program? + + Most people use <[GNU gettext](https://www.gnu.org/software/gettext/), + already required in order to install GLib. On a UNIX or Linux system with + gettext installed, type `info gettext` to read the documentation. + + The short checklist on how to use gettext is: call bindtextdomain() so + gettext can find the files containing your translations, call textdomain() + to set the default translation domain, call bind_textdomain_codeset() to + request that all translated strings are returned in UTF-8, then call + gettext() to look up each string to be translated in the default domain. + + `gi18n.h` provides the following shorthand macros for convenience. + Conventionally, people define macros as follows for convenience: + + #define _(x) gettext (x) + #define N_(x) x + #define C_(ctx,x) pgettext (ctx, x) + + You use N_() (N stands for no-op) to mark a string for translation in + a location where a function call to gettext() is not allowed, such as + in an array initializer. You eventually have to call gettext() on the + string to actually fetch the translation. _() both marks the string for + translation and actually translates it. The C_() macro (C stands for + context) adds an additional context to the string that is marked for + translation, which can help to disambiguate short strings that might + need different translations in different parts of your program. + + Code using these macros ends up looking like this: + + #include <gi18n.h> + + static const char *global_variable = N_("Translate this string"); + + static void + make_widgets (void) + { + GtkWidget *label1; + GtkWidget *label2; + + label1 = gtk_label_new (_("Another string to translate")); + label2 = gtk_label_new (_(global_variable)); + ... + + Libraries using gettext should use dgettext() instead of gettext(), + which allows them to specify the translation domain each time they + ask for a translation. Libraries should also avoid calling textdomain(), + since they will be specifying the domain instead of using the default. + + With the convention that the macro `GETTEXT_PACKAGE` is defined to hold + your libraries translation domain, `gi18n-lib.h` can be included to provide + the following convenience: + + #define _(x) dgettext (GETTEXT_PACKAGE, x) + +8. How do I use non-ASCII characters in GTK programs ? + + GTK uses [Unicode](http://www.unicode.org) (more exactly UTF-8) for all text. + UTF-8 encodes each Unicode codepoint as a sequence of one to six bytes and + has a number of nice properties which make it a good choice for working with + Unicode text in C programs: + + - ASCII characters are encoded by their familiar ASCII codepoints. + - ASCII characters never appear as part of any other character. + - The zero byte doesn't occur as part of a character, so that UTF-8 + string can be manipulated with the usual C library functions for + handling zero-terminated strings. + + More information about Unicode and UTF-8 can be found in the + [UTF-8 and Unicode FAQ](https://www.cl.cam.ac.uk/~mgk25/unicode.html). + GLib provides functions for converting strings between UTF-8 and other + encodings, see g_locale_to_utf8() and g_convert(). + + Text coming from external sources (e.g. files or user input), has to be + converted to UTF-8 before being handed over to GTK. The following example + writes the content of a IS0-8859-1 encoded text file to `stdout`: + + char *text, *utf8_text; + gsize length; + GError *error = NULL; + + if (g_file_get_contents (filename, &text, &length, NULL)) + { + utf8_text = g_convert (text, length, "UTF-8", "ISO-8859-1", + NULL, NULL, &error); + if (error != NULL) + { + fprintf ("Couldn't convert file %s to UTF-8\n", filename); + g_error_free (error); + } + else + g_print (utf8_text); + } + else + fprintf (stderr, "Unable to read file %s\n", filename); + + For string literals in the source code, there are several alternatives + for handling non-ASCII content: + + - Direct UTF-8 + + If your editor and compiler are capable of handling UTF-8 encoded + sources, it is very convenient to simply use UTF-8 for string literals, + since it allows you to edit the strings in "wysiwyg". Note that choosing + this option may reduce the portability of your code. + + - Escaped UTF-8 + + Even if your toolchain can't handle UTF-8 directly, you can still + encode string literals in UTF-8 by using octal or hexadecimal escapes + like `\\212` or `\\xa8` to encode each byte. This is portable, but + modifying the escaped strings is not very convenient. Be careful when + mixing hexadecimal escapes with ordinary text; `"\\xa8abcd" is a string + of length 1 ! + + - Runtime conversion + + If the string literals can be represented in an encoding which your + toolchain can handle (e.g. IS0-8859-1), you can write your source + files in that encoding and use g_convert() to convert the strings + to UTF-8 at runtime. Note that this has some runtime overhead, so + you may want to move the conversion out of inner loops. + + Here is an example showing the three approaches using the copyright + sign © which has Unicode and ISO-8859-1 codepoint 169 and is represented + in UTF-8 by the two bytes 194, 169, or `"\\302\\251"` as a string literal: + + g_print ("direct UTF-8: ©"); + g_print ("escaped UTF-8: \302\251"); + text = g_convert ("runtime conversion: ©", -1, + "ISO-8859-1", "UTF-8", NULL, NULL, NULL); + g_print (text); + g_free (text); + + If you are using gettext() to localize your application, you need + to call bind_textdomain_codeset() to ensure that translated strings + are returned in UTF-8 encoding. + +9. How do I use GTK with C++? + + There are two ways to approach this. The GTK header files use the subset + of C that's also valid C++, so you can simply use the normal GTK API + in a C++ program. Alternatively, you can use a "C++ binding" such as + [gtkmm](https://www.gtkmm.org/) which provides a native C++ API. + + When using GTK directly, keep in mind that only functions can be + connected to signals, not methods. So you will need to use global + functions or "static" class functions for signal connections. + + Another common issue when using GTK directly is that C++ will not + implicitly convert an integer to an enumeration. This comes up when + using bitfields; in C you can write the following code: + + gdk_surface_set_events (gdk_surface, + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + while in C++ you must write: + + gdk_surface_set_events (gdk_surface, + (GdkEventMask) GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + There are very few functions that require this cast, however. + +10. How do I use GTK with other non-C languages? + + See the list of [language bindings](https://www.gtk.org/language-bindings.php) + on the GTK [website](https://www.gtk.org). + +11. How do I load an image or animation from a file? + + To load an image file straight into a display widget, use + gtk_image_new_from_file(). To load an image for another purpose, use + gdk_texture_new_from_file(). To load a video from a file, use + gtk_media_file_new_for_file(). + +12. How do I draw text? + + To draw a piece of text onto a cairo surface, use a Pango layout and + pango_cairo_show_layout(). + + layout = gtk_widget_create_pango_layout (widget, text); + fontdesc = pango_font_description_from_string ("Luxi Mono 12"); + pango_layout_set_font_description (layout, fontdesc); + pango_cairo_show_layout (cr, layout); + pango_font_description_free (fontdesc); + g_object_unref (layout); + + See also the [Cairo Rendering](https://developer.gnome.org/pango/stable/pango-Cairo-Rendering.html) + section of the [Pango documentation](https://developer.gnome.org/pango/stable/). + + To draw a piece of text in a widget snapshot() implementation, use + gtk_snapshot_append_layout(). + +13. How do I measure the size of a piece of text? + + To obtain the size of a piece of text, use a Pango layout and + pango_layout_get_pixel_size(), using code like the following: + + layout = gtk_widget_create_pango_layout (widget, text); + fontdesc = pango_font_description_from_string ("Luxi Mono 12"); + pango_layout_set_font_description (layout, fontdesc); + pango_layout_get_pixel_size (layout, &width, &height); + pango_font_description_free (fontdesc); + g_object_unref (layout); + + See also the [Layout Objects](https://developer.gnome.org/pango/stable/pango-Layout-Objects.html) + section of the [Pango documentation](https://developer.gnome.org/pango/stable/). + +14. Why are types not registered if I use their `GTK_TYPE_BLAH` macro? + + The %GTK_TYPE_BLAH macros are defined as calls to gtk_blah_get_type(), and + the `_get_type()` functions are declared as %G_GNUC_CONST which allows the + compiler to optimize the call away if it appears that the value is not + being used. + + GLib provides the g_type_ensure() function to work around this problem. + + g_type_ensure (GTK_TYPE_BLAH); + +15. How do I create a transparent toplevel window? + + Any toplevel window can be transparent. It is just a matter of setting a + transparent background in the CSS style for it. + +## Which widget should I use... + +16. ...for lists and trees? + + This question has different answers, depending on the size of the dataset + and the required formatting flexibility. + + If you want to display a large amount of data in a uniform way, your best + option is a #GtkTreeView widget. See the [tree widget overview](#TreeWidget). + A list is just a tree with no branches, so the treeview widget is used for + lists as well. + + If you want to display a small amount of items, but need flexible formatting + and widgetry inside the list, then you probably want to use a #GtkListBox, + which uses regular widgets for display. + +17. ...for multi-line text display or editing? + + See the [text widget overview](#TextWidget) -- you should use the + #GtkTextView widget. + + If you only have a small amount of text, #GtkLabel may also be appropriate + of course. It can be made selectable with gtk_label_set_selectable(). For a + single-line text entry, see #GtkEntry. + +18. ...to display an image or animation? + + GTK has two widgets that are dedicated to displaying images. #GtkImage, for + small, fixed-size icons and #GtkPicture for content images. + + Both can display images in just about any format GTK understands. + You can also use #GtkDrawingArea if you need to do something more complex, + such as draw text or graphics over the top of the image. + + Both GtkImage and GtkPicture can display animations and videos as well. + To show an webm file, load it with the GtkMediaFile API and then use + it as a paintable: + + mediafile = gtk_media_file_new_for_filename ("example.webm"); + picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (mediafile)); + +19. ...for presenting a set of mutually-exclusive choices, where Windows + would use a combo box? + + With GTK, a #GtkComboBox is the recommended widget to use for this use case. + If you need an editable text entry, use the #GtkComboBox:has-entry property. + +## Questions about GtkWidget + +20. How do I change the color of a widget? + + The background color of a widget is determined by the CSS style that applies + to it. To change that, you can set style classes on the widget, and provide + custom CSS to change the appearance. Such CSS can be loaded with + gtk_css_provider_load_from_file() and its variants. + See gtk_style_context_add_provider(). + +21. How do I change the font of a widget? + + If you want to make the text of a label larger, you can use + gtk_label_set_markup(): + + gtk_label_set_markup (label, "big tex"); + + This is preferred for many apps because it's a relative size to the + user's chosen font size. See g_markup_escape_text() if you are + constructing such strings on the fly. + + You can also change the font of a widget by putting + + .my-widget-class { + font: Sans 30; + } + + in a CSS file, loading it with gtk_css_provider_load_from_file(), and + adding the provider with gtk_style_context_add_provider_for_display(). + To associate this style information with your widget, set a style class + on its #GtkStyleContext using gtk_style_context_add_class(). The advantage + of this approach is that users can then override the font you have chosen. + See the #GtkStyleContext documentation for more discussion. + +22. How do I disable/ghost/desensitize a widget? + + In GTK a disabled widget is termed _insensitive_. + See gtk_widget_set_sensitive(). + +## GtkTextView questions + +23. How do I get the contents of the entire text widget as a string? + + See gtk_text_buffer_get_bounds() and gtk_text_buffer_get_text() + or gtk_text_iter_get_text(). + + GtkTextIter start, end; + GtkTextBuffer *buffer; + char *text; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + gtk_text_buffer_get_bounds (buffer, &start, &end); + text = gtk_text_iter_get_text (&start, &end); + /* use text */ + g_free (text); + +24. How do I make a text widget display its complete contents in a specific font? + + If you use gtk_text_buffer_insert_with_tags() with appropriate tags to + select the font, the inserted text will have the desired appearance, but + text typed in by the user before or after the tagged block will appear in + the default style. + + To ensure that all text has the desired appearance, use + gtk_widget_override_font() to change the default font for the widget. + +25. How do I make a text view scroll to the end of the buffer automatically ? + + A good way to keep a text buffer scrolled to the end is to place a + [mark](#GtkTextMark) at the end of the buffer, and give it right gravity. + The gravity has the effect that text inserted at the mark gets inserted + *before*, keeping the mark at the end. + + To ensure that the end of the buffer remains visible, use + gtk_text_view_scroll_to_mark() to scroll to the mark after + inserting new text. + + The gtk-demo application contains an example of this technique. + +## GtkTreeView questions + +26. How do I associate some data with a row in the tree? + + Remember that the #GtkTreeModel columns don't necessarily have to be + displayed. So you can put non-user-visible data in your model just + like any other data, and retrieve it with gtk_tree_model_get(). + See the [tree widget overview](#TreeWidget). + +27. How do I put an image and some text in the same column? + + You can pack more than one #GtkCellRenderer into a single #GtkTreeViewColumn + using gtk_tree_view_column_pack_start() or gtk_tree_view_column_pack_end(). + So pack both a #GtkCellRendererPixbuf and a #GtkCellRendererText into the + column. + +28. I can set data easily on my #GtkTreeStore/#GtkListStore models using + gtk_list_store_set() and gtk_tree_store_set(), but can't read it back? + + Both the #GtkTreeStore and the #GtkListStore implement the #GtkTreeModel + interface. Consequentially, you can use any function this interface + implements. The easiest way to read a set of data back is to use + gtk_tree_model_get(). + +29. How do I change the way that numbers are formatted by #GtkTreeView? + + Use gtk_tree_view_insert_column_with_data_func() or + gtk_tree_view_column_set_cell_data_func() and do the conversion + from number to string yourself (with, say, g_strdup_printf()). + + The following example demonstrates this: + + enum + { + DOUBLE_COLUMN, + N_COLUMNS + }; + + GtkListStore *mycolumns; + + GtkTreeView *treeview; + + void + my_cell_double_to_text (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) + { + GtkCellRendererText *cell_text = (GtkCellRendererText *)cell; + double d; + char *text; + + /* Get the double value from the model. */ + gtk_tree_model_get (tree_model, iter, (int)data, &d, -1); + /* Now we can format the value ourselves. */ + text = g_strdup_printf ("%.2f", d); + g_object_set (cell, "text", text, NULL); + g_free (text); + } + + void + set_up_new_columns (GtkTreeView *myview) + { + GtkCellRendererText *renderer; + GtkTreeViewColumn *column; + GtkListStore *mycolumns; + + /* Create the data model and associate it with the given TreeView */ + mycolumns = gtk_list_store_new (N_COLUMNS, G_TYPE_DOUBLE); + gtk_tree_view_set_model (myview, GTK_TREE_MODEL (mycolumns)); + + /* Create a GtkCellRendererText */ + renderer = gtk_cell_renderer_text_new (); + + /* Create a new column that has a title ("Example column"), + * uses the above created renderer that will render the double + * value into text from the associated model's rows. + */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, "Example column"); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + + /* Append the new column after the GtkTreeView's previous columns. */ + gtk_tree_view_append_column (GTK_TREE_VIEW (myview), column); + /* Since we created the column by hand, we can set it up for our + * needs, e.g. set its minimum and maximum width, etc. + */ + /* Set up a custom function that will be called when the column content + * is rendered. We use the func_data pointer as an index into our + * model. This is convenient when using multi column lists. + */ + gtk_tree_view_column_set_cell_data_func (column, renderer, + my_cell_double_to_text, + (gpointer)DOUBLE_COLUMN, NULL); + } + +30. How do I hide the expander arrows in my tree view? + + Set the expander-column property of the tree view to a hidden column. + See gtk_tree_view_set_expander_column() and gtk_tree_view_column_set_visible(). + +## Using cairo with GTK + +31. How do I use cairo to draw in GTK applications? + + Use gtk_snapshot_append_cairo() in your #GtkWidgetClass.snapshot() vfunc + to obtain a cairo context and draw with that. + +32. Can I improve the performance of my application by using another backend + of cairo (such as GL)? + + No. Most drawing in GTK is not done via cairo anymore (but instead + by the GL or Vulkan renderers of GSK). + + If you use cairo for drawing your own widgets, gtk_snapshot_append_cairo() + will choose the most appropriate surface type for you. + + If you are interested in using GL for your own drawing, see #GtkGLArea. + +33. Can I use cairo to draw on a #GdkPixbuf? + + No. The cairo image surface does not support the pixel format used by GdkPixbuf. + + If you need to get cairo drawing into a format that can be displayed efficiently + by GTK, you may want to use an image surface and gdk_memory_texture_new(). diff --git a/docs/reference/gtk/question_index.xml b/docs/reference/gtk/question_index.xml deleted file mode 100644 index c6da3c8f9a..0000000000 --- a/docs/reference/gtk/question_index.xml +++ /dev/null @@ -1,973 +0,0 @@ - - - - -Common Questions -3 -Common Questions - - - -Common Questions - -Find answers to common questions in the GTK manual - - - - -Questions and Answers - - -This is an "index" of the reference manual organized by common "How do -I..." questions. If you aren't sure which documentation to read for -the question you have, this list is a good place to start. - - - - -General - - - -How do I get started with GTK? - - - -The GTK website offers some -tutorials and other -documentation (most of it about GTK 2.x, but still somewhat applicable). This -reference manual also contains a introductory -Getting Started part. -More documentation ranging from whitepapers to online books can be found at -the GNOME developer's site. -After studying these materials you should be well prepared to come back to -this reference manual for details. - - - - - -Where can I get help with GTK, submit a bug report, or make a feature request? - - - - - -See the documentation on this topic. - - - - - - - - - -How do I port from one GTK version to another? - - - - - -See . -You may also find useful information in the documentation for specific widgets -and functions. If you have a question not covered in the manual, feel free to -ask, and please file a bug report -against the documentation. - - - - - - - - - -How does memory management work in GTK? Should I free data returned from functions? - - - - - -See the documentation for #GObject and #GInitiallyUnowned. For #GObject note -specifically g_object_ref() and g_object_unref(). #GInitiallyUnowned is a -subclass of #GObject so the same points apply, except that it has a "floating" -state (explained in its documentation). - - - -For strings returned from functions, they will be declared "const" -if they should not be freed. Non-const strings should be -freed with g_free(). Arrays follow the same rule. If you find an -undocumented exception to the rules, please -file a bug report. - - - -The transfer annotations for gobject-introspection that are part of the -documentation can provide useful hints for memory handling semantics as well. - - - - - - - - -Why does my program leak memory, if I destroy a widget immediately -after creating it ? - - - - - -If GtkFoo isn't a toplevel window, then - - foo = gtk_foo_new (); - g_object_unref (foo); - -is a memory leak, because no one assumed the initial floating reference -(you will get a warning about this too). If you are using a widget and -you aren't immediately packing it into a container, then you probably -want standard reference counting, not floating reference counting. - - - -To get this, you must acquire a reference to the widget and drop the -floating reference (ref and sink in GObject parlance) after -creating it: - - foo = gtk_foo_new (); - g_object_ref_sink (foo); - -When you immediately add a widget to a container, it takes care of assuming -the initial floating reference and you don't have to worry about reference -counting at all ... just remove the widget from the container to get rid of it. - - - - - - -How do I use GTK with threads? - - - - - -GTK requires that all GTK API calls are made from the same thread in which -gtk_init() was called (the main thread). - -If you want to take advantage of multi-threading in a GTK application, -it is usually best to send long-running tasks to worker threads, and feed -the results back to the main thread using g_idle_add() or GAsyncQueue. GIO -offers useful tools for such an approach such as GTask. - - - - - - - - -How do I internationalize a GTK program? - - - - -Most people use GNU -gettext, already required in order to install GLib. On a UNIX -or Linux system with gettext installed, type info gettext -to read the documentation. - - -The short checklist on how to use gettext is: call bindtextdomain() so -gettext can find the files containing your translations, call textdomain() -to set the default translation domain, call bind_textdomain_codeset() to -request that all translated strings are returned in UTF-8, then call -gettext() to look up each string to be translated in the default domain. - - -gi18n.h provides the following shorthand macros for -convenience. -Conventionally, people define macros as follows for convenience: - - - #define _(x) gettext (x) - #define N_(x) x - #define C_(ctx,x) pgettext (ctx, x) - - -You use N_() (N stands for no-op) to mark a string for translation in -a location where a function call to gettext() is not allowed, such as -in an array initializer. -You eventually have to call gettext() on the string to actually fetch -the translation. _() both marks the string for translation and actually -translates it. -The C_() macro (C stands for context) adds an additional context to -the string that is marked for translation, which can help to disambiguate -short strings that might need different translations in different -parts of your program. - - -Code using these macros ends up looking like this: - - - #include <gi18n.h> - - static const char *global_variable = N_("Translate this string"); - - static void - make_widgets (void) - { - GtkWidget *label1; - GtkWidget *label2; - - label1 = gtk_label_new (_("Another string to translate")); - label2 = gtk_label_new (_(global_variable)); -... - - - - -Libraries using gettext should use dgettext() instead of gettext(), which -allows them to specify the translation domain each time they ask for a -translation. Libraries should also avoid calling textdomain(), since -they will be specifying the domain instead of using the default. - - -With the convention that the macro GETTEXT_PACKAGE is -defined to hold your libraries translation domain, -gi18n-lib.h can be included to provide -the following convenience: - - - #define _(x) dgettext (GETTEXT_PACKAGE, x) - - - - - - - - - -How do I use non-ASCII characters in GTK programs ? - - - - - -GTK uses Unicode (more exactly -UTF-8) for all text. UTF-8 encodes each Unicode codepoint as a sequence of -one to six bytes and has a number of nice properties which make it a good -choice for working with Unicode text in C programs: - - -ASCII characters are encoded by their familiar ASCII codepoints. - - -ASCII characters never appear as part of any other character. - - -The zero byte doesn't occur as part of a character, so that UTF-8 strings -can be manipulated with the usual C library functions for handling -zero-terminated strings. - - -More information about Unicode and UTF-8 can be found in the -UTF-8 and Unicode -FAQ for Unix/Linux. -GLib provides functions for converting strings between UTF-8 and other -encodings, see g_locale_to_utf8() and g_convert(). - - -Text coming from external sources (e.g. files or user input), has to be -converted to UTF-8 before being handed over to GTK. The following example -writes the content of a IS0-8859-1 encoded text file to -stdout: - -gchar *text, *utf8_text; -gsize length; -GError *error = NULL; - -if (g_file_get_contents (filename, &text, &length, NULL)) - { - utf8_text = g_convert (text, length, "UTF-8", "ISO-8859-1", - NULL, NULL, &error); - if (error != NULL) - { - fprintf ("Couldn't convert file %s to UTF-8\n", filename); - g_error_free (error); - } - else - g_print (utf8_text); - } -else - fprintf (stderr, "Unable to read file %s\n", filename); - - - -For string literals in the source code, there are several alternatives for -handling non-ASCII content: - -direct UTF-8 - -If your editor and compiler are capable of handling UTF-8 encoded sources, -it is very convenient to simply use UTF-8 for string literals, since it -allows you to edit the strings in "wysiwyg". Note that choosing this option -may reduce the portability of your code. - - - -escaped UTF-8 - -Even if your toolchain can't handle UTF-8 directly, you can still encode -string literals in UTF-8 by using octal or hexadecimal escapes like -\212 or \xa8 to encode each byte. -This is portable, but modifying the escaped strings is not very convenient. -Be careful when mixing hexadecimal escapes with ordinary text; -"\xa8abcd" is a string of length 1 ! - - - -runtime conversion - -If the string literals can be represented in an encoding which your -toolchain can handle (e.g. IS0-8859-1), you can write your source files -in that encoding and use g_convert() to convert the strings to UTF-8 at -runtime. Note that this has some runtime overhead, so you may want to move -the conversion out of inner loops. - - - -Here is an example showing the three approaches using the copyright sign -© which has Unicode and ISO-8859-1 codepoint 169 and is represented -in UTF-8 by the two bytes 194, 169, or "\302\251" as -a string literal: - -g_print ("direct UTF-8: ©"); -g_print ("escaped UTF-8: \302\251"); -text = g_convert ("runtime conversion: ©", -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL); -g_print(text); -g_free (text); - - - -If you are using gettext() to localize your application, you need to -call bind_textdomain_codeset() to ensure that translated strings are -returned in UTF-8 encoding. - - - - - - -How do I use GTK with C++? - - - - -There are two ways to approach this. The GTK header files use the subset -of C that's also valid C++, so you can simply use the normal GTK API -in a C++ program. Alternatively, you can use a "C++ binding" -such as gtkmm -which provides a native C++ API. - - -When using GTK directly, keep in mind that only functions can be -connected to signals, not methods. So you will need to use global -functions or "static" class functions for signal connections. - - -Another common issue when using GTK directly is that -C++ will not implicitly convert an integer to an enumeration. -This comes up when using bitfields; in C you can write the following -code: - - - gdk_surface_set_events (gdk_surface, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - - -while in C++ you must write: - - - gdk_surface_set_events (gdk_surface, - (GdkEventMask) GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - - -There are very few functions that require this cast, however. - - - - - - - -How do I use GTK with other non-C languages? - - - - -See the list of language -bindings on https://www.gtk.org. - - - - - - - - -How do I load an image or animation from a file? - - - - - -To load an image file straight into a display widget, use -gtk_image_new_from_file(). To load an image for another purpose, use -gdk_texture_new_from_file(). To load a video from a file, use -gtk_media_file_new_for_file(). - - - - - - - -How do I draw text ? - - - - -To draw a piece of text onto a cairo surface, use a Pango layout and -pango_cairo_show_layout(). - - - layout = gtk_widget_create_pango_layout (widget, text); - fontdesc = pango_font_description_from_string ("Luxi Mono 12"); - pango_layout_set_font_description (layout, fontdesc); - pango_cairo_show_layout (cr, layout); - pango_font_description_free (fontdesc); - g_object_unref (layout); - - - - - -See also the -Cairo Rendering -section of Pango manual. - - - -To draw a piece of text in a widget snapshot() implementation, use -gtk_snapshot_append_layout(). - - - - - - - - -How do I measure the size of a piece of text ? - - - - - -To obtain the size of a piece of text, use a Pango layout and -pango_layout_get_pixel_size(), using code like the following: - - - layout = gtk_widget_create_pango_layout (widget, text); - fontdesc = pango_font_description_from_string ("Luxi Mono 12"); - pango_layout_set_font_description (layout, fontdesc); - pango_layout_get_pixel_size (layout, &width, &height); - pango_font_description_free (fontdesc); - g_object_unref (layout); - - - - - -See also the -Layout Objects -section of Pango manual. - - - - - - - -Why are types not registered if I use their GTK_TYPE_BLAH -macro ? - - - - - -The %GTK_TYPE_BLAH macros are defined as calls to gtk_blah_get_type(), and -the _get_type() functions are declared as %G_GNUC_CONST which allows -the compiler to optimize the call away if it appears that the value is not -being used. - - - -GLib provides the g_type_ensure() function to work around this problem. - - g_type_ensure (GTK_TYPE_BLAH); - - - - - - - - -How do I create a transparent toplevel window ? - - - - - -Any toplevel window can be transparent. -It is just a matter of setting a transparent background -in the CSS style for it. - - - - - - -Which widget should I use... - - - -...for lists and trees? - - - - -This question has different answers, depending on the size of the dataset -and the required formatting flexibility. - - -If you want to display a large amount of data in a uniform way, your -best option is a #GtkTreeView widget. See tree -widget overview. A list is just a tree with no branches, so the treeview -widget is used for lists as well. - - -If you want to display a small amount of items, but need flexible formatting -and widgetry inside the list, then you probably want to use a #GtkListBox, -which uses regular widgets for display. - - - - - - -...for multi-line text display or editing? - - - - -See text widget overview — you -should use the #GtkTextView widget. - - -If you only have a small amount of text, #GtkLabel may also be appropriate -of course. It can be made selectable with gtk_label_set_selectable(). For a -single-line text entry, see #GtkEntry. - - - - - - - -...to display an image or animation? - - - - -GTK has two widgets that are dedicated to displaying images. #GtkImage, for -small, fixed-size icons and #GtkPicture for content images. - - -Both can display images in just about any format GTK understands. -You can also use #GtkDrawingArea if you need to do something more complex, -such as draw text or graphics over the top of the image. - - -Both GtkImage and GtkPicture can display animations and videos as well. -To show an webm file, load it with the GtkMediaFile API and then use -it as a paintable: - - -mediafile = gtk_media_file_new_for_filename ("example.webm"); -picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (mediafile)); - - - - - - - - - -...for presenting a set of mutually-exclusive choices, where Windows -would use a combo box? - - - - -With GTK, a #GtkComboBox is the recommended widget to use for this use case. -If you need an editable text entry, use the #GtkComboBox:has-entry property. - - - - - - -GtkWidget - - - -How do I change the color of a widget? - - - -The background color of a widget is determined by the CSS style that applies -to it. To change that, you can set style classes on the widget, and provide -custom CSS to change the appearance. Such CSS can be loaded with -gtk_css_provider_load_from_file() and its variants. -See gtk_style_context_add_provider(). - - - - - -How do I change the font of a widget? - - - -If you want to make the text of a label larger, you can use -gtk_label_set_markup(): - -gtk_label_set_markup (label, "<big>big text</big>"); - -This is preferred for many apps because it's a relative size to the -user's chosen font size. See g_markup_escape_text() if you are -constructing such strings on the fly. - - -You can also change the font of a widget by putting - - .my-widget-class { - font: Sans 30; - } - -in a CSS file, loading it with gtk_css_provider_load_from_file(), and -adding the provider with gtk_style_context_add_provider_for_display(). -To associate this style information with your widget, set a style class -on its #GtkStyleContext using gtk_style_context_add_class(). -The advantage of this approach is that users can then override the font -you have chosen. See the #GtkStyleContext documentation for more discussion. - - - - - - -How do I disable/ghost/desensitize a widget? - - - -In GTK a disabled widget is termed "insensitive." -See gtk_widget_set_sensitive(). - - - - - - -GtkTextView - - - -How do I get the contents of the entire text widget as a string? - - - -See gtk_text_buffer_get_bounds() and gtk_text_buffer_get_text() -or gtk_text_iter_get_text(). - - - - GtkTextIter start, end; - GtkTextBuffer *buffer; - char *text; - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); - gtk_text_buffer_get_bounds (buffer, &start, &end); - text = gtk_text_iter_get_text (&start, &end); - /* use text */ - g_free (text); - - - - - - -How do I make a text widget display its complete contents in a specific font? - - - -If you use gtk_text_buffer_insert_with_tags() with appropriate tags to -select the font, the inserted text will have the desired appearance, but -text typed in by the user before or after the tagged block will appear in -the default style. - - -To ensure that all text has the desired appearance, use -gtk_widget_override_font() to change the default font for the widget. - - - - - - -How do I make a text view scroll to the end of the buffer automatically ? - - - - - -A good way to keep a text buffer scrolled to the end is to place a -mark at the end of the buffer, and -give it right gravity. The gravity has the effect that text inserted -at the mark gets inserted before, keeping the mark -at the end. - - - -To ensure that the end of the buffer remains visible, use -gtk_text_view_scroll_to_mark() to scroll to the mark after -inserting new text. - - - -The gtk-demo application contains an example of this technique. - - - - - - - -#GtkTreeView - - - -How do I associate some data with a row in the tree? - - - - -Remember that the #GtkTreeModel columns don't necessarily have to be -displayed. So you can put non-user-visible data in your model just -like any other data, and retrieve it with gtk_tree_model_get(). -See the tree widget overview. - - - - - - -How do I put an image and some text in the same column? - - - - -You can pack more than one #GtkCellRenderer into a single #GtkTreeViewColumn -using gtk_tree_view_column_pack_start() or gtk_tree_view_column_pack_end(). -So pack both a #GtkCellRendererPixbuf and a #GtkCellRendererText into the -column. - - - - - - -I can set data easily on my #GtkTreeStore/#GtkListStore models using -gtk_list_store_set() and gtk_tree_store_set(), but can't read it back? - - - - -Both the #GtkTreeStore and the #GtkListStore implement the #GtkTreeModel -interface. Consequentially, you can use any function this interface -implements. The easiest way to read a set of data back is to use -gtk_tree_model_get(). - - - - - - -How do I change the way that numbers are formatted by #GtkTreeView? - - -Use gtk_tree_view_insert_column_with_data_func() -or gtk_tree_view_column_set_cell_data_func() and do the conversion -from number to string yourself (with, say, g_strdup_printf()). - - - -The following example demonstrates this: - -enum -{ - DOUBLE_COLUMN, - N_COLUMNS -}; - -GtkListStore *mycolumns; -GtkTreeView *treeview; - -void -my_cell_double_to_text (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - GtkCellRendererText *cell_text = (GtkCellRendererText *)cell; - gdouble d; - gchar *text; - - /* Get the double value from the model. */ - gtk_tree_model_get (tree_model, iter, (gint)data, &d, -1); - /* Now we can format the value ourselves. */ - text = g_strdup_printf ("%.2f", d); - g_object_set (cell, "text", text, NULL); - g_free (text); -} - -void -set_up_new_columns (GtkTreeView *myview) -{ - GtkCellRendererText *renderer; - GtkTreeViewColumn *column; - GtkListStore *mycolumns; - - /* Create the data model and associate it with the given TreeView */ - mycolumns = gtk_list_store_new (N_COLUMNS, G_TYPE_DOUBLE); - gtk_tree_view_set_model (myview, GTK_TREE_MODEL (mycolumns)); - - /* Create a GtkCellRendererText */ - renderer = gtk_cell_renderer_text_new (); - - /* Create a new column that has a title ("Example column"), - * uses the above created renderer that will render the double - * value into text from the associated model's rows. - */ - column = gtk_tree_view_column_new (); - gtk_tree_view_column_set_title (column, "Example column"); - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (column, renderer, TRUE); - - /* Append the new column after the GtkTreeView's previous columns. */ - gtk_tree_view_append_column (GTK_TREE_VIEW (myview), column); - /* Since we created the column by hand, we can set it up for our - * needs, e.g. set its minimum and maximum width, etc. - */ - /* Set up a custom function that will be called when the column content - * is rendered. We use the func_data pointer as an index into our - * model. This is convenient when using multi column lists. - */ - gtk_tree_view_column_set_cell_data_func (column, renderer, - my_cell_double_to_text, - (gpointer)DOUBLE_COLUMN, NULL); -} - - - - - - -How do I hide the expander arrows in my tree view ? - - - -Set the expander-column property of the tree view to a hidden column. -See gtk_tree_view_set_expander_column() and gtk_tree_view_column_set_visible(). - - - - - -Using cairo with GTK - - - -How do I use cairo to draw in GTK applications ? - - - -Use gtk_snapshot_append_cairo() in your #GtkWidgetClass.snapshot() vfunc -to obtain a cairo context and draw with that. - - - - - - -Can I improve the performance of my application by using another backend -of cairo (such as GL) ? - - - -No. Most drawing in GTK is not done via cairo anymore (but instead -by the GL or Vulkan renderers of GSK). - - -If you use cairo for drawing your own widgets, gtk_snapshot_append_cairo() -will choose the most appropriate surface type for you. - - -If you are interested in using GL for your own drawing, see #GtkGLArea. - - - - - -Can I use cairo to draw on a #GdkPixbuf ? - - - -No. The cairo image surface does not support the pixel format used by GdkPixbuf. - - -If you need to get cairo drawing into a format that can be displayed efficiently -by GTK, you may want to use an image surface and gdk_memory_texture_new(). - - - - - - - - - - From f60149cd9e060650edbb1a487499c1fb44267367 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 25 May 2020 12:45:29 -0400 Subject: [PATCH 16/18] ci: Add a fedora-docs image Split the fedora image into fedora-base (containing just the packages) and fedora (adding the user setup), and add a fedora-docs image that is adding pandoc on top of the fedora-base image. --- .gitlab-ci/fedora-base.Dockerfile | 88 +++++++++++++++++++++++++++++++ .gitlab-ci/fedora-docs.Dockerfile | 12 +++++ .gitlab-ci/fedora.Dockerfile | 88 +------------------------------ 3 files changed, 101 insertions(+), 87 deletions(-) create mode 100644 .gitlab-ci/fedora-base.Dockerfile create mode 100644 .gitlab-ci/fedora-docs.Dockerfile diff --git a/.gitlab-ci/fedora-base.Dockerfile b/.gitlab-ci/fedora-base.Dockerfile new file mode 100644 index 0000000000..0237b3804b --- /dev/null +++ b/.gitlab-ci/fedora-base.Dockerfile @@ -0,0 +1,88 @@ +FROM fedora:31 + +RUN dnf -y install \ + adwaita-icon-theme \ + atk-devel \ + at-spi2-atk-devel \ + avahi-gobject-devel \ + cairo-devel \ + cairo-gobject-devel \ + ccache \ + clang \ + clang-analyzer \ + colord-devel \ + cups-devel \ + dbus-daemon \ + dbus-x11 \ + dejavu-sans-mono-fonts \ + desktop-file-utils \ + diffutils \ + elfutils-libelf-devel \ + fribidi-devel \ + gcc \ + gcc-c++ \ + gdk-pixbuf2-devel \ + gdk-pixbuf2-modules \ + gettext \ + git \ + glib2-devel \ + glib2-static \ + glibc-devel \ + glibc-headers \ + gobject-introspection-devel \ + graphene-devel \ + gstreamer1-devel \ + gstreamer1-plugins-good \ + gstreamer1-plugins-bad-free-devel \ + gstreamer1-plugins-base-devel \ + gtk-doc \ + hicolor-icon-theme \ + iso-codes \ + itstool \ + json-glib-devel \ + lcov \ + libattr-devel \ + libepoxy-devel \ + libffi-devel \ + libmount-devel \ + librsvg2 \ + libselinux-devel \ + libXcomposite-devel \ + libXcursor-devel \ + libXcursor-devel \ + libXdamage-devel \ + libXfixes-devel \ + libXi-devel \ + libXinerama-devel \ + libxkbcommon-devel \ + libXrandr-devel \ + libXrender-devel \ + libXtst-devel \ + libxslt \ + mesa-dri-drivers \ + mesa-libEGL-devel \ + mesa-libwayland-egl-devel \ + ninja-build \ + pango-devel \ + pcre-devel \ + pcre-static \ + python3 \ + python3-jinja2 \ + python3-pip \ + python3-pygments \ + python3-wheel \ + redhat-rpm-config \ + sassc \ + sysprof-devel \ + systemtap-sdt-devel \ + vulkan-devel \ + wayland-devel \ + wayland-protocols-devel \ + weston \ + weston-libs \ + which \ + xorg-x11-server-Xvfb \ + && dnf clean all + +RUN pip3 install meson==0.53.1 + diff --git a/.gitlab-ci/fedora-docs.Dockerfile b/.gitlab-ci/fedora-docs.Dockerfile new file mode 100644 index 0000000000..a99a15e6c2 --- /dev/null +++ b/.gitlab-ci/fedora-docs.Dockerfile @@ -0,0 +1,12 @@ +FROM registry.gitlab.gnome.org/gnome/gtk/fedora-base:v19 + +RUN dnf -y install pandoc + +ARG HOST_USER_ID=5555 +ENV HOST_USER_ID ${HOST_USER_ID} +RUN useradd -u $HOST_USER_ID -ms /bin/bash user + +USER user +WORKDIR /home/user + +ENV LANG C.UTF-8 diff --git a/.gitlab-ci/fedora.Dockerfile b/.gitlab-ci/fedora.Dockerfile index 217dd45a98..95e2bdf727 100644 --- a/.gitlab-ci/fedora.Dockerfile +++ b/.gitlab-ci/fedora.Dockerfile @@ -1,90 +1,4 @@ -FROM fedora:31 - -RUN dnf -y install \ - adwaita-icon-theme \ - atk-devel \ - at-spi2-atk-devel \ - avahi-gobject-devel \ - cairo-devel \ - cairo-gobject-devel \ - ccache \ - clang \ - clang-analyzer \ - colord-devel \ - cups-devel \ - dbus-daemon \ - dbus-x11 \ - dejavu-sans-mono-fonts \ - desktop-file-utils \ - diffutils \ - elfutils-libelf-devel \ - fribidi-devel \ - gcc \ - gcc-c++ \ - gdk-pixbuf2-devel \ - gdk-pixbuf2-modules \ - gettext \ - git \ - glib2-devel \ - glib2-static \ - glibc-devel \ - glibc-headers \ - gobject-introspection-devel \ - graphene-devel \ - gstreamer1-devel \ - gstreamer1-plugins-good \ - gstreamer1-plugins-bad-free-devel \ - gstreamer1-plugins-base-devel \ - gtk-doc \ - hicolor-icon-theme \ - iso-codes \ - itstool \ - json-glib-devel \ - lcov \ - libattr-devel \ - libepoxy-devel \ - libffi-devel \ - libmount-devel \ - librsvg2 \ - libselinux-devel \ - libXcomposite-devel \ - libXcursor-devel \ - libXcursor-devel \ - libXdamage-devel \ - libXfixes-devel \ - libXi-devel \ - libXinerama-devel \ - libxkbcommon-devel \ - libXrandr-devel \ - libXrender-devel \ - libXtst-devel \ - libxslt \ - mesa-dri-drivers \ - mesa-libEGL-devel \ - mesa-libwayland-egl-devel \ - ninja-build \ - pango-devel \ - pcre-devel \ - pcre-static \ - python3 \ - python3-jinja2 \ - python3-pip \ - python3-pygments \ - python3-wheel \ - redhat-rpm-config \ - sassc \ - sysprof-devel \ - systemtap-sdt-devel \ - vulkan-devel \ - wayland-devel \ - wayland-protocols-devel \ - weston \ - weston-libs \ - which \ - xorg-x11-server-Xvfb \ - && dnf clean all - -RUN pip3 install meson==0.53.1 +FROM registry.gitlab.gnome.org/gnome/gtk/fedora-base:v19 ARG HOST_USER_ID=5555 ENV HOST_USER_ID ${HOST_USER_ID} From 86f8be41d0639dde2b61637e34cebff6a86f56aa Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 25 May 2020 12:48:10 -0400 Subject: [PATCH 17/18] ci: Use fedora-docs for the doc build Our doc build now uses pandoc, so use an image that has it. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 141954326a..1f9cd0f906 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -184,7 +184,7 @@ static-scan: allow_failure: true reference: - image: registry.gitlab.gnome.org/gnome/gtk/fedora:v16 + image: registry.gitlab.gnome.org/gnome/gtk/fedora-docs:v19 stage: docs variables: EXTRA_MESON_FLAGS: "--buildtype=release" From ae2e548b4bed446bfbda9cb50c406ac1c5bfae13 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 25 May 2020 18:40:09 -0400 Subject: [PATCH 18/18] docs: Use the right python --- docs/reference/gtk/gtk-markdown-to-docbook | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/gtk/gtk-markdown-to-docbook b/docs/reference/gtk/gtk-markdown-to-docbook index 5830d64d78..f48b556f48 100755 --- a/docs/reference/gtk/gtk-markdown-to-docbook +++ b/docs/reference/gtk/gtk-markdown-to-docbook @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # Call pandoc to convert markdown to docbook, then expand gtk-doc # abbreviations (|[ ]|, function(), #object, %constant, etc)