1 /*
2 * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1+
5 */
6
7 #include "config.h"
8
9 #include "adw-gizmo-private.h"
10 #include "adw-shadow-helper-private.h"
11
12 /**
13 * PRIVATE:adwshadowhelper
14 * @short_description: Shadow helper used in #AdwLeaflet
15 * @title: AdwShadowHelper
16 * @See_also: #AdwLeaflet
17 * @stability: Private
18 *
19 * A helper class for drawing #AdwLeaflet transition shadow.
20 *
21 * Since: 1.0
22 */
23
24 struct _AdwShadowHelper
25 {
26 GObject parent_instance;
27
28 GtkWidget *widget;
29
30 GtkWidget *dimming;
31 GtkWidget *shadow;
32 GtkWidget *border;
33 GtkWidget *outline;
34 };
35
36 G_DEFINE_TYPE (AdwShadowHelper, adw_shadow_helper, G_TYPE_OBJECT);
37
38 enum {
39 PROP_0,
40 PROP_WIDGET,
41 LAST_PROP,
42 };
43
44 static GParamSpec *props[LAST_PROP];
45
46 static void
adw_shadow_helper_constructed(GObject * object)47 adw_shadow_helper_constructed (GObject *object)
48 {
49 AdwShadowHelper *self = ADW_SHADOW_HELPER (object);
50
51 self->dimming = adw_gizmo_new ("dimming", NULL, NULL, NULL, NULL, NULL, NULL);
52 self->shadow = adw_gizmo_new ("shadow", NULL, NULL, NULL, NULL, NULL, NULL);
53 self->border = adw_gizmo_new ("border", NULL, NULL, NULL, NULL, NULL, NULL);
54 self->outline = adw_gizmo_new ("outline", NULL, NULL, NULL, NULL, NULL, NULL);
55
56 gtk_widget_set_child_visible (self->dimming, FALSE);
57 gtk_widget_set_child_visible (self->shadow, FALSE);
58 gtk_widget_set_child_visible (self->border, FALSE);
59 gtk_widget_set_child_visible (self->outline, FALSE);
60
61 gtk_widget_set_can_target (self->dimming, FALSE);
62 gtk_widget_set_can_target (self->shadow, FALSE);
63 gtk_widget_set_can_target (self->border, FALSE);
64 gtk_widget_set_can_target (self->outline, FALSE);
65
66 gtk_widget_set_parent (self->dimming, self->widget);
67 gtk_widget_set_parent (self->shadow, self->widget);
68 gtk_widget_set_parent (self->border, self->widget);
69 gtk_widget_set_parent (self->outline, self->widget);
70
71 G_OBJECT_CLASS (adw_shadow_helper_parent_class)->constructed (object);
72 }
73
74 static void
adw_shadow_helper_dispose(GObject * object)75 adw_shadow_helper_dispose (GObject *object)
76 {
77 AdwShadowHelper *self = ADW_SHADOW_HELPER (object);
78
79 g_clear_pointer (&self->dimming, gtk_widget_unparent);
80 g_clear_pointer (&self->shadow, gtk_widget_unparent);
81 g_clear_pointer (&self->border, gtk_widget_unparent);
82 g_clear_pointer (&self->outline, gtk_widget_unparent);
83 g_clear_object (&self->widget);
84
85 G_OBJECT_CLASS (adw_shadow_helper_parent_class)->dispose (object);
86 }
87
88 static void
adw_shadow_helper_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)89 adw_shadow_helper_get_property (GObject *object,
90 guint prop_id,
91 GValue *value,
92 GParamSpec *pspec)
93 {
94 AdwShadowHelper *self = ADW_SHADOW_HELPER (object);
95
96 switch (prop_id) {
97 case PROP_WIDGET:
98 g_value_set_object (value, self->widget);
99 break;
100
101 default:
102 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
103 }
104 }
105
106 static void
adw_shadow_helper_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)107 adw_shadow_helper_set_property (GObject *object,
108 guint prop_id,
109 const GValue *value,
110 GParamSpec *pspec)
111 {
112 AdwShadowHelper *self = ADW_SHADOW_HELPER (object);
113
114 switch (prop_id) {
115 case PROP_WIDGET:
116 self->widget = GTK_WIDGET (g_object_ref (g_value_get_object (value)));
117 break;
118
119 default:
120 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121 }
122 }
123
124 static void
adw_shadow_helper_class_init(AdwShadowHelperClass * klass)125 adw_shadow_helper_class_init (AdwShadowHelperClass *klass)
126 {
127 GObjectClass *object_class = G_OBJECT_CLASS (klass);
128
129 object_class->constructed = adw_shadow_helper_constructed;
130 object_class->dispose = adw_shadow_helper_dispose;
131 object_class->get_property = adw_shadow_helper_get_property;
132 object_class->set_property = adw_shadow_helper_set_property;
133
134 /**
135 * AdwShadowHelper:widget:
136 *
137 * The widget the shadow will be drawn for. Must not be %NULL
138 *
139 * Since: 1.0
140 */
141 props[PROP_WIDGET] =
142 g_param_spec_object ("widget",
143 "Widget",
144 "The widget the shadow will be drawn for",
145 GTK_TYPE_WIDGET,
146 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
147
148 g_object_class_install_properties (object_class, LAST_PROP, props);
149 }
150
151 static void
adw_shadow_helper_init(AdwShadowHelper * self)152 adw_shadow_helper_init (AdwShadowHelper *self)
153 {
154 }
155
156 /**
157 * adw_shadow_helper_new:
158 *
159 * Creates a new #AdwShadowHelper object.
160 *
161 * Returns: The newly created #AdwShadowHelper object
162 *
163 * Since: 1.0
164 */
165 AdwShadowHelper *
adw_shadow_helper_new(GtkWidget * widget)166 adw_shadow_helper_new (GtkWidget *widget)
167 {
168 return g_object_new (ADW_TYPE_SHADOW_HELPER,
169 "widget", widget,
170 NULL);
171 }
172
173 static void
set_style_classes(AdwShadowHelper * self,GtkPanDirection direction)174 set_style_classes (AdwShadowHelper *self,
175 GtkPanDirection direction)
176 {
177 const char *classes[2];
178
179 switch (direction) {
180 case GTK_PAN_DIRECTION_LEFT:
181 classes[0] = "left";
182 break;
183 case GTK_PAN_DIRECTION_RIGHT:
184 classes[0] = "right";
185 break;
186 case GTK_PAN_DIRECTION_UP:
187 classes[0] = "up";
188 break;
189 case GTK_PAN_DIRECTION_DOWN:
190 classes[0] = "down";
191 break;
192 default:
193 g_assert_not_reached ();
194 }
195 classes[1] = NULL;
196
197 gtk_widget_set_css_classes (self->dimming, classes);
198 gtk_widget_set_css_classes (self->shadow, classes);
199 gtk_widget_set_css_classes (self->border, classes);
200 gtk_widget_set_css_classes (self->outline, classes);
201 }
202
203 void
adw_shadow_helper_size_allocate(AdwShadowHelper * self,int width,int height,int baseline,int x,int y,double progress,GtkPanDirection direction)204 adw_shadow_helper_size_allocate (AdwShadowHelper *self,
205 int width,
206 int height,
207 int baseline,
208 int x,
209 int y,
210 double progress,
211 GtkPanDirection direction)
212 {
213 double distance, remaining_distance;
214 double shadow_opacity;
215 int shadow_size, border_size, outline_size;
216 GtkOrientation orientation;
217
218 set_style_classes (self, direction);
219
220 gtk_widget_allocate (self->dimming, width, height, baseline,
221 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y)));
222
223 switch (direction) {
224 case GTK_PAN_DIRECTION_LEFT:
225 case GTK_PAN_DIRECTION_RIGHT:
226 distance = width;
227 orientation = GTK_ORIENTATION_HORIZONTAL;
228 break;
229 case GTK_PAN_DIRECTION_UP:
230 case GTK_PAN_DIRECTION_DOWN:
231 distance = height;
232 orientation = GTK_ORIENTATION_VERTICAL;
233 break;
234 default:
235 g_assert_not_reached ();
236 }
237
238 gtk_widget_set_child_visible (self->dimming, progress < 1);
239 gtk_widget_set_child_visible (self->shadow, progress < 1);
240 gtk_widget_set_child_visible (self->border, progress < 1);
241 gtk_widget_set_child_visible (self->outline, progress < 1);
242
243 gtk_widget_measure (self->shadow, orientation, -1, &shadow_size, NULL, NULL, NULL);
244 gtk_widget_measure (self->border, orientation, -1, &border_size, NULL, NULL, NULL);
245 gtk_widget_measure (self->outline, orientation, -1, &outline_size, NULL, NULL, NULL);
246
247 remaining_distance = (1 - progress) * (double) distance;
248 if (remaining_distance < shadow_size)
249 shadow_opacity = (remaining_distance / shadow_size);
250 else
251 shadow_opacity = 1;
252
253 gtk_widget_set_opacity (self->dimming, 1 - progress);
254 gtk_widget_set_opacity (self->shadow, shadow_opacity);
255
256 switch (direction) {
257 case GTK_PAN_DIRECTION_LEFT:
258 gtk_widget_allocate (self->shadow, shadow_size, MAX (height, shadow_size), baseline,
259 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y)));
260 gtk_widget_allocate (self->border, border_size, MAX (height, border_size), baseline,
261 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y)));
262 gtk_widget_allocate (self->outline, outline_size, MAX (height, outline_size), baseline,
263 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x - outline_size, y)));
264 break;
265 case GTK_PAN_DIRECTION_RIGHT:
266 gtk_widget_allocate (self->shadow, shadow_size, MAX (height, shadow_size), baseline,
267 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x + width - shadow_size, y)));
268 gtk_widget_allocate (self->border, border_size, MAX (height, border_size), baseline,
269 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x + width - border_size, y)));
270 gtk_widget_allocate (self->outline, outline_size, MAX (height, outline_size), baseline,
271 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x + width, y)));
272 break;
273 case GTK_PAN_DIRECTION_UP:
274 gtk_widget_allocate (self->shadow, MAX (width, shadow_size), shadow_size, baseline,
275 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y)));
276 gtk_widget_allocate (self->border, MAX (width, border_size), border_size, baseline,
277 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y)));
278 gtk_widget_allocate (self->outline, MAX (width, outline_size), outline_size, baseline,
279 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y - outline_size)));
280 break;
281 case GTK_PAN_DIRECTION_DOWN:
282 gtk_widget_allocate (self->shadow, MAX (width, shadow_size), shadow_size, baseline,
283 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y + height - shadow_size)));
284 gtk_widget_allocate (self->border, MAX (width, border_size), border_size, baseline,
285 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y + height - border_size)));
286 gtk_widget_allocate (self->border, MAX (width, outline_size), outline_size, baseline,
287 gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y + height)));
288 break;
289 default:
290 g_assert_not_reached ();
291 }
292 }
293
294 void
adw_shadow_helper_snapshot(AdwShadowHelper * self,GtkSnapshot * snapshot)295 adw_shadow_helper_snapshot (AdwShadowHelper *self,
296 GtkSnapshot *snapshot)
297 {
298 if (!gtk_widget_get_child_visible (self->dimming))
299 return;
300
301 gtk_widget_snapshot_child (self->widget, self->dimming, snapshot);
302 gtk_widget_snapshot_child (self->widget, self->shadow, snapshot);
303 gtk_widget_snapshot_child (self->widget, self->border, snapshot);
304 gtk_widget_snapshot_child (self->widget, self->outline, snapshot);
305 }
306