home.c 9.47 KB
Newer Older
Guido Gunther's avatar
Guido Gunther committed
1 2 3 4 5 6 7 8 9
/*
 * Copyright (C) 2018 Purism SPC
 * SPDX-License-Identifier: GPL-3.0+
 * Author: Guido Günther <agx@sigxcpu.org>
 */

#define G_LOG_DOMAIN "phosh-home"

#include "config.h"
10
#include "arrow.h"
11
#include "overview.h"
Guido Gunther's avatar
Guido Gunther committed
12
#include "home.h"
13 14
#include "shell.h"
#include "phosh-enums.h"
Guido Gunther's avatar
Guido Gunther committed
15
#include "osk/osk-button.h"
Guido Gunther's avatar
Guido Gunther committed
16

17 18 19
#define HANDY_USE_UNSTABLE_API
#include <handy.h>

Guido Gunther's avatar
Guido Gunther committed
20 21
/**
 * SECTION:phosh-home
22 23
 * @short_description: The home screen (sometimes called overview)  and the corrsponding
 * button at the bottom of the screen
Guido Gunther's avatar
Guido Gunther committed
24 25
 * @Title: PhoshHome
 *
Guido Gunther's avatar
Guido Gunther committed
26 27
 * The #PhoshHome is displayed at the bottom of the screen. It features
 * the home button and the button to toggle the OSK.
Guido Gunther's avatar
Guido Gunther committed
28 29
 */
enum {
Guido Gunther's avatar
Guido Gunther committed
30
  OSK_ACTIVATED,
Guido Gunther's avatar
Guido Gunther committed
31 32 33 34
  N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0 };

35 36 37 38 39 40 41 42 43

enum {
  PROP_0,
  PROP_HOME_STATE,
  PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];


Guido Gunther's avatar
Guido Gunther committed
44 45 46 47
struct _PhoshHome
{
  PhoshLayerSurface parent;

Guido Gunther's avatar
Guido Gunther committed
48
  GtkWidget *btn_home;
49
  GtkWidget *arrow_home;
Guido Gunther's avatar
Guido Gunther committed
50
  GtkWidget *btn_osk;
51
  GtkWidget *overview;
52

53 54 55 56 57
  struct {
    gdouble progress;
    gint64 last_frame;
  } animation;

58
  PhoshHomeState state;
Guido Gunther's avatar
Guido Gunther committed
59 60
};
G_DEFINE_TYPE(PhoshHome, phosh_home, PHOSH_TYPE_LAYER_SURFACE);
Guido Gunther's avatar
Guido Gunther committed
61 62


63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
static void
phosh_home_set_property (GObject *object,
                          guint property_id,
                          const GValue *value,
                          GParamSpec *pspec)
{
  PhoshHome *self = PHOSH_HOME (object);

  switch (property_id) {
    case PROP_HOME_STATE:
      self->state = g_value_get_enum (value);
      g_object_notify_by_pspec (G_OBJECT (self), props[PROP_HOME_STATE]);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}


static void
phosh_home_get_property (GObject *object,
                          guint property_id,
                          GValue *value,
                          GParamSpec *pspec)
{
  PhoshHome *self = PHOSH_HOME (object);

  switch (property_id) {
    case PROP_HOME_STATE:
      g_value_set_enum (value, self->state);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}


102 103 104 105 106
static void
phosh_home_resize (PhoshHome *self)
{
  gint margin;
  gint height;
107
  gdouble progress = hdy_ease_out_cubic (self->animation.progress);
108 109 110 111

  if (self->state == PHOSH_HOME_STATE_UNFOLDED)
    progress = 1.0 - progress;

112 113
  phosh_arrow_set_progress (PHOSH_ARROW (self->arrow_home), 1 - progress);

114 115 116 117 118 119 120 121 122 123
  gtk_window_get_size (GTK_WINDOW (self), NULL, &height);
  margin = (-height + PHOSH_HOME_BUTTON_HEIGHT) * progress;

  phosh_layer_surface_set_margins (PHOSH_LAYER_SURFACE (self), 0, 0, margin, 0);
  /* Adjust the exclusive zone since exclusive zone includes margins.
     We don't want to change the effective exclusive zone at all to
     prevent all clients from being resized. */
  phosh_layer_surface_set_exclusive_zone (PHOSH_LAYER_SURFACE (self),
                                          -margin + PHOSH_HOME_BUTTON_HEIGHT);

124
  phosh_layer_surface_wl_surface_commit (PHOSH_LAYER_SURFACE (self));
125 126 127
}


Guido Gunther's avatar
Guido Gunther committed
128 129 130 131 132
static void
home_clicked_cb (PhoshHome *self, GtkButton *btn)
{
  g_return_if_fail (PHOSH_IS_HOME (self));
  g_return_if_fail (GTK_IS_BUTTON (btn));
133 134

  phosh_home_set_state (self, !self->state);
Guido Gunther's avatar
Guido Gunther committed
135 136 137
}


Guido Gunther's avatar
Guido Gunther committed
138 139 140 141 142 143 144 145 146
static void
osk_clicked_cb (PhoshHome *self, GtkButton *btn)
{
  g_return_if_fail (PHOSH_IS_HOME (self));
  g_return_if_fail (GTK_IS_BUTTON (btn));
  g_signal_emit(self, signals[OSK_ACTIVATED], 0);
}


147
static void
148
fold_cb (PhoshHome *self, PhoshOverview *overview)
149 150
{
  g_return_if_fail (PHOSH_IS_HOME (self));
151
  g_return_if_fail (PHOSH_IS_OVERVIEW (overview));
152 153 154 155

  phosh_home_set_state (self, PHOSH_HOME_STATE_FOLDED);
}

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
static gboolean
key_press_event_cb (PhoshHome *self, GdkEventKey *event, gpointer data)
{
  gboolean handled = FALSE;
  g_return_val_if_fail (PHOSH_IS_HOME (self), FALSE);

  switch (event->keyval) {
    case GDK_KEY_Escape:
      phosh_home_set_state (self, PHOSH_HOME_STATE_FOLDED);
      handled = TRUE;
      break;
    default:
      /* nothing to do */
      break;
  }

  return handled;
}
174 175


Guido Gunther's avatar
Guido Gunther committed
176 177 178 179 180
static void
phosh_home_constructed (GObject *object)
{
  PhoshHome *self = PHOSH_HOME (object);

Guido Gunther's avatar
Guido Gunther committed
181
  g_signal_connect_object (self->btn_home,
Guido Gunther's avatar
Guido Gunther committed
182 183 184 185 186
                           "clicked",
                           G_CALLBACK (home_clicked_cb),
                           self,
                           G_CONNECT_SWAPPED);

Guido Gunther's avatar
Guido Gunther committed
187
  g_signal_connect_object (self->btn_osk,
Guido Gunther's avatar
Guido Gunther committed
188 189 190 191 192
                           "clicked",
                           G_CALLBACK (osk_clicked_cb),
                           self,
                           G_CONNECT_SWAPPED);

193
  g_signal_connect_swapped (self->overview,
194 195 196
                            "activity-launched",
                            G_CALLBACK(fold_cb),
                            self);
197
  g_signal_connect_swapped (self->overview,
198 199 200
                            "activity-raised",
                            G_CALLBACK(fold_cb),
                            self);
201
  g_signal_connect_swapped (self->overview,
202 203 204 205
                            "selection-aborted",
                            G_CALLBACK(fold_cb),
                            self);

206 207 208 209 210
  g_signal_connect (self,
                    "size-allocate",
                    G_CALLBACK (phosh_home_resize),
                    NULL);

211 212 213 214 215 216
  gtk_widget_add_events (GTK_WIDGET (self), GDK_KEY_PRESS_MASK);
  g_signal_connect (G_OBJECT (self),
                    "key_press_event",
                    G_CALLBACK (key_press_event_cb),
                    NULL);

Guido Gunther's avatar
Guido Gunther committed
217 218 219 220 221 222 223 224 225 226 227 228
  G_OBJECT_CLASS (phosh_home_parent_class)->constructed (object);
}


static void
phosh_home_class_init (PhoshHomeClass *klass)
{
  GObjectClass *object_class = (GObjectClass *)klass;
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->constructed = phosh_home_constructed;

229 230 231
  object_class->set_property = phosh_home_set_property;
  object_class->get_property = phosh_home_get_property;

Guido Gunther's avatar
Guido Gunther committed
232 233 234
  signals[OSK_ACTIVATED] = g_signal_new ("osk-activated",
      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
      NULL, G_TYPE_NONE, 0);
Guido Gunther's avatar
Guido Gunther committed
235

236 237 238 239 240 241 242 243 244 245
  props[PROP_HOME_STATE] =
    g_param_spec_enum ("state",
                       "Home State",
                       "The state of the home screen",
                       PHOSH_TYPE_HOME_STATE,
                       PHOSH_HOME_STATE_FOLDED,
                       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);

  g_object_class_install_properties (object_class, PROP_LAST_PROP, props);

246
  g_type_ensure (PHOSH_TYPE_ARROW);
247 248 249
  g_type_ensure (PHOSH_TYPE_OSK_BUTTON);
  g_type_ensure (PHOSH_TYPE_OVERVIEW);

Guido Gunther's avatar
Guido Gunther committed
250 251
  gtk_widget_class_set_template_from_resource (widget_class,
                                               "/sm/puri/phosh/ui/home.ui");
Guido Gunther's avatar
Guido Gunther committed
252
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, btn_home);
253
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, arrow_home);
Guido Gunther's avatar
Guido Gunther committed
254
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, btn_osk);
255
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, overview);
Zander Brown's avatar
Zander Brown committed
256 257

  gtk_widget_class_set_css_name (widget_class, "phosh-home");
Guido Gunther's avatar
Guido Gunther committed
258 259 260 261 262 263
}


static void
phosh_home_init (PhoshHome *self)
{
264
  self->state = PHOSH_HOME_STATE_FOLDED;
265
  self->animation.progress = 1.0;
266

Guido Gunther's avatar
Guido Gunther committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
  gtk_widget_init_template (GTK_WIDGET (self));
}


GtkWidget *
phosh_home_new (struct zwlr_layer_shell_v1 *layer_shell,
                struct wl_output *wl_output)
{
  return g_object_new (PHOSH_TYPE_HOME,
                       "layer-shell", layer_shell,
                       "wl-output", wl_output,
                       "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
                                 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
                                 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
                       "layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP,
                       "kbd-interactivity", FALSE,
283
                       "exclusive-zone", PHOSH_HOME_BUTTON_HEIGHT,
284
                       "namespace", "phosh home",
Guido Gunther's avatar
Guido Gunther committed
285 286
                       NULL);
}
287

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

static gboolean
animate_cb(GtkWidget *widget,
           GdkFrameClock *frame_clock,
           gpointer user_data)
{
  gint64 time;
  gboolean finished = FALSE;
  PhoshHome *self = PHOSH_HOME (widget);

  time = gdk_frame_clock_get_frame_time (frame_clock) - self->animation.last_frame;
  if (self->animation.last_frame < 0) {
    time = 0;
  }

  self->animation.progress += 0.06666 * time / 16666.00;
  self->animation.last_frame = gdk_frame_clock_get_frame_time (frame_clock);

  if (self->animation.progress >= 1.0) {
    finished = TRUE;
    self->animation.progress = 1.0;
  }

  phosh_home_resize (self);

  return finished ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE;
}

316 317 318 319 320 321 322 323
/**
 * phosh_home_set_state:
 *
 * Set the state of the home screen. See #PhoshHomeState.
 */
void
phosh_home_set_state (PhoshHome *self, PhoshHomeState state)
{
324
  gboolean enable_animations;
325
  gboolean kbd_interactivity;
326

327 328 329 330 331
  g_return_if_fail (PHOSH_IS_HOME (self));

  if (self->state == state)
    return;

332
  enable_animations = hdy_get_enable_animations (GTK_WIDGET (self));
333

334 335 336
  self->state = state;
  g_debug ("Setting state to %s", g_enum_to_string (PHOSH_TYPE_HOME_STATE, state));

337 338 339
  self->animation.last_frame = -1;
  self->animation.progress = enable_animations ? (1.0 - self->animation.progress) : 1.0;
  gtk_widget_add_tick_callback (GTK_WIDGET (self), animate_cb, NULL, NULL);
340

341
  if (state == PHOSH_HOME_STATE_UNFOLDED) {
342
    gtk_widget_hide (self->btn_osk);
343
    kbd_interactivity = TRUE;
344
  } else {
345
    gtk_widget_show (self->btn_osk);
346
    kbd_interactivity = FALSE;
347
  }
348
  phosh_layer_surface_set_kbd_interactivity (PHOSH_LAYER_SURFACE (self), kbd_interactivity);
349

350 351
  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_HOME_STATE]);
}