[gnome-maps] placeBubble: Large Wikipedia thumbnails



commit da8f112acaed84194dd6a932bf75de250df463f1
Author: James Westman <james flyingpimonster net>
Date:   Thu Aug 6 13:35:14 2020 -0500

    placeBubble: Large Wikipedia thumbnails
    
    The Wikipedia thumbnail, if available, is now shown much larger and at the top
    of the popover, rather than in place of the icon.

 data/gnome-maps.css                  |  10 +++
 data/ui/map-bubble.ui                |  15 +++++
 src/mapBubble.js                     |  18 ++++++
 src/org.gnome.Maps.src.gresource.xml |   1 +
 src/placeBubble.js                   |  29 +--------
 src/placeBubbleImage.js              | 118 +++++++++++++++++++++++++++++++++++
 6 files changed, 165 insertions(+), 26 deletions(-)
---
diff --git a/data/gnome-maps.css b/data/gnome-maps.css
index ea4977fe..55e60fb9 100644
--- a/data/gnome-maps.css
+++ b/data/gnome-maps.css
@@ -61,6 +61,16 @@
     padding-left: 6px;
 }
 
+.map-bubble {
+    /* This is so the Wikipedia image is flush against the borders of the popover */
+    padding: 0;
+}
+
+.no-margin-separator {
+  /* A separator with no margin, so it's flush with the widgets/borders around it */
+  margin: 0;
+}
+
 .bubble-title {
     font-size: large;
     font-weight: bold;
diff --git a/data/ui/map-bubble.ui b/data/ui/map-bubble.ui
index d94fc41a..d64d00b1 100644
--- a/data/ui/map-bubble.ui
+++ b/data/ui/map-bubble.ui
@@ -9,6 +9,21 @@
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
+        <child>
+          <object class="Gjs_PlaceBubbleImage" id="bubble-thumbnail">
+            <property name="visible">False</property>
+            <property name="can_focus">False</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSeparator" id="thumbnail-separator">
+            <property name="visible">False</property>
+            <property name="can_focus">False</property>
+            <style>
+              <class name="no-margin-separator"/>
+            </style>
+          </object>
+        </child>
         <child>
           <object class="GtkGrid" id="bubble-content-area">
             <property name="visible">True</property>
diff --git a/src/mapBubble.js b/src/mapBubble.js
index 5f4c82a5..f99bf9fb 100644
--- a/src/mapBubble.js
+++ b/src/mapBubble.js
@@ -71,6 +71,8 @@ class MapBubble extends Gtk.Popover {
         super._init(params);
         let ui = Utils.getUIObject('map-bubble', [ 'bubble-main-box',
                                                    'bubble-spinner',
+                                                   'bubble-thumbnail',
+                                                   'thumbnail-separator',
                                                    'bubble-main-stack',
                                                    'bubble-content-area',
                                                    'bubble-button-area',
@@ -80,6 +82,8 @@ class MapBubble extends Gtk.Popover {
                                                    'bubble-check-in-button',
                                                    'bubble-edit-button',
                                                    'bubble-favorite-button-image']);
+        this._thumbnail = ui.bubbleThumbnail;
+        this._thumbnailSeparator = ui.thumbnailSeparator;
         this._content = ui.bubbleContentArea;
         this._mainStack = ui.bubbleMainStack;
         this._spinner = ui.bubbleSpinner;
@@ -101,6 +105,8 @@ class MapBubble extends Gtk.Popover {
         }
 
         this.add(this._mainStack);
+
+        this.get_style_context().add_class("map-bubble");
     }
 
     get place() {
@@ -111,6 +117,18 @@ class MapBubble extends Gtk.Popover {
         return this._content;
     }
 
+    get thumbnail() {
+        return this._thumbnail.pixbuf;
+    }
+
+    set thumbnail(val) {
+        if (val) {
+            this._thumbnail.pixbuf = val;
+            this._thumbnail.visible = true;
+            this._thumbnailSeparator.visible = true;
+        }
+    }
+
     get loading() {
         return this._spinner.active;
     }
diff --git a/src/org.gnome.Maps.src.gresource.xml b/src/org.gnome.Maps.src.gresource.xml
index ab2e12f0..80f852ee 100644
--- a/src/org.gnome.Maps.src.gresource.xml
+++ b/src/org.gnome.Maps.src.gresource.xml
@@ -55,6 +55,7 @@
     <file>photonParser.js</file>
     <file>place.js</file>
     <file>placeBubble.js</file>
+    <file>placeBubbleImage.js</file>
     <file>placeEntry.js</file>
     <file>placeFormatter.js</file>
     <file>placeListRow.js</file>
diff --git a/src/placeBubble.js b/src/placeBubble.js
index 846a3d15..5d44139c 100644
--- a/src/placeBubble.js
+++ b/src/placeBubble.js
@@ -31,15 +31,14 @@ const ContactPlace = imports.contactPlace;
 const MapBubble = imports.mapBubble;
 const Overpass = imports.overpass;
 const Place = imports.place;
+const PlaceBubbleImage = imports.placeBubbleImage;
 const PlaceFormatter = imports.placeFormatter;
 const PlaceStore = imports.placeStore;
 const Utils = imports.utils;
 const Wikipedia = imports.wikipedia;
 
 // maximum dimension of thumbnails to fetch from Wikipedia
-const THUMBNAIL_FETCH_SIZE = 128;
-// final scaled size of cropped thumnail
-const THUMBNAIL_FINAL_SIZE = 70;
+const THUMBNAIL_FETCH_SIZE = 360;
 
 var PlaceBubble = GObject.registerClass({
     Properties: {
@@ -75,7 +74,6 @@ var PlaceBubble = GObject.registerClass({
 
         this._title = ui.labelTitle;
         this._boxContent = ui.boxContent;
-        this._gridContent = ui.gridContent;
         this._expandButton = ui.expandButton;
         this._expandedContent = ui.expandedContent;
         this._revealer = ui.contentRevealer;
@@ -284,28 +282,7 @@ var PlaceBubble = GObject.registerClass({
     }
 
     _onThumbnailComplete(thumbnail) {
-        if (thumbnail) {
-            // TODO: Add thumbnails back
-        }
-    }
-
-    // returns a cropped square-shaped thumbnail
-    _cropAndScaleThumbnail(thumbnail) {
-        let width = thumbnail.get_width();
-        let height = thumbnail.get_height();
-        let croppedThumbnail;
-
-        if (width > height) {
-            let x = (width - height) / 2;
-            croppedThumbnail = thumbnail.new_subpixbuf(x, 0, height, height);
-        } else {
-            let y = (height - width) / 2;
-            croppedThumbnail = thumbnail.new_subpixbuf(0, y, width, width);
-        }
-
-        return croppedThumbnail.scale_simple(THUMBNAIL_FINAL_SIZE,
-                                             THUMBNAIL_FINAL_SIZE,
-                                             GdkPixbuf.InterpType.BILINEAR);
+        this.thumbnail = thumbnail;
     }
 
     // clear the view widgets to be able to re-populate an updated place
diff --git a/src/placeBubbleImage.js b/src/placeBubbleImage.js
new file mode 100644
index 00000000..1745df26
--- /dev/null
+++ b/src/placeBubbleImage.js
@@ -0,0 +1,118 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2020 James Westman
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 GNOME Maps; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: James Westman <james flyingpimonster net>
+ */
+
+const Cairo = imports.cairo;
+const Gdk = imports.gi.Gdk;
+const GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+
+/* The maximum aspect ratio, after which the image will be cropped vertically */
+const MAX_ASPECT_RATIO = 1;
+
+var PlaceBubbleImage = GObject.registerClass(
+class PlaceBubbleImage extends Gtk.DrawingArea {
+    _init(params) {
+        super._init(params);
+
+        this._pixbuf = null;
+        this._cached = null;
+    }
+
+    get pixbuf() {
+        return this._pixbuf;
+    }
+
+    set pixbuf(val) {
+        /* crop the pixbuf to the max aspect ratio, if necessary */
+        if (val.height / val.width > MAX_ASPECT_RATIO) {
+            let y = (val.height - val.width * MAX_ASPECT_RATIO) / 2;
+            val = val.new_subpixbuf(0, y, val.width, val.width * MAX_ASPECT_RATIO);
+        }
+
+        this._pixbuf = val;
+        this.queue_resize();
+    }
+
+    vfunc_draw(cr) {
+        let [{x, y, width, height}, baseline] = this.get_allocated_size();
+
+        if (this._pixbuf === null || width === 0 || height === 0) {
+            return;
+        }
+
+        width *= this.scale_factor;
+        height *= this.scale_factor;
+
+        /* Cache surfaces so we don't have to do as much scaling */
+        if (this._cached === null || width !== this._cached.getWidth() || height !== 
this._cached.getHeight()) {
+            // create a new, scaled image
+            this._cached = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);
+
+            let cr_scaled = new Cairo.Context(this._cached);
+            cr_scaled.scale(width / this._pixbuf.width, height / this._pixbuf.height);
+            Gdk.cairo_set_source_pixbuf(cr_scaled, this._pixbuf, 0, 0);
+            cr_scaled.paint();
+        }
+
+        cr.save();
+
+        if (this.scale_factor !== 1) {
+            cr.scale(1 / this.scale_factor, 1 / this.scale_factor);
+        }
+
+        let popover = this.get_ancestor(Gtk.Popover);
+        if (popover) {
+            // clip the top corners to the rounded corner
+            let radius = popover.get_style_context()
+                                .get_property(Gtk.STYLE_PROPERTY_BORDER_RADIUS, popover.get_state_flags())
+                                * this.scale_factor;
+
+            // bottom left
+            cr.moveTo(x, y + height);
+            //cr.lineTo(x, y + radius);
+            cr.arc(x + radius, y + radius, radius, Math.PI, -Math.PI / 2.0);
+            cr.arc(x + width - radius, y + radius, radius, -Math.PI / 2.0, 0);
+            cr.lineTo(x + width, y + height);
+
+            cr.clip();
+        }
+
+        cr.setSourceSurface(this._cached, 0, 0);
+
+        cr.paint();
+        cr.restore();
+
+        return false;
+    }
+
+    vfunc_get_request_mode() {
+        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH;
+    }
+
+    vfunc_get_preferred_height_for_width(width) {
+        if (this._pixbuf) {
+            let height = (this._pixbuf.height / this._pixbuf.width) * width;
+            return [height, height];
+        } else {
+            return [0, 0];
+        }
+    }
+});


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