home.c 8.84 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 124 125 126 127
  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);

  gtk_widget_queue_draw (GTK_WIDGET (self));
}


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 156 157

  phosh_home_set_state (self, PHOSH_HOME_STATE_FOLDED);
}



Guido Gunther's avatar
Guido Gunther committed
158 159 160 161 162
static void
phosh_home_constructed (GObject *object)
{
  PhoshHome *self = PHOSH_HOME (object);

Guido Gunther's avatar
Guido Gunther committed
163
  g_signal_connect_object (self->btn_home,
Guido Gunther's avatar
Guido Gunther committed
164 165 166 167 168
                           "clicked",
                           G_CALLBACK (home_clicked_cb),
                           self,
                           G_CONNECT_SWAPPED);

Guido Gunther's avatar
Guido Gunther committed
169
  g_signal_connect_object (self->btn_osk,
Guido Gunther's avatar
Guido Gunther committed
170 171 172 173 174
                           "clicked",
                           G_CALLBACK (osk_clicked_cb),
                           self,
                           G_CONNECT_SWAPPED);

175
  g_signal_connect_swapped (self->overview,
176 177 178
                            "activity-launched",
                            G_CALLBACK(fold_cb),
                            self);
179
  g_signal_connect_swapped (self->overview,
180 181 182
                            "activity-raised",
                            G_CALLBACK(fold_cb),
                            self);
183
  g_signal_connect_swapped (self->overview,
184 185 186 187
                            "selection-aborted",
                            G_CALLBACK(fold_cb),
                            self);

188 189 190 191 192
  g_signal_connect (self,
                    "size-allocate",
                    G_CALLBACK (phosh_home_resize),
                    NULL);

Guido Gunther's avatar
Guido Gunther committed
193 194 195 196 197 198 199 200 201 202 203 204
  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;

205 206 207
  object_class->set_property = phosh_home_set_property;
  object_class->get_property = phosh_home_get_property;

Guido Gunther's avatar
Guido Gunther committed
208 209 210
  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
211

212 213 214 215 216 217 218 219 220 221
  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);

222
  g_type_ensure (PHOSH_TYPE_ARROW);
223 224 225
  g_type_ensure (PHOSH_TYPE_OSK_BUTTON);
  g_type_ensure (PHOSH_TYPE_OVERVIEW);

Guido Gunther's avatar
Guido Gunther committed
226 227
  gtk_widget_class_set_template_from_resource (widget_class,
                                               "/sm/puri/phosh/ui/home.ui");
Guido Gunther's avatar
Guido Gunther committed
228
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, btn_home);
229
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, arrow_home);
Guido Gunther's avatar
Guido Gunther committed
230
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, btn_osk);
231
  gtk_widget_class_bind_template_child (widget_class, PhoshHome, overview);
Zander Brown's avatar
Zander Brown committed
232 233

  gtk_widget_class_set_css_name (widget_class, "phosh-home");
Guido Gunther's avatar
Guido Gunther committed
234 235 236 237 238 239
}


static void
phosh_home_init (PhoshHome *self)
{
240
  self->state = PHOSH_HOME_STATE_FOLDED;
241
  self->animation.progress = 1.0;
242

Guido Gunther's avatar
Guido Gunther committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
  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,
259
                       "exclusive-zone", PHOSH_HOME_BUTTON_HEIGHT,
260
                       "namespace", "phosh home",
Guido Gunther's avatar
Guido Gunther committed
261 262
                       NULL);
}
263

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

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;
}

292 293 294 295 296 297 298 299
/**
 * phosh_home_set_state:
 *
 * Set the state of the home screen. See #PhoshHomeState.
 */
void
phosh_home_set_state (PhoshHome *self, PhoshHomeState state)
{
300
  gboolean enable_animations;
301
  gboolean kbd_interactivity;
302

303 304 305 306 307
  g_return_if_fail (PHOSH_IS_HOME (self));

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

308
  enable_animations = hdy_get_enable_animations (GTK_WIDGET (self));
309

310 311 312
  self->state = state;
  g_debug ("Setting state to %s", g_enum_to_string (PHOSH_TYPE_HOME_STATE, state));

313 314 315
  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);
316

317
  if (state == PHOSH_HOME_STATE_UNFOLDED) {
318
    gtk_widget_hide (self->btn_osk);
319
    kbd_interactivity = TRUE;
320
  } else {
321
    gtk_widget_show (self->btn_osk);
322
    kbd_interactivity = FALSE;
323
  }
324
  phosh_layer_surface_set_kbd_interactivity (PHOSH_LAYER_SURFACE (self), kbd_interactivity);
325

326 327
  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_HOME_STATE]);
}