1 | /* |
2 | * Copyright © 2020 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Authors: Matthias Clasen <mclasen@redhat.com> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include "gdk-private.h" |
23 | #include "gdkintl.h" |
24 | #include "gdkpopupprivate.h" |
25 | |
26 | /** |
27 | * GdkPopup: |
28 | * |
29 | * A `GdkPopup` is a surface that is attached to another surface. |
30 | * |
31 | * The `GdkPopup` is positioned relative to its parent surface. |
32 | * |
33 | * `GdkPopup`s are typically used to implement menus and similar popups. |
34 | * They can be modal, which is indicated by the [property@GdkPopup:autohide] |
35 | * property. |
36 | */ |
37 | |
38 | G_DEFINE_INTERFACE (GdkPopup, gdk_popup, GDK_TYPE_SURFACE) |
39 | |
40 | static gboolean |
41 | (GdkPopup *, |
42 | int width, |
43 | int height, |
44 | GdkPopupLayout *layout) |
45 | { |
46 | return FALSE; |
47 | } |
48 | |
49 | static GdkGravity |
50 | (GdkPopup *) |
51 | { |
52 | return GDK_GRAVITY_STATIC; |
53 | } |
54 | |
55 | static GdkGravity |
56 | (GdkPopup *) |
57 | { |
58 | return GDK_GRAVITY_STATIC; |
59 | } |
60 | |
61 | static int |
62 | (GdkPopup *) |
63 | { |
64 | return 0; |
65 | } |
66 | |
67 | static int |
68 | (GdkPopup *) |
69 | { |
70 | return 0; |
71 | } |
72 | |
73 | static void |
74 | (GdkPopupInterface *iface) |
75 | { |
76 | iface->present = gdk_popup_default_present; |
77 | iface->get_surface_anchor = gdk_popup_default_get_surface_anchor; |
78 | iface->get_rect_anchor = gdk_popup_default_get_rect_anchor; |
79 | iface->get_position_x = gdk_popup_default_get_position_x; |
80 | iface->get_position_y = gdk_popup_default_get_position_y; |
81 | |
82 | /** |
83 | * GdkPopup:parent: (attributes org.gtk.Property.get=gdk_popup_get_parent) |
84 | * |
85 | * The parent surface. |
86 | */ |
87 | g_object_interface_install_property (g_iface: iface, |
88 | pspec: g_param_spec_object (name: "parent" , |
89 | P_("Parent" ), |
90 | P_("The parent surface" ), |
91 | GDK_TYPE_SURFACE, |
92 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); |
93 | |
94 | /** |
95 | * GdkPopup:autohide: (attributes org.gtk.Property.get=gdk_popup_get_autohide) |
96 | * |
97 | * Whether to hide on outside clicks. |
98 | */ |
99 | g_object_interface_install_property (g_iface: iface, |
100 | pspec: g_param_spec_boolean (name: "autohide" , |
101 | P_("Autohide" ), |
102 | P_("Whether to hide on outside clicks" ), |
103 | FALSE, |
104 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); |
105 | } |
106 | |
107 | /** |
108 | * gdk_popup_present: |
109 | * @popup: the `GdkPopup` to show |
110 | * @width: the unconstrained popup width to layout |
111 | * @height: the unconstrained popup height to layout |
112 | * @layout: the `GdkPopupLayout` object used to layout |
113 | * |
114 | * Present @popup after having processed the `GdkPopupLayout` rules. |
115 | * |
116 | * If the popup was previously now showing, it will be showed, |
117 | * otherwise it will change position according to @layout. |
118 | * |
119 | * After calling this function, the result should be handled in response |
120 | * to the [signal@GdkSurface::layout] signal being emitted. The resulting |
121 | * popup position can be queried using [method@Gdk.Popup.get_position_x], |
122 | * [method@Gdk.Popup.get_position_y], and the resulting size will be sent as |
123 | * parameters in the layout signal. Use [method@Gdk.Popup.get_rect_anchor] |
124 | * and [method@Gdk.Popup.get_surface_anchor] to get the resulting anchors. |
125 | * |
126 | * Presenting may fail, for example if the @popup is set to autohide |
127 | * and is immediately hidden upon being presented. If presenting failed, |
128 | * the [signal@Gdk.Surface::layout] signal will not me emitted. |
129 | * |
130 | * Returns: %FALSE if it failed to be presented, otherwise %TRUE. |
131 | */ |
132 | gboolean |
133 | (GdkPopup *, |
134 | int width, |
135 | int height, |
136 | GdkPopupLayout *layout) |
137 | { |
138 | g_return_val_if_fail (GDK_IS_POPUP (popup), FALSE); |
139 | g_return_val_if_fail (width > 0, FALSE); |
140 | g_return_val_if_fail (height > 0, FALSE); |
141 | g_return_val_if_fail (layout != NULL, FALSE); |
142 | |
143 | return GDK_POPUP_GET_IFACE (ptr: popup)->present (popup, width, height, layout); |
144 | } |
145 | |
146 | /** |
147 | * gdk_popup_get_surface_anchor: |
148 | * @popup: a `GdkPopup` |
149 | * |
150 | * Gets the current popup surface anchor. |
151 | * |
152 | * The value returned may change after calling [method@Gdk.Popup.present], |
153 | * or after the [signal@Gdk.Surface::layout] signal is emitted. |
154 | * |
155 | * Returns: the current surface anchor value of @popup |
156 | */ |
157 | GdkGravity |
158 | (GdkPopup *) |
159 | { |
160 | g_return_val_if_fail (GDK_IS_POPUP (popup), GDK_GRAVITY_STATIC); |
161 | |
162 | return GDK_POPUP_GET_IFACE (ptr: popup)->get_surface_anchor (popup); |
163 | } |
164 | |
165 | /** |
166 | * gdk_popup_get_rect_anchor: |
167 | * @popup: a `GdkPopup` |
168 | * |
169 | * Gets the current popup rectangle anchor. |
170 | * |
171 | * The value returned may change after calling [method@Gdk.Popup.present], |
172 | * or after the [signal@Gdk.Surface::layout] signal is emitted. |
173 | * |
174 | * Returns: the current rectangle anchor value of @popup |
175 | */ |
176 | GdkGravity |
177 | (GdkPopup *) |
178 | { |
179 | g_return_val_if_fail (GDK_IS_POPUP (popup), GDK_GRAVITY_STATIC); |
180 | |
181 | return GDK_POPUP_GET_IFACE (ptr: popup)->get_rect_anchor (popup); |
182 | } |
183 | |
184 | /** |
185 | * gdk_popup_get_parent: (attributes org.gtk.Method.get_property=parent) |
186 | * @popup: a `GdkPopup` |
187 | * |
188 | * Returns the parent surface of a popup. |
189 | * |
190 | * Returns: (transfer none) (nullable): the parent surface |
191 | */ |
192 | GdkSurface * |
193 | (GdkPopup *) |
194 | { |
195 | GdkSurface *surface; |
196 | |
197 | g_return_val_if_fail (GDK_IS_POPUP (popup), NULL); |
198 | |
199 | g_object_get (object: popup, first_property_name: "parent" , &surface, NULL); |
200 | |
201 | if (surface) |
202 | g_object_unref (object: surface); |
203 | |
204 | return surface; |
205 | } |
206 | |
207 | /** |
208 | * gdk_popup_get_position_x: |
209 | * @popup: a `GdkPopup` |
210 | * |
211 | * Obtains the position of the popup relative to its parent. |
212 | * |
213 | * Returns: the X coordinate of @popup position |
214 | */ |
215 | int |
216 | (GdkPopup *) |
217 | { |
218 | g_return_val_if_fail (GDK_IS_POPUP (popup), 0); |
219 | |
220 | return GDK_POPUP_GET_IFACE (ptr: popup)->get_position_x (popup); |
221 | } |
222 | |
223 | /** |
224 | * gdk_popup_get_position_y: |
225 | * @popup: a `GdkPopup` |
226 | * |
227 | * Obtains the position of the popup relative to its parent. |
228 | * |
229 | * Returns: the Y coordinate of @popup position |
230 | */ |
231 | int |
232 | (GdkPopup *) |
233 | { |
234 | g_return_val_if_fail (GDK_IS_POPUP (popup), 0); |
235 | |
236 | return GDK_POPUP_GET_IFACE (ptr: popup)->get_position_y (popup); |
237 | } |
238 | |
239 | /** |
240 | * gdk_popup_get_autohide: (attributes org.gtk.Method.get_property=autohide) |
241 | * @popup: a `GdkPopup` |
242 | * |
243 | * Returns whether this popup is set to hide on outside clicks. |
244 | * |
245 | * Returns: %TRUE if @popup will autohide |
246 | */ |
247 | gboolean |
248 | (GdkPopup *) |
249 | { |
250 | gboolean autohide; |
251 | |
252 | g_return_val_if_fail (GDK_IS_POPUP (popup), FALSE); |
253 | |
254 | g_object_get (object: popup, first_property_name: "autohide" , &autohide, NULL); |
255 | |
256 | return autohide; |
257 | } |
258 | |
259 | guint |
260 | (GObjectClass *object_class, |
261 | guint first_prop) |
262 | { |
263 | /** |
264 | * GdkToplevel:parent: |
265 | * |
266 | * The parent surface of the toplevel. |
267 | */ |
268 | g_object_class_override_property (oclass: object_class, property_id: first_prop + GDK_POPUP_PROP_PARENT, name: "parent" ); |
269 | |
270 | /** |
271 | * GdkToplevel:autohide: |
272 | * |
273 | * Whether the toplevel should be modal with respect to its parent. |
274 | */ |
275 | g_object_class_override_property (oclass: object_class, property_id: first_prop + GDK_POPUP_PROP_AUTOHIDE, name: "autohide" ); |
276 | |
277 | return GDK_POPUP_NUM_PROPERTIES; |
278 | } |
279 | |