aboutsummaryrefslogtreecommitdiff
path: root/src/dash-box.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dash-box.vala')
-rw-r--r--src/dash-box.vala230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/dash-box.vala b/src/dash-box.vala
new file mode 100644
index 0000000..889ba41
--- /dev/null
+++ b/src/dash-box.vala
@@ -0,0 +1,230 @@
+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*-
+ *
+ * Copyright (C) 2011,2012 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Michael Terry <michael.terry@canonical.com>
+ */
+
+public class DashBox : Gtk.Box
+{
+ public Background? background { get; construct; default = null; }
+
+ public bool has_base { get; private set; default = false; }
+ public double base_alpha { get; private set; default = 1.0; }
+
+ private enum Mode
+ {
+ NORMAL,
+ PUSH_FADE_OUT,
+ PUSH_FADE_IN,
+ POP_FADE_OUT,
+ POP_FADE_IN,
+ }
+
+ private GreeterList pushed;
+ private Gtk.Widget orig = null;
+ private FadeTracker orig_tracker;
+ private int orig_height = -1;
+ private Mode mode;
+
+ public DashBox (Background bg)
+ {
+ Object (background: bg);
+ }
+
+ construct
+ {
+ mode = Mode.NORMAL;
+ }
+
+ /* Does not actually add w to this widget, as doing so would potentially mess with w's placement. */
+ public void set_base (Gtk.Widget? w)
+ {
+ return_if_fail (pushed == null);
+ return_if_fail (mode == Mode.NORMAL);
+
+ if (orig != null)
+ orig.size_allocate.disconnect (base_size_allocate_cb);
+ orig = w;
+
+ if (orig != null)
+ {
+ orig.size_allocate.connect (base_size_allocate_cb);
+ orig_tracker = new FadeTracker (orig);
+ orig_tracker.notify["alpha"].connect (() =>
+ {
+ base_alpha = orig_tracker.alpha;
+ queue_draw ();
+ });
+ orig_tracker.done.connect (fade_done_cb);
+ base_alpha = orig_tracker.alpha;
+ has_base = true;
+ }
+ else
+ {
+ orig_height = -1;
+ get_preferred_height (null, out orig_height); /* save height */
+
+ orig_tracker = null;
+ base_alpha = 1.0;
+ has_base = false;
+ }
+
+ queue_resize ();
+ }
+
+ public void push (GreeterList l)
+ {
+ /* This isn't designed to push more than one widget at a time yet */
+ return_if_fail (pushed == null);
+ return_if_fail (orig != null);
+ return_if_fail (mode == Mode.NORMAL);
+
+ get_preferred_height (null, out orig_height);
+ pushed = l;
+ pushed.fade_done.connect (fade_done_cb);
+ mode = Mode.PUSH_FADE_OUT;
+ orig_tracker.reset (FadeTracker.Mode.FADE_OUT);
+ queue_resize ();
+ }
+
+ public void pop ()
+ {
+ return_if_fail (pushed != null);
+ return_if_fail (orig != null);
+ return_if_fail (mode == Mode.NORMAL);
+
+ mode = Mode.POP_FADE_OUT;
+ pushed.fade_out ();
+ }
+
+ private void fade_done_cb ()
+ {
+ switch (mode)
+ {
+ case Mode.PUSH_FADE_OUT:
+ mode = Mode.PUSH_FADE_IN;
+ orig.hide ();
+ pushed.fade_in ();
+ break;
+ case Mode.PUSH_FADE_IN:
+ mode = Mode.NORMAL;
+ pushed.grab_focus ();
+ break;
+ case Mode.POP_FADE_OUT:
+ mode = Mode.POP_FADE_IN;
+ orig_tracker.reset (FadeTracker.Mode.FADE_IN);
+ orig.show ();
+ break;
+ case Mode.POP_FADE_IN:
+ mode = Mode.NORMAL;
+ pushed.fade_done.disconnect (fade_done_cb);
+ pushed.destroy ();
+ pushed = null;
+ queue_resize ();
+ orig.grab_focus ();
+ break;
+ }
+ }
+
+ private void base_size_allocate_cb ()
+ {
+ queue_resize ();
+ }
+
+ public override void get_preferred_height (out int min, out int nat)
+ {
+ if (orig == null)
+ {
+ /* Return cached height if we have it. This makes transitions between two base widgets smoother. */
+ if (orig_height >= 0)
+ {
+ min = orig_height;
+ nat = orig_height;
+ }
+ else
+ {
+ min = grid_size * GreeterList.DEFAULT_BOX_HEIGHT - GreeterList.BORDER * 2;
+ nat = grid_size * GreeterList.DEFAULT_BOX_HEIGHT - GreeterList.BORDER * 2;
+ }
+ }
+ else
+ {
+ if (pushed == null)
+ orig.get_preferred_height (out min, out nat);
+ else
+ {
+ pushed.selected_entry.get_preferred_height (out min, out nat);
+ min = int.max (orig_height, min);
+ nat = int.max (orig_height, nat);
+ }
+ }
+ }
+
+ public override void get_preferred_width (out int min, out int nat)
+ {
+ min = grid_size * GreeterList.BOX_WIDTH - GreeterList.BORDER * 2;
+ nat = grid_size * GreeterList.BOX_WIDTH - GreeterList.BORDER * 2;
+ }
+
+ public override bool draw (Cairo.Context c)
+ {
+ if (background != null)
+ {
+ int x, y;
+ background.translate_coordinates (this, 0, 0, out x, out y);
+ c.save ();
+ c.translate (x, y);
+ background.draw_full (c, Background.DrawFlags.NONE);
+ c.restore ();
+ }
+
+ /* Draw darker background with a rounded border */
+ var box_r = 0.3 * grid_size;
+ int box_y = 0;
+ int box_w;
+ int box_h;
+ get_preferred_width (null, out box_w);
+ get_preferred_height (null, out box_h);
+
+ if (mode == Mode.PUSH_FADE_OUT)
+ {
+ /* Grow dark bg to fit new pushed object */
+ var new_box_h = box_h - (int) ((box_h - orig_height) * base_alpha);
+ box_h = new_box_h;
+ }
+ else if (mode == Mode.POP_FADE_IN)
+ {
+ /* Shrink dark bg to fit orig */
+ var new_box_h = box_h - (int) ((box_h - orig_height) * base_alpha);
+ box_h = new_box_h;
+ }
+
+ c.save ();
+
+ CairoUtils.rounded_rectangle (c, 0, box_y, box_w, box_h, box_r);
+
+ c.set_source_rgba (0.1, 0.1, 0.1, 0.4);
+ c.fill_preserve ();
+
+ c.set_source_rgba (0.4, 0.4, 0.4, 0.4);
+ c.set_line_width (1);
+ c.stroke ();
+
+ c.restore ();
+
+ return base.draw (c);
+ }
+}