[gnome-shell] New widget: BoxPointer



commit 47dae0832ba18d2eeff9e91e1a457b78bb995045
Author: Colin Walters <walters verbum org>
Date:   Tue May 4 14:32:28 2010 -0400

    New widget: BoxPointer
    
    Implement a rounded box with an arrow pointer.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=613804

 js/ui/boxpointer.js |  175 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 175 insertions(+), 0 deletions(-)
---
diff --git a/js/ui/boxpointer.js b/js/ui/boxpointer.js
new file mode 100644
index 0000000..11e31f2
--- /dev/null
+++ b/js/ui/boxpointer.js
@@ -0,0 +1,175 @@
+const Lang = imports.lang;
+
+const Cairo = imports.cairo;
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
+
+/**
+ * BoxPointer:
+ * @side: A St.Side type; currently only St.Side.TOP is implemented
+ * @sourceActor: The side of the BoxPointer where the pointer is
+ *   constrained to be as big as the corresponding dimension of
+ *   @sourceActor. E.g. for St.Side.TOP, the BoxPointer is horizontally
+ *   constrained to be bigger than @sourceActor.
+ * @binProperties: Properties to set on contained bin
+ *
+ * An actor which displays a triangle "arrow" pointing to a given
+ * side.  The .bin property is a container in which content can be
+ * placed.  The arrow position may be controlled via setArrowOrigin().
+ *
+ */
+function BoxPointer(side, sourceActor, binProperties) {
+    this._init(side, sourceActor, binProperties);
+}
+
+BoxPointer.prototype = {
+    _init: function(arrowSide, sourceActor, binProperties) {
+        if (arrowSide != St.Side.TOP)
+            throw new Error("not implemented");
+        this._arrowSide = arrowSide;
+        this._sourceActor = sourceActor;
+        this._arrowOrigin = 0;
+        this.actor = new St.Bin({ x_fill: true,
+                                  y_fill: true });
+        this._container = new Shell.GenericContainer();
+        this.actor.set_child(this._container);
+        this._container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+        this._container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+        this._container.connect('allocate', Lang.bind(this, this._allocate));
+        this.bin = new St.Bin(binProperties);
+        this._container.add_actor(this.bin);
+        this._border = new St.DrawingArea();
+        this._border.connect('repaint', Lang.bind(this, this._drawBorder));
+        this._container.add_actor(this._border);
+        this.bin.raise(this._border);
+    },
+
+    _adjustAllocationForArrow: function(isWidth, alloc) {
+        let themeNode = this.actor.get_theme_node();
+        let found, borderWidth, base, rise;
+        [found, borderWidth] = themeNode.get_length('-arrow-border-width', false);
+        alloc.min_size += borderWidth * 2;
+        alloc.natural_size += borderWidth * 2;
+        if ((!isWidth && (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM))
+            || (isWidth && (this._arrowSide == St.Side.LEFT || this._arrowSide == St.Side.RIGHT))) {
+            let [found, rise] = themeNode.get_length('-arrow-rise', false);
+            alloc.min_size += rise;
+            alloc.natural_size += rise;
+        }
+    },
+
+    _getPreferredWidth: function(actor, forHeight, alloc) {
+        let [minInternalSize, natInternalSize] = this.bin.get_preferred_width(forHeight);
+        // The allocation property unlike get_allocation_box() doesn't trigger
+        // synchronous relayout, which would be bad here. We assume that the
+        // previous allocation of the button is fine.
+        let sourceAlloc = this._sourceActor.allocation;
+        let sourceWidth = sourceAlloc.x2 - sourceAlloc.x1;
+        alloc.min_size = Math.max(minInternalSize, sourceWidth);
+        alloc.natural_size = Math.max(natInternalSize, sourceWidth);
+        this._adjustAllocationForArrow(true, alloc);
+    },
+
+    _getPreferredHeight: function(actor, forWidth, alloc) {
+        let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
+        alloc.min_size = minSize;
+        alloc.natural_size = naturalSize;
+        this._adjustAllocationForArrow(false, alloc);
+    },
+
+    _allocate: function(actor, box, flags) {
+        let themeNode = this.actor.get_theme_node();
+        let found, borderWidth, borderRadius, rise, base;
+        [found, borderWidth] = themeNode.get_length('-arrow-border-width', false);
+        [found, rise] = themeNode.get_length('-arrow-rise', false);
+        let childBox = new Clutter.ActorBox();
+        let availWidth = box.x2 - box.x1;
+        let availHeight = box.y2 - box.y1;
+
+        childBox.x1 = 0
+        childBox.y1 = 0;
+        childBox.x2 = availWidth;
+        childBox.y2 = availHeight;
+        this._border.allocate(childBox, flags);
+        switch (this._arrowSide) {
+            case St.Side.TOP:
+                childBox.x1 = borderWidth;
+                childBox.y1 = rise + borderWidth;
+                childBox.x2 = availWidth - borderWidth;
+                childBox.y2 = availHeight - borderWidth;
+                break;
+            default:
+                break;
+        }
+        this.bin.allocate(childBox, flags);
+    },
+
+    _drawBorder: function(area) {
+        let themeNode = this.actor.get_theme_node();
+
+        let [sourceX, sourceY] = this._sourceActor.get_transformed_position();
+
+        let found, borderWidth, borderRadius, rise, base;
+        [found, borderWidth] = themeNode.get_length('-arrow-border-width', false);
+        [found, base] = themeNode.get_length('-arrow-base', false);
+        [found, rise] = themeNode.get_length('-arrow-rise', false);
+        [found, borderRadius] = themeNode.get_length('-arrow-border-radius', false);
+
+        let halfBorder = borderWidth / 2;
+
+        let borderColor = new Clutter.Color();
+        themeNode.get_color('-arrow-border-color', false, borderColor);
+        let backgroundColor = new Clutter.Color();
+        themeNode.get_color('-arrow-background-color', false, backgroundColor);
+
+        let [width, height] = area.get_surface_size();
+        let [boxWidth, boxHeight] = [width, height];
+        if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) {
+            boxHeight -= rise;
+        } else {
+            boxWidth -= rise;
+        }
+        let cr = area.get_context();
+        Clutter.cairo_set_source_color(cr, borderColor);
+        if (this._arrowSide == St.Side.TOP) {
+            cr.translate(0, rise);
+        }
+        cr.moveTo(borderRadius, halfBorder);
+        if (this._arrowSide == St.Side.TOP) {
+            cr.translate(0, -rise);
+            let halfBase = Math.floor(base/2);
+            cr.lineTo(this._arrowOrigin - halfBase, rise + halfBorder);
+            cr.lineTo(this._arrowOrigin, halfBorder);
+            cr.lineTo(this._arrowOrigin + halfBase, rise + halfBorder);
+            cr.translate(0, rise);
+        }
+        cr.lineTo(boxWidth - borderRadius, halfBorder);
+        cr.arc(boxWidth - borderRadius - halfBorder, borderRadius + halfBorder, borderRadius,
+               3*Math.PI/2, Math.PI*2);
+        cr.lineTo(boxWidth - halfBorder, boxHeight - borderRadius);
+        cr.arc(boxWidth - borderRadius - halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
+               0, Math.PI/2);
+        cr.lineTo(borderRadius, boxHeight - halfBorder);
+        cr.arc(borderRadius + halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
+               Math.PI/2, Math.PI);
+        cr.lineTo(halfBorder, borderRadius);
+        cr.arc(borderRadius + halfBorder, borderRadius + halfBorder, borderRadius,
+               Math.PI, 3*Math.PI/2);
+        Clutter.cairo_set_source_color(cr, backgroundColor);
+        cr.fillPreserve();
+        Clutter.cairo_set_source_color(cr, borderColor);
+        cr.setLineWidth(borderWidth);
+        cr.stroke();
+    },
+
+    // @origin: Coordinate specifying middle of the arrow, along
+    // the Y axis for St.Side.LEFT, St.Side.RIGHT from the top and X axis from
+    // the left for St.Side.TOP and St.Side.BOTTOM.
+    setArrowOrigin: function(origin) {
+        if (this._arrowOrigin != origin) {
+            this._arrowOrigin = origin;
+            this._border.queue_repaint();
+        }
+    }
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]