From fd81565c5527b99dd212b03c2f329a00e146f03b Mon Sep 17 00:00:00 2001 From: Rory MacQueen Date: Thu, 15 Aug 2013 15:41:36 -0700 Subject: Reorganized import paths Moved all widgets into widgets directory. Changed Endless Wikipedia file to expose wikipedia web view [endlessm/eos-sdk#260] --- wikipedia/ArticleList.js | 2 +- wikipedia/BackButton.js | 18 --- wikipedia/BoxWithBg.js | 19 --- wikipedia/EndlessWikipedia.js | 2 +- wikipedia/Makefile.am.inc | 20 ++-- wikipedia/PrebuiltArticlesPage.js | 5 +- wikipedia/PrebuiltCategoryPage.js | 4 +- wikipedia/PrebuiltFrontPage.js | 4 +- wikipedia/PrebuiltWikipediaApplication.js | 6 +- wikipedia/SideBarButton.js | 39 ------- wikipedia/TextButton.js | 58 ---------- wikipedia/WikipediaApplication.js | 4 +- wikipedia/WikipediaWebView.js | 45 ++++++++ wikipedia/models/category_model.js | 2 +- wikipedia/models/domain_wiki_model.js | 4 +- wikipedia/presenters/domain_wiki_presenter.js | 6 +- wikipedia/scaled_image.js | 159 -------------------------- wikipedia/views/category_button.js | 146 ----------------------- wikipedia/views/category_layout_manager.js | 65 ----------- wikipedia/views/category_selector_view.js | 43 ------- wikipedia/views/domain_wiki_view.js | 6 +- wikipedia/views/title_label_view.js | 95 --------------- wikipedia/views/wikipedia_view.js | 45 -------- wikipedia/widgets/BackButton.js | 18 +++ wikipedia/widgets/BoxWithBg.js | 19 +++ wikipedia/widgets/SideBarButton.js | 39 +++++++ wikipedia/widgets/TextButton.js | 58 ++++++++++ wikipedia/widgets/category_button.js | 146 +++++++++++++++++++++++ wikipedia/widgets/category_layout_manager.js | 65 +++++++++++ wikipedia/widgets/category_selector_view.js | 43 +++++++ wikipedia/widgets/scaled_image.js | 159 ++++++++++++++++++++++++++ wikipedia/widgets/title_label_view.js | 95 +++++++++++++++ 32 files changed, 719 insertions(+), 720 deletions(-) delete mode 100644 wikipedia/BackButton.js delete mode 100644 wikipedia/BoxWithBg.js delete mode 100644 wikipedia/SideBarButton.js delete mode 100644 wikipedia/TextButton.js create mode 100644 wikipedia/WikipediaWebView.js delete mode 100644 wikipedia/scaled_image.js delete mode 100644 wikipedia/views/category_button.js delete mode 100644 wikipedia/views/category_layout_manager.js delete mode 100644 wikipedia/views/category_selector_view.js delete mode 100644 wikipedia/views/title_label_view.js delete mode 100644 wikipedia/views/wikipedia_view.js create mode 100644 wikipedia/widgets/BackButton.js create mode 100644 wikipedia/widgets/BoxWithBg.js create mode 100644 wikipedia/widgets/SideBarButton.js create mode 100644 wikipedia/widgets/TextButton.js create mode 100644 wikipedia/widgets/category_button.js create mode 100644 wikipedia/widgets/category_layout_manager.js create mode 100644 wikipedia/widgets/category_selector_view.js create mode 100644 wikipedia/widgets/scaled_image.js create mode 100644 wikipedia/widgets/title_label_view.js (limited to 'wikipedia') diff --git a/wikipedia/ArticleList.js b/wikipedia/ArticleList.js index 05f4470..d6edb0f 100644 --- a/wikipedia/ArticleList.js +++ b/wikipedia/ArticleList.js @@ -3,7 +3,7 @@ const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang; -const TextButton = imports.TextButton; +const TextButton = imports.wikipedia.widgets.TextButton; const ARTICLE_LIST_SIZE_REQUEST = 320; const HOVER_ARROW_URI = "/com/endlessm/wikipedia-domain/assets/submenu_hover_arrow.png"; diff --git a/wikipedia/BackButton.js b/wikipedia/BackButton.js deleted file mode 100644 index cfb3155..0000000 --- a/wikipedia/BackButton.js +++ /dev/null @@ -1,18 +0,0 @@ -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -const BackButton = new Lang.Class({ - Name: 'BackButton', - Extends: Gtk.Button, - - _init: function(props) { - props = props || {}; - props.image = Gtk.Image.new_from_icon_name('go-previous-symbolic', - Gtk.IconSize.BUTTON); - props.always_show_image = true; - // Don't do that. What should actually happen is the system-wide setting - // that controls whether buttons show images should be changed. - this.parent(props); - } -}); diff --git a/wikipedia/BoxWithBg.js b/wikipedia/BoxWithBg.js deleted file mode 100644 index 1ae9404..0000000 --- a/wikipedia/BoxWithBg.js +++ /dev/null @@ -1,19 +0,0 @@ -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -const BoxWithBg = new Lang.Class({ - Name: "BoxWithBg", - Extends: Gtk.Box, - - vfunc_draw: function(cairoContext) { - let width = this.get_allocated_width(); - let height = this.get_allocated_height(); - let context = this.get_style_context(); - Gtk.render_background(context, cairoContext, 0, 0, width, height); - Gtk.render_frame(context, cairoContext, 0, 0, width, height); - - return this.parent(cairoContext); - } -}); - diff --git a/wikipedia/EndlessWikipedia.js b/wikipedia/EndlessWikipedia.js index 333f486..25df16c 100644 --- a/wikipedia/EndlessWikipedia.js +++ b/wikipedia/EndlessWikipedia.js @@ -1,5 +1,4 @@ const Endless = imports.gi.Endless; -imports.searchPath.unshift(Endless.getCurrentFileDir()); // Pull modules into this namespace, sort of like __init__.py and __all__ this.WikipediaApplication = imports.wikipedia.WikipediaApplication.WikipediaApplication; @@ -8,6 +7,7 @@ this.PrebuiltFrontPage = imports.wikipedia.PrebuiltFrontPage.PrebuiltFrontPage; this.PrebuiltCategoryPage = imports.wikipedia.PrebuiltCategoryPage.PrebuiltCategoryPage; this.PrebuiltArticlesPage = imports.wikipedia.PrebuiltArticlesPage.PrebuiltArticlesPage; this.ArticleList = imports.wikipedia.ArticleList.ArticleList; +this.WikipediaWebView = imports.wikipedia.WikipediaWebView.WikipediaWebView; const STYLE_CLASS_TITLE = 'title'; const STYLE_CLASS_PREBUILT = 'prebuilt'; diff --git a/wikipedia/Makefile.am.inc b/wikipedia/Makefile.am.inc index 038082a..572788b 100644 --- a/wikipedia/Makefile.am.inc +++ b/wikipedia/Makefile.am.inc @@ -22,17 +22,17 @@ EXTRA_DIST += wikipedia/config.js.in js_sources = \ wikipedia/ArticleList.js \ - wikipedia/BackButton.js \ - wikipedia/BoxWithBg.js \ + wikipedia/widgets/BackButton.js \ + wikipedia/widgets/BoxWithBg.js \ wikipedia/EndlessWikipedia.js \ wikipedia/PrebuiltArticlesPage.js \ wikipedia/PrebuiltCategoryPage.js \ wikipedia/PrebuiltFrontPage.js \ wikipedia/PrebuiltWikipediaApplication.js \ - wikipedia/SideBarButton.js \ - wikipedia/TextButton.js \ + wikipedia/widgets/SideBarButton.js \ + wikipedia/widgets/TextButton.js \ wikipedia/config.js \ - wikipedia/scaled_image.js \ + wikipedia/widgets/scaled_image.js \ wikipedia/utils.js \ wikipedia/WikipediaApplication.js \ wikipedia/models/article_model.js \ @@ -41,12 +41,12 @@ js_sources = \ wikipedia/models/utils/json_utils.js \ wikipedia/models/utils/locale_utils.js \ wikipedia/presenters/domain_wiki_presenter.js \ - wikipedia/views/category_button.js \ - wikipedia/views/category_layout_manager.js \ - wikipedia/views/category_selector_view.js \ + wikipedia/widgets/category_button.js \ + wikipedia/widgets/category_layout_manager.js \ + wikipedia/widgets/category_selector_view.js \ wikipedia/views/domain_wiki_view.js \ - wikipedia/views/title_label_view.js \ - wikipedia/views/wikipedia_view.js \ + wikipedia/widgets/title_label_view.js \ + wikipedia/WikipediaWebView.js \ $(NULL) parafernalia_sources = \ diff --git a/wikipedia/PrebuiltArticlesPage.js b/wikipedia/PrebuiltArticlesPage.js index 933e3b4..59935ac 100644 --- a/wikipedia/PrebuiltArticlesPage.js +++ b/wikipedia/PrebuiltArticlesPage.js @@ -3,7 +3,6 @@ const Gtk = imports.gi.Gtk; const Lang = imports.lang; const EndlessWikipedia = imports.wikipedia.EndlessWikipedia; -const WikipediaView = imports.views.wikipedia_view; GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE; @@ -27,8 +26,8 @@ const PrebuiltArticlesPage = new Lang.Class({ this._article_title = null; this._article_uri = null; - this._wiki_view = new WikipediaView.WikipediaView({ - expand:true, + this._wiki_view = new EndlessWikipedia.WikipediaWebView({ + expand:true }); this.parent(props); diff --git a/wikipedia/PrebuiltCategoryPage.js b/wikipedia/PrebuiltCategoryPage.js index 5ee3e23..85484b9 100644 --- a/wikipedia/PrebuiltCategoryPage.js +++ b/wikipedia/PrebuiltCategoryPage.js @@ -4,8 +4,8 @@ const GdkPixbuf = imports.gi.GdkPixbuf; const Gtk = imports.gi.Gtk; const Lang = imports.lang; -const BoxWithBg = imports.BoxWithBg; -const ScaledImage = imports.scaled_image; +const BoxWithBg = imports.wikipedia.widgets.BoxWithBg; +const ScaledImage = imports.wikipedia.widgets.scaled_image; const CATEGORY_DESCRIPTION_WIDTH = 520; const SUBMENU_SEPARATOR_A_URI = "/com/endlessm/wikipedia-domain/assets/submenu_separator_shadow_a.png"; diff --git a/wikipedia/PrebuiltFrontPage.js b/wikipedia/PrebuiltFrontPage.js index 352220d..5da89f0 100644 --- a/wikipedia/PrebuiltFrontPage.js +++ b/wikipedia/PrebuiltFrontPage.js @@ -3,8 +3,8 @@ const Gtk = imports.gi.Gtk; const Lang = imports.lang; const EndlessWikipedia = imports.wikipedia.EndlessWikipedia; -const CategorySelectorView = imports.views.category_selector_view; -const TitleLabelView = imports.views.title_label_view; +const CategorySelectorView = imports.wikipedia.widgets.category_selector_view; +const TitleLabelView = imports.wikipedia.widgets.title_label_view; const TITLE_CATEGORY_COLUMN_SPACING = 10; // pixels diff --git a/wikipedia/PrebuiltWikipediaApplication.js b/wikipedia/PrebuiltWikipediaApplication.js index 9747330..d6e2e5b 100644 --- a/wikipedia/PrebuiltWikipediaApplication.js +++ b/wikipedia/PrebuiltWikipediaApplication.js @@ -4,9 +4,9 @@ const Lang = imports.lang; const Gtk = imports.gi.Gtk; const EndlessWikipedia = imports.wikipedia.EndlessWikipedia; -const DomainWikiView = imports.views.domain_wiki_view; -const DomainWikiModel = imports.models.domain_wiki_model; -const DomainWikiPresenter = imports.presenters.domain_wiki_presenter; +const DomainWikiView = imports.wikipedia.views.domain_wiki_view; +const DomainWikiModel = imports.wikipedia.models.domain_wiki_model; +const DomainWikiPresenter = imports.wikipedia.presenters.domain_wiki_presenter; String.prototype.format = Format.format; diff --git a/wikipedia/SideBarButton.js b/wikipedia/SideBarButton.js deleted file mode 100644 index b602d6c..0000000 --- a/wikipedia/SideBarButton.js +++ /dev/null @@ -1,39 +0,0 @@ -const Lang = imports.lang; -const Gdk = imports.gi.Gdk; -const GdkPixbuf = imports.gi.GdkPixbuf; -const Gtk = imports.gi.Gtk; - -const SideBarButton = new Lang.Class({ - Name: 'EndlessSideBarButton', - Extends: Gtk.Button, - - // This is a button for the article list widget. It has a label and an icon image. - // The icon image will only appear on hover or press of button - _init: function(hover_icon_path, params) { - this.parent(params); - - this.set_size_request(40, -1); - - this._image = new Gtk.Image({ - resource: hover_icon_path, - no_show_all: true - }); - - this.add(this._image); - - this.connect('state-changed', Lang.bind(this, this._update_appearance)); - }, - - _update_appearance: function(widget, state) { - // If button is hovered over and/or pressed, then show the arrow icon - if (widget.get_state_flags() & Gtk.StateFlags.ACTIVE || - widget.get_state_flags() & Gtk.StateFlags.PRELIGHT) { - this._image.show(); - return false; // don't block event - } - // If no hover or press, then hide the arrow icon - this._image.hide(); - return false; // don't block event - } -}); - diff --git a/wikipedia/TextButton.js b/wikipedia/TextButton.js deleted file mode 100644 index 834826f..0000000 --- a/wikipedia/TextButton.js +++ /dev/null @@ -1,58 +0,0 @@ -const Lang = imports.lang; -const Gdk = imports.gi.Gdk; -const GdkPixbuf = imports.gi.GdkPixbuf; -const Gtk = imports.gi.Gtk; -const Pango = imports.gi.Pango; - -// This is an approximate number of characters that will keep the label from -// going over its specified width -const ARTICLE_LABEL_MAX_WIDTH_CHARS = 24; - -const TextButton = new Lang.Class({ - Name: 'EndlessTextButton', - Extends: Gtk.Button, - - // This is a button for the article list widget. It has a label and an icon image. - // The icon image will only appear on hover or press of button - _init: function(hover_icon_path, label_text, params) { - params.hexpand = true; - this.parent(params); - - this._hover_icon_pixbuf = GdkPixbuf.Pixbuf.new_from_resource(hover_icon_path); - - this._image = new Gtk.Image({ - no_show_all: true - }); - this._image.set_from_pixbuf(this._hover_icon_pixbuf); - - this._box = new Gtk.Box({ - orientation: Gtk.Orientation.HORIZONTAL - }); - - this._label = new Gtk.Label({ - label: label_text.toUpperCase(), - max_width_chars: ARTICLE_LABEL_MAX_WIDTH_CHARS, - ellipsize: Pango.EllipsizeMode.END - }); - - this._box.pack_start(this._label, false, false, 0); - this._box.pack_end(this._image, false, false, 0); - - this.add(this._box); - this.connect('state-changed', Lang.bind(this, this._update_appearance)); - this.show_all(); - }, - - _update_appearance: function(widget, state) { - // If button is hovered over and/or pressed, then show the arrow icon - if (widget.get_state_flags() & Gtk.StateFlags.ACTIVE || - widget.get_state_flags() & Gtk.StateFlags.PRELIGHT) { - this._image.show(); - return false; - } - // If no hover or press, then hide the arrow icon - this._image.hide(); - return false; - } -}); - diff --git a/wikipedia/WikipediaApplication.js b/wikipedia/WikipediaApplication.js index a72b03b..152f770 100644 --- a/wikipedia/WikipediaApplication.js +++ b/wikipedia/WikipediaApplication.js @@ -5,8 +5,8 @@ const Lang = imports.lang; const GObject = imports.gi.GObject; const Gio = imports.gi.Gio; -const Config = imports.config; -const DomainWikiModel = imports.models.domain_wiki_model; +const Config = imports.wikipedia.config; +const DomainWikiModel = imports.wikipedia.models.domain_wiki_model; GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE; diff --git a/wikipedia/WikipediaWebView.js b/wikipedia/WikipediaWebView.js new file mode 100644 index 0000000..c820c8f --- /dev/null +++ b/wikipedia/WikipediaWebView.js @@ -0,0 +1,45 @@ +const Gio = imports.gi.Gio; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; +const Soup = imports.gi.Soup; +const WebKit = imports.gi.WebKit2; +const Utils = imports.wikipedia.utils; + +const getPageURL = "http://127.0.0.1:3000/getDomainSpecificArticle?title="; + +// Interpret image:// URIs as wikipedia images +WebKit.WebContext.get_default().register_uri_scheme('image', function(request) { + let filename = request.get_uri().slice('image://'.length); + filename = decodeURI(filename); + let pictures_dir = request.get_web_view()._getArticleImagesPath(); + let parent = Gio.File.new_for_path(pictures_dir); + let file = parent.get_child(filename); + let stream = file.read(null); + request.finish(stream, -1, 'image/png'); +}); + +const WikipediaWebView = new Lang.Class({ + Name: 'EndlessWikipediaWebView', + Extends: WebKit.WebView, + + _init: function(params) { + this.parent(params); + // For debugging + //let settings = this.get_settings(); + //settings.set_enable_developer_extras(true); + //this.set_settings(settings); + this.connect('context-menu', Lang.bind(this, function(){return true})); + }, + + loadArticleByTitle: function(url) { + let parts = url.split("/"); + let suffix = parts[parts.length - 1]; + let title = decodeURI(suffix.replace("_", " ", 'g')); + this.load_uri(getPageURL + title); + }, + + _getArticleImagesPath: function() { + let cur_exec = this.get_toplevel().get_application().application_base_path; + return cur_exec + "/web_view/article_images/"; + } +}); diff --git a/wikipedia/models/category_model.js b/wikipedia/models/category_model.js index ea9bfb6..1c981bc 100644 --- a/wikipedia/models/category_model.js +++ b/wikipedia/models/category_model.js @@ -3,7 +3,7 @@ const GObject = imports.gi.GObject; const Lang = imports.lang; // Local libraries -const ArticleModel = imports.models.article_model; +const ArticleModel = imports.wikipedia.models.article_model; const CategoryModel = new Lang.Class({ Name: "CategoryModel", diff --git a/wikipedia/models/domain_wiki_model.js b/wikipedia/models/domain_wiki_model.js index c838361..87941ba 100644 --- a/wikipedia/models/domain_wiki_model.js +++ b/wikipedia/models/domain_wiki_model.js @@ -5,8 +5,8 @@ const GObject = imports.gi.GObject; const Lang = imports.lang; // Local libraries -const CategoryModel = imports.models.category_model; -const Utils = imports.utils; +const CategoryModel = imports.wikipedia.models.category_model; +const Utils = imports.wikipedia.utils; const DomainWikiModel = new Lang.Class({ diff --git a/wikipedia/presenters/domain_wiki_presenter.js b/wikipedia/presenters/domain_wiki_presenter.js index bdc8d0f..15229ce 100644 --- a/wikipedia/presenters/domain_wiki_presenter.js +++ b/wikipedia/presenters/domain_wiki_presenter.js @@ -2,10 +2,10 @@ const Lang = imports.lang; const GObject = imports.gi.GObject; //Local Libraries -const Utils = imports.utils; +const Utils = imports.wikipedia.utils; -const CategoryModel = imports.models.category_model; -const ArticleModel = imports.models.article_model; +const CategoryModel = imports.wikipedia.models.category_model; +const ArticleModel = imports.wikipedia.models.article_model; function _resourceUriToPath(uri) { diff --git a/wikipedia/scaled_image.js b/wikipedia/scaled_image.js deleted file mode 100644 index 9808914..0000000 --- a/wikipedia/scaled_image.js +++ /dev/null @@ -1,159 +0,0 @@ -const Format = imports.format; -const GdkPixbuf = imports.gi.GdkPixbuf; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -String.prototype.format = Format.format; -GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE; - -const BACKGROUND_CSS_TEMPLATE = "\ -Gjs_ScaledImage {\n\ - background-image: url('resource://%s');\n\ - background-size: %s;\n\ - background-position: %s %s;\n\ - background-repeat: no-repeat;\n\ -}"; - -const ScaledImage = new Lang.Class({ - Name: 'ScaledImage', - Extends: Gtk.EventBox, - Properties: { - 'constraint': GObject.ParamSpec.enum('constraint', - 'Constraint direction', - 'Orientation in which the size of the image should be constrained', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - Gtk.Orientation, Gtk.Orientation.HORIZONTAL), - 'resource': GObject.ParamSpec.string('resource', - 'Resource path', - 'Resource path for the image', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - '') - }, - - _init: function(props) { - this._constraint = null; - this._resource_path = null; - this._pixbuf = null; - this._css_provider = null; - this.parent(props); - }, - - // OVERRIDES - - vfunc_get_request_mode: function() { - if(this._constraint == Gtk.Orientation.HORIZONTAL) - return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH; - return Gtk.SizeRequestMode.WIDTH_FOR_HEIGHT; - }, - - vfunc_get_preferred_width_for_height: function(height) { - if(!this._pixbuf) - return this.parent(height); - let source_width = this._pixbuf.width; - let source_height = this._pixbuf.height; - let width = (height / source_height) * source_width; - return [width, width]; - }, - - vfunc_get_preferred_height_for_width: function(width) { - if(!this._pixbuf) - return this.parent(width); - let source_width = this._pixbuf.width; - let source_height = this._pixbuf.height; - let height = (width / source_width) * source_height; - return [height, height]; - }, - - vfunc_size_allocate: function(allocation) { - if(this._constraint == Gtk.Orientation.VERTICAL - && this.valign != Gtk.Align.FILL) { - printerr("ScaledImage Warning: Setting constraint to VERTICAL and\ - valign to anything but FILL makes no sense"); - this.valign = Gtk.Align.FILL; - } - if(this._constraint == Gtk.Orientation.HORIZONTAL - && this.halign != Gtk.Align.FILL) { - printerr("ScaledImage Warning: Setting constraint to HORIZONTAL and\ - halign to anything but FILL makes no sense"); - this.halign = Gtk.Align.FILL; - } - this.parent(allocation); - }, - - // PROPERTIES - - get constraint() { - return this._constraint; - }, - - set constraint(value) { - this._constraint = value; - }, - - get resource() { - return this._resource_path; - }, - - set resource(value) { - this._resource_path = value; - this._pixbuf = GdkPixbuf.Pixbuf.new_from_resource(this._resource_path); - this._updateImage(); - }, - - // PRIVATE - - _gtk_align_to_css_align: function(align, orientation) { - switch(align) { - case Gtk.Align.START: - if(orientation == Gtk.Orientation.VERTICAL) - return "top"; - return "left"; - case Gtk.Align.END: - if(orientation == Gtk.Orientation.VERTICAL) - return "bottom"; - return "right"; - } - return "center"; - }, - - _updateImage: function() { - if(this._resource_path === null) - return; - - let context = this.get_style_context(); - - if(this._css_provider !== null) - context.remove_provider(this._css_provider); - - let scaling; - if(this._constraint == Gtk.Orientation.HORIZONTAL) - scaling = "100% auto"; - else - scaling = "auto 100%"; - - let css = BACKGROUND_CSS_TEMPLATE.format(this._resource_path, scaling, - this._gtk_align_to_css_align(this.valign, Gtk.Orientation.VERTICAL), - this._gtk_align_to_css_align(this.halign, - Gtk.Orientation.HORIZONTAL)); - this._css_provider = new Gtk.CssProvider(); - this._css_provider.load_from_data(css); - context.add_provider(this._css_provider, - Gtk.STYLE_PROVIDER_PRIORITY_USER); - } -}); - -// const Gio = imports.gi.Gio; -// Gtk.init(null); -// let resource = Gio.Resource.load('data/endless_brazil.gresource'); -// resource._register(); -// let w = new Gtk.Window(); -// let i = new ScaledImage({ -// resource: '/com/endlessm/brazil/category_images/cuisine.jpg', -// constraint: Gtk.Orientation.HORIZONTAL, -// valign: Gtk.Align.END, -// }); -// w.add(i); -// w.connect('destroy', Gtk.main_quit); -// w.show_all(); -// Gtk.main(); diff --git a/wikipedia/views/category_button.js b/wikipedia/views/category_button.js deleted file mode 100644 index 7fde6a1..0000000 --- a/wikipedia/views/category_button.js +++ /dev/null @@ -1,146 +0,0 @@ -const Gdk = imports.gi.Gdk; -const GdkPixbuf = imports.gi.GdkPixbuf; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -const Utils = imports.utils; - -const CATEGORY_LABEL_LEFT_MARGIN = 25; // pixels -const CATEGORY_LABEL_BOTTOM_MARGIN = 20; // pixels -const CATEGORY_BUTTON_RIGHT_MARGIN = 20; // pixels -const CATEGORY_BUTTON_BOTTOM_MARGIN = 20; // pixels -const CATEGORY_LABEL_BENTON_SANS_CORRECTION = 0; // pixels -const _HOVER_ARROW_URI = '/com/endlessm/wikipedia-domain/assets/category_hover_arrow.png'; - -GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE; - -const CategoryButton = new Lang.Class({ - Name: 'CategoryButton', - Extends: Gtk.EventBox, - Properties: { - // resource URI for the category's accompanying image - 'image-uri': GObject.ParamSpec.string('image-uri', - 'Image URI', - 'Resource URI for the image file accompanying the category', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - ''), - - // Title of the category to display - 'category-title': GObject.ParamSpec.string('category-title', - 'Category title', - 'Display name for the category', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - '') - }, - Signals: { - 'clicked': {} - }, - - _init: function(props) { - // Get ready for property construction - this._image_uri = null; - this._category_title = null; - - this._overlay = new Gtk.Overlay(); - this._eventbox = new Gtk.EventBox({ - expand: true - }); - this._eventbox_grid = new Gtk.Grid({ - orientation: Gtk.Orientation.HORIZONTAL, - hexpand: true, - valign: Gtk.Align.END - }); - this._label = new Gtk.Label({ - margin_left: CATEGORY_LABEL_LEFT_MARGIN, - margin_bottom: CATEGORY_LABEL_BOTTOM_MARGIN - CATEGORY_LABEL_BENTON_SANS_CORRECTION, - hexpand: true, - halign: Gtk.Align.START - }); - this._arrow = new Gtk.Image({ - resource: _HOVER_ARROW_URI, - margin_right: CATEGORY_BUTTON_RIGHT_MARGIN, - margin_bottom: CATEGORY_BUTTON_BOTTOM_MARGIN - CATEGORY_LABEL_BENTON_SANS_CORRECTION, - halign: Gtk.Align.END, - no_show_all: true - }); - - this._eventbox.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK | - Gdk.EventMask.LEAVE_NOTIFY_MASK); - this._eventbox.connect('enter-notify-event', - Lang.bind(this, function(widget, event) { - this._eventbox.set_state_flags(Gtk.StateFlags.PRELIGHT, false); - this._arrow.show(); - })); - this._eventbox.connect('leave-notify-event', - Lang.bind(this, function(widget, event) { - this._eventbox.unset_state_flags(Gtk.StateFlags.PRELIGHT); - this._arrow.hide(); - })); - - let context = this._label.get_style_context(); - context.add_class(EndlessWikipedia.STYLE_CLASS_TITLE); - context.add_class(EndlessWikipedia.STYLE_CLASS_CATEGORY); - context.add_class(EndlessWikipedia.STYLE_CLASS_FRONT_PAGE); - this._image = new Gtk.Image({ - expand: true, - halign: Gtk.Align.FILL, - valign: Gtk.Align.FILL - }); - - // Parent constructor sets all properties - this.parent(props); - - // Put widgets together - this.add(this._overlay); - this._overlay.add(this._image); - this._eventbox_grid.add(this._label); - this._eventbox_grid.add(this._arrow); - this._eventbox.add(this._eventbox_grid); - this._overlay.add_overlay(this._eventbox); - this.show_all(); - - // Connect signals - this.connect('button-press-event', - Lang.bind(this, this._onButtonPress)); - }, - - get image_uri() { - return this._image_uri; - }, - - set image_uri(value) { - this._image_uri = value; - if(this._image) { - let allocation = this.get_allocation(); - let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), - allocation.width, allocation.height); - this._image.set_from_pixbuf(new_pixbuf); - } - }, - - get category_title() { - return this._category_title; - }, - - set category_title(value) { - this._category_title = value; - if(this._label) - this._label.set_text(value.toUpperCase()); - }, - - // OVERRIDES - - vfunc_size_allocate: function(allocation) { - this.parent(allocation); - let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), - allocation.width, allocation.height); - this._image.set_from_pixbuf(new_pixbuf); - }, - - // HANDLERS - - _onButtonPress: function(widget, event) { - this.emit('clicked') - } -}); diff --git a/wikipedia/views/category_layout_manager.js b/wikipedia/views/category_layout_manager.js deleted file mode 100644 index a7be3bb..0000000 --- a/wikipedia/views/category_layout_manager.js +++ /dev/null @@ -1,65 +0,0 @@ -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -const CategoryLayoutManager = new Lang.Class({ - Name: 'CategoryLayoutManager', - Extends: Gtk.Grid, - - _init: function(props) { - props = props || {}; - props.column_homogeneous = true; - props.row_homogeneous = true; - this.parent(props); - - this._childWidgets = []; - }, - - // Distribute children in two columns, except for the last one if an odd - // number; that should span two columns - _redistributeChildren: function() { - let numChildren = this._childWidgets.length; - let oddNumber = numChildren % 2 == 1; - this._childWidgets.forEach(function(child, index) { - let column = index % 2; - let row = Math.floor(index / 2); - - if(child.get_parent() === this) - Gtk.Container.prototype.remove.call(this, - this._childWidgets[index]); - - if(oddNumber && index == numChildren - 1) - this.attach(child, 0, row, 2, 1); - else - this.attach(child, column, row, 1, 1); - }, this); - }, - - add: function(child) { - this._childWidgets.push(child); - this._redistributeChildren(); - }, - - remove: function(child) { - let index = this._childWidgets.indexOf(child); - if(index == -1) { - printerr('Widget', System.addressOf(child), - 'is not contained in CategoryLayoutManager'); - return; - } - this._childWidgets.splice(index, 1); // remove - this._redistributeChildren(); - } -}); - -// Gtk.init(null); -// let w = new Gtk.Window(); -// let g = new CategoryLayoutManager(); -// let count = 7; -// for(let i = 0; i < count; i++) { -// let widget = new Gtk.Button({label: 'Widget ' + i}); -// g.add(widget); -// } -// w.add(g); -// w.connect('destroy', Gtk.main_quit); -// w.show_all(); -// Gtk.main(); diff --git a/wikipedia/views/category_selector_view.js b/wikipedia/views/category_selector_view.js deleted file mode 100644 index 5432a3e..0000000 --- a/wikipedia/views/category_selector_view.js +++ /dev/null @@ -1,43 +0,0 @@ -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -const CategoryButton = imports.views.category_button; -const CategoryLayoutManager = imports.views.category_layout_manager; - -const CATEGORY_COLUMN_SPACING = 10; // pixels -const CATEGORY_ROW_SPACING = 10; // pixels - -const CategorySelectorView = new Lang.Class({ - Name: 'CategorySelectorView', - Extends: CategoryLayoutManager.CategoryLayoutManager, - Signals: { - 'category-chosen': { - param_types: [GObject.TYPE_STRING, GObject.TYPE_INT] - } - }, - - _init: function(props) { - props = props || {}; - props.column_spacing = CATEGORY_COLUMN_SPACING; - props.row_spacing = CATEGORY_ROW_SPACING; - this.parent(props); - }, - - // Takes an array of dictionaries with keys 'title' and 'image_uri' - setCategories: function(categories) { - categories.forEach(function(category, index, obj) { - let button = new CategoryButton.CategoryButton({ - category_title: category.title, - image_uri: category.image_thumbnail_uri - }); - button.index = index; - button.connect('clicked', Lang.bind(this, this._onButtonClicked)); - this.add(button); - }, this); - }, - - _onButtonClicked: function(button) { - this.emit('category-chosen', button.category_title, button.index); - } -}); \ No newline at end of file diff --git a/wikipedia/views/domain_wiki_view.js b/wikipedia/views/domain_wiki_view.js index 448f2d5..9dd8358 100644 --- a/wikipedia/views/domain_wiki_view.js +++ b/wikipedia/views/domain_wiki_view.js @@ -6,9 +6,9 @@ const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Endless = imports.gi.Endless; -const Config = imports.config; -const BackButton = imports.BackButton; -const SideBarButton = imports.SideBarButton; +const Config = imports.wikipedia.config; +const BackButton = imports.wikipedia.widgets.BackButton; +const SideBarButton = imports.wikipedia.widgets.SideBarButton; const _ = function(string) { return GLib.dgettext('eos-sdk', string); }; Gettext.bindtextdomain('eos-sdk', Config.DATADIR + '/locale'); diff --git a/wikipedia/views/title_label_view.js b/wikipedia/views/title_label_view.js deleted file mode 100644 index 479747d..0000000 --- a/wikipedia/views/title_label_view.js +++ /dev/null @@ -1,95 +0,0 @@ -const GdkPixbuf = imports.gi.GdkPixbuf; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; - -const Utils = imports.utils; - -const TITLE_LABEL_SCREEN_WIDTH_PERCENTAGE = 0.37; -const TITLE_LABEL_LEFT_MARGIN = 20; // pixels -const TITLE_LABEL_BOTTOM_MARGIN = 20; // pixels -const TITLE_LABEL_BENTON_SANS_CORRECTION = 20; // pixels - -const TitleLabelView = new Lang.Class({ - Name: 'TitleLabelView', - Extends: Gtk.Overlay, - Properties: { - 'title': GObject.ParamSpec.string('title', - 'Front page title', - 'Name of the Wikipedia-based application', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - ''), - 'image-uri': GObject.ParamSpec.string('image-uri', - 'Image URI', - 'Image URI for title image', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - '') - }, - - _init: function(props) { - this._title = null; - this._image_uri = null; - this._label = new Gtk.Label({ - halign: Gtk.Align.START, - valign: Gtk.Align.END, - margin_left: TITLE_LABEL_LEFT_MARGIN, - margin_bottom: TITLE_LABEL_BOTTOM_MARGIN - TITLE_LABEL_BENTON_SANS_CORRECTION - }); - this._image = new Gtk.Image(); - - let context = this._label.get_style_context() - context.add_class(EndlessWikipedia.STYLE_CLASS_TITLE); - - this.parent(props); - - this.add(this._image); - this.add_overlay(this._label); - }, - - // OVERRIDES - - // Ensure that this widget is 37% of the window's width - vfunc_get_preferred_width: function() { - let toplevel = this.get_toplevel(); - if(toplevel == null) - return this.parent(); - let width = toplevel.get_allocated_width() * TITLE_LABEL_SCREEN_WIDTH_PERCENTAGE; - return [width, width]; - }, - - vfunc_size_allocate: function(allocation) { - this.parent(allocation); - if(this._image_uri !== "" && this._image_uri != null) { - let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), - allocation.width, allocation.height); - this._image.set_from_pixbuf(new_pixbuf); - } - }, - - // PROPERTIES - - get title() { - return this._title; - }, - - set title(value) { - this._title = value; - if(this._label) - this._label.label = value.toUpperCase(); - }, - - get image_uri() { - return this._image_uri; - }, - - set image_uri(value) { - this._image_uri = value; - if(this._image) { - let res_path = Utils.resourceUriToPath(value); - let allocation = this.get_allocation(); - let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), - allocation.width, allocation.height); - this._image.set_from_pixbuf(new_pixbuf); - } - } -}); diff --git a/wikipedia/views/wikipedia_view.js b/wikipedia/views/wikipedia_view.js deleted file mode 100644 index bcd6270..0000000 --- a/wikipedia/views/wikipedia_view.js +++ /dev/null @@ -1,45 +0,0 @@ -const Gio = imports.gi.Gio; -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; -const Soup = imports.gi.Soup; -const WebKit = imports.gi.WebKit2; -const Utils = imports.utils; - -const getPageURL = "http://127.0.0.1:3000/getDomainSpecificArticle?title="; - -// Interpret image:// URIs as wikipedia images -WebKit.WebContext.get_default().register_uri_scheme('image', function(request) { - let filename = request.get_uri().slice('image://'.length); - filename = decodeURI(filename); - let pictures_dir = request.get_web_view()._getArticleImagesPath(); - let parent = Gio.File.new_for_path(pictures_dir); - let file = parent.get_child(filename); - let stream = file.read(null); - request.finish(stream, -1, 'image/png'); -}); - -const WikipediaView = new Lang.Class({ - Name: 'EndlessWikipediaView', - Extends: WebKit.WebView, - - _init: function(params) { - this.parent(params); - // For debugging - //let settings = this.get_settings(); - //settings.set_enable_developer_extras(true); - //this.set_settings(settings); - this.connect('context-menu', Lang.bind(this, function(){return true})); - }, - - loadArticleByTitle: function(url) { - let parts = url.split("/"); - let suffix = parts[parts.length - 1]; - let title = decodeURI(suffix.replace("_", " ", 'g')); - this.load_uri(getPageURL + title); - }, - - _getArticleImagesPath: function() { - let cur_exec = this.get_toplevel().get_application().application_base_path; - return cur_exec + "/web_view/article_images/"; - } -}); diff --git a/wikipedia/widgets/BackButton.js b/wikipedia/widgets/BackButton.js new file mode 100644 index 0000000..cfb3155 --- /dev/null +++ b/wikipedia/widgets/BackButton.js @@ -0,0 +1,18 @@ +const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +const BackButton = new Lang.Class({ + Name: 'BackButton', + Extends: Gtk.Button, + + _init: function(props) { + props = props || {}; + props.image = Gtk.Image.new_from_icon_name('go-previous-symbolic', + Gtk.IconSize.BUTTON); + props.always_show_image = true; + // Don't do that. What should actually happen is the system-wide setting + // that controls whether buttons show images should be changed. + this.parent(props); + } +}); diff --git a/wikipedia/widgets/BoxWithBg.js b/wikipedia/widgets/BoxWithBg.js new file mode 100644 index 0000000..1ae9404 --- /dev/null +++ b/wikipedia/widgets/BoxWithBg.js @@ -0,0 +1,19 @@ +const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +const BoxWithBg = new Lang.Class({ + Name: "BoxWithBg", + Extends: Gtk.Box, + + vfunc_draw: function(cairoContext) { + let width = this.get_allocated_width(); + let height = this.get_allocated_height(); + let context = this.get_style_context(); + Gtk.render_background(context, cairoContext, 0, 0, width, height); + Gtk.render_frame(context, cairoContext, 0, 0, width, height); + + return this.parent(cairoContext); + } +}); + diff --git a/wikipedia/widgets/SideBarButton.js b/wikipedia/widgets/SideBarButton.js new file mode 100644 index 0000000..b602d6c --- /dev/null +++ b/wikipedia/widgets/SideBarButton.js @@ -0,0 +1,39 @@ +const Lang = imports.lang; +const Gdk = imports.gi.Gdk; +const GdkPixbuf = imports.gi.GdkPixbuf; +const Gtk = imports.gi.Gtk; + +const SideBarButton = new Lang.Class({ + Name: 'EndlessSideBarButton', + Extends: Gtk.Button, + + // This is a button for the article list widget. It has a label and an icon image. + // The icon image will only appear on hover or press of button + _init: function(hover_icon_path, params) { + this.parent(params); + + this.set_size_request(40, -1); + + this._image = new Gtk.Image({ + resource: hover_icon_path, + no_show_all: true + }); + + this.add(this._image); + + this.connect('state-changed', Lang.bind(this, this._update_appearance)); + }, + + _update_appearance: function(widget, state) { + // If button is hovered over and/or pressed, then show the arrow icon + if (widget.get_state_flags() & Gtk.StateFlags.ACTIVE || + widget.get_state_flags() & Gtk.StateFlags.PRELIGHT) { + this._image.show(); + return false; // don't block event + } + // If no hover or press, then hide the arrow icon + this._image.hide(); + return false; // don't block event + } +}); + diff --git a/wikipedia/widgets/TextButton.js b/wikipedia/widgets/TextButton.js new file mode 100644 index 0000000..834826f --- /dev/null +++ b/wikipedia/widgets/TextButton.js @@ -0,0 +1,58 @@ +const Lang = imports.lang; +const Gdk = imports.gi.Gdk; +const GdkPixbuf = imports.gi.GdkPixbuf; +const Gtk = imports.gi.Gtk; +const Pango = imports.gi.Pango; + +// This is an approximate number of characters that will keep the label from +// going over its specified width +const ARTICLE_LABEL_MAX_WIDTH_CHARS = 24; + +const TextButton = new Lang.Class({ + Name: 'EndlessTextButton', + Extends: Gtk.Button, + + // This is a button for the article list widget. It has a label and an icon image. + // The icon image will only appear on hover or press of button + _init: function(hover_icon_path, label_text, params) { + params.hexpand = true; + this.parent(params); + + this._hover_icon_pixbuf = GdkPixbuf.Pixbuf.new_from_resource(hover_icon_path); + + this._image = new Gtk.Image({ + no_show_all: true + }); + this._image.set_from_pixbuf(this._hover_icon_pixbuf); + + this._box = new Gtk.Box({ + orientation: Gtk.Orientation.HORIZONTAL + }); + + this._label = new Gtk.Label({ + label: label_text.toUpperCase(), + max_width_chars: ARTICLE_LABEL_MAX_WIDTH_CHARS, + ellipsize: Pango.EllipsizeMode.END + }); + + this._box.pack_start(this._label, false, false, 0); + this._box.pack_end(this._image, false, false, 0); + + this.add(this._box); + this.connect('state-changed', Lang.bind(this, this._update_appearance)); + this.show_all(); + }, + + _update_appearance: function(widget, state) { + // If button is hovered over and/or pressed, then show the arrow icon + if (widget.get_state_flags() & Gtk.StateFlags.ACTIVE || + widget.get_state_flags() & Gtk.StateFlags.PRELIGHT) { + this._image.show(); + return false; + } + // If no hover or press, then hide the arrow icon + this._image.hide(); + return false; + } +}); + diff --git a/wikipedia/widgets/category_button.js b/wikipedia/widgets/category_button.js new file mode 100644 index 0000000..77812f1 --- /dev/null +++ b/wikipedia/widgets/category_button.js @@ -0,0 +1,146 @@ +const Gdk = imports.gi.Gdk; +const GdkPixbuf = imports.gi.GdkPixbuf; +const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +const Utils = imports.wikipedia.utils; + +const CATEGORY_LABEL_LEFT_MARGIN = 25; // pixels +const CATEGORY_LABEL_BOTTOM_MARGIN = 20; // pixels +const CATEGORY_BUTTON_RIGHT_MARGIN = 20; // pixels +const CATEGORY_BUTTON_BOTTOM_MARGIN = 20; // pixels +const CATEGORY_LABEL_BENTON_SANS_CORRECTION = 0; // pixels +const _HOVER_ARROW_URI = '/com/endlessm/wikipedia-domain/assets/category_hover_arrow.png'; + +GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE; + +const CategoryButton = new Lang.Class({ + Name: 'CategoryButton', + Extends: Gtk.EventBox, + Properties: { + // resource URI for the category's accompanying image + 'image-uri': GObject.ParamSpec.string('image-uri', + 'Image URI', + 'Resource URI for the image file accompanying the category', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, + ''), + + // Title of the category to display + 'category-title': GObject.ParamSpec.string('category-title', + 'Category title', + 'Display name for the category', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, + '') + }, + Signals: { + 'clicked': {} + }, + + _init: function(props) { + // Get ready for property construction + this._image_uri = null; + this._category_title = null; + + this._overlay = new Gtk.Overlay(); + this._eventbox = new Gtk.EventBox({ + expand: true + }); + this._eventbox_grid = new Gtk.Grid({ + orientation: Gtk.Orientation.HORIZONTAL, + hexpand: true, + valign: Gtk.Align.END + }); + this._label = new Gtk.Label({ + margin_left: CATEGORY_LABEL_LEFT_MARGIN, + margin_bottom: CATEGORY_LABEL_BOTTOM_MARGIN - CATEGORY_LABEL_BENTON_SANS_CORRECTION, + hexpand: true, + halign: Gtk.Align.START + }); + this._arrow = new Gtk.Image({ + resource: _HOVER_ARROW_URI, + margin_right: CATEGORY_BUTTON_RIGHT_MARGIN, + margin_bottom: CATEGORY_BUTTON_BOTTOM_MARGIN - CATEGORY_LABEL_BENTON_SANS_CORRECTION, + halign: Gtk.Align.END, + no_show_all: true + }); + + this._eventbox.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK | + Gdk.EventMask.LEAVE_NOTIFY_MASK); + this._eventbox.connect('enter-notify-event', + Lang.bind(this, function(widget, event) { + this._eventbox.set_state_flags(Gtk.StateFlags.PRELIGHT, false); + this._arrow.show(); + })); + this._eventbox.connect('leave-notify-event', + Lang.bind(this, function(widget, event) { + this._eventbox.unset_state_flags(Gtk.StateFlags.PRELIGHT); + this._arrow.hide(); + })); + + let context = this._label.get_style_context(); + context.add_class(EndlessWikipedia.STYLE_CLASS_TITLE); + context.add_class(EndlessWikipedia.STYLE_CLASS_CATEGORY); + context.add_class(EndlessWikipedia.STYLE_CLASS_FRONT_PAGE); + this._image = new Gtk.Image({ + expand: true, + halign: Gtk.Align.FILL, + valign: Gtk.Align.FILL + }); + + // Parent constructor sets all properties + this.parent(props); + + // Put widgets together + this.add(this._overlay); + this._overlay.add(this._image); + this._eventbox_grid.add(this._label); + this._eventbox_grid.add(this._arrow); + this._eventbox.add(this._eventbox_grid); + this._overlay.add_overlay(this._eventbox); + this.show_all(); + + // Connect signals + this.connect('button-press-event', + Lang.bind(this, this._onButtonPress)); + }, + + get image_uri() { + return this._image_uri; + }, + + set image_uri(value) { + this._image_uri = value; + if(this._image) { + let allocation = this.get_allocation(); + let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), + allocation.width, allocation.height); + this._image.set_from_pixbuf(new_pixbuf); + } + }, + + get category_title() { + return this._category_title; + }, + + set category_title(value) { + this._category_title = value; + if(this._label) + this._label.set_text(value.toUpperCase()); + }, + + // OVERRIDES + + vfunc_size_allocate: function(allocation) { + this.parent(allocation); + let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), + allocation.width, allocation.height); + this._image.set_from_pixbuf(new_pixbuf); + }, + + // HANDLERS + + _onButtonPress: function(widget, event) { + this.emit('clicked') + } +}); diff --git a/wikipedia/widgets/category_layout_manager.js b/wikipedia/widgets/category_layout_manager.js new file mode 100644 index 0000000..a7be3bb --- /dev/null +++ b/wikipedia/widgets/category_layout_manager.js @@ -0,0 +1,65 @@ +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +const CategoryLayoutManager = new Lang.Class({ + Name: 'CategoryLayoutManager', + Extends: Gtk.Grid, + + _init: function(props) { + props = props || {}; + props.column_homogeneous = true; + props.row_homogeneous = true; + this.parent(props); + + this._childWidgets = []; + }, + + // Distribute children in two columns, except for the last one if an odd + // number; that should span two columns + _redistributeChildren: function() { + let numChildren = this._childWidgets.length; + let oddNumber = numChildren % 2 == 1; + this._childWidgets.forEach(function(child, index) { + let column = index % 2; + let row = Math.floor(index / 2); + + if(child.get_parent() === this) + Gtk.Container.prototype.remove.call(this, + this._childWidgets[index]); + + if(oddNumber && index == numChildren - 1) + this.attach(child, 0, row, 2, 1); + else + this.attach(child, column, row, 1, 1); + }, this); + }, + + add: function(child) { + this._childWidgets.push(child); + this._redistributeChildren(); + }, + + remove: function(child) { + let index = this._childWidgets.indexOf(child); + if(index == -1) { + printerr('Widget', System.addressOf(child), + 'is not contained in CategoryLayoutManager'); + return; + } + this._childWidgets.splice(index, 1); // remove + this._redistributeChildren(); + } +}); + +// Gtk.init(null); +// let w = new Gtk.Window(); +// let g = new CategoryLayoutManager(); +// let count = 7; +// for(let i = 0; i < count; i++) { +// let widget = new Gtk.Button({label: 'Widget ' + i}); +// g.add(widget); +// } +// w.add(g); +// w.connect('destroy', Gtk.main_quit); +// w.show_all(); +// Gtk.main(); diff --git a/wikipedia/widgets/category_selector_view.js b/wikipedia/widgets/category_selector_view.js new file mode 100644 index 0000000..776bf52 --- /dev/null +++ b/wikipedia/widgets/category_selector_view.js @@ -0,0 +1,43 @@ +const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +const CategoryButton = imports.wikipedia.widgets.category_button; +const CategoryLayoutManager = imports.wikipedia.widgets.category_layout_manager; + +const CATEGORY_COLUMN_SPACING = 10; // pixels +const CATEGORY_ROW_SPACING = 10; // pixels + +const CategorySelectorView = new Lang.Class({ + Name: 'CategorySelectorView', + Extends: CategoryLayoutManager.CategoryLayoutManager, + Signals: { + 'category-chosen': { + param_types: [GObject.TYPE_STRING, GObject.TYPE_INT] + } + }, + + _init: function(props) { + props = props || {}; + props.column_spacing = CATEGORY_COLUMN_SPACING; + props.row_spacing = CATEGORY_ROW_SPACING; + this.parent(props); + }, + + // Takes an array of dictionaries with keys 'title' and 'image_uri' + setCategories: function(categories) { + categories.forEach(function(category, index, obj) { + let button = new CategoryButton.CategoryButton({ + category_title: category.title, + image_uri: category.image_thumbnail_uri + }); + button.index = index; + button.connect('clicked', Lang.bind(this, this._onButtonClicked)); + this.add(button); + }, this); + }, + + _onButtonClicked: function(button) { + this.emit('category-chosen', button.category_title, button.index); + } +}); \ No newline at end of file diff --git a/wikipedia/widgets/scaled_image.js b/wikipedia/widgets/scaled_image.js new file mode 100644 index 0000000..9808914 --- /dev/null +++ b/wikipedia/widgets/scaled_image.js @@ -0,0 +1,159 @@ +const Format = imports.format; +const GdkPixbuf = imports.gi.GdkPixbuf; +const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +String.prototype.format = Format.format; +GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE; + +const BACKGROUND_CSS_TEMPLATE = "\ +Gjs_ScaledImage {\n\ + background-image: url('resource://%s');\n\ + background-size: %s;\n\ + background-position: %s %s;\n\ + background-repeat: no-repeat;\n\ +}"; + +const ScaledImage = new Lang.Class({ + Name: 'ScaledImage', + Extends: Gtk.EventBox, + Properties: { + 'constraint': GObject.ParamSpec.enum('constraint', + 'Constraint direction', + 'Orientation in which the size of the image should be constrained', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, + Gtk.Orientation, Gtk.Orientation.HORIZONTAL), + 'resource': GObject.ParamSpec.string('resource', + 'Resource path', + 'Resource path for the image', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, + '') + }, + + _init: function(props) { + this._constraint = null; + this._resource_path = null; + this._pixbuf = null; + this._css_provider = null; + this.parent(props); + }, + + // OVERRIDES + + vfunc_get_request_mode: function() { + if(this._constraint == Gtk.Orientation.HORIZONTAL) + return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH; + return Gtk.SizeRequestMode.WIDTH_FOR_HEIGHT; + }, + + vfunc_get_preferred_width_for_height: function(height) { + if(!this._pixbuf) + return this.parent(height); + let source_width = this._pixbuf.width; + let source_height = this._pixbuf.height; + let width = (height / source_height) * source_width; + return [width, width]; + }, + + vfunc_get_preferred_height_for_width: function(width) { + if(!this._pixbuf) + return this.parent(width); + let source_width = this._pixbuf.width; + let source_height = this._pixbuf.height; + let height = (width / source_width) * source_height; + return [height, height]; + }, + + vfunc_size_allocate: function(allocation) { + if(this._constraint == Gtk.Orientation.VERTICAL + && this.valign != Gtk.Align.FILL) { + printerr("ScaledImage Warning: Setting constraint to VERTICAL and\ + valign to anything but FILL makes no sense"); + this.valign = Gtk.Align.FILL; + } + if(this._constraint == Gtk.Orientation.HORIZONTAL + && this.halign != Gtk.Align.FILL) { + printerr("ScaledImage Warning: Setting constraint to HORIZONTAL and\ + halign to anything but FILL makes no sense"); + this.halign = Gtk.Align.FILL; + } + this.parent(allocation); + }, + + // PROPERTIES + + get constraint() { + return this._constraint; + }, + + set constraint(value) { + this._constraint = value; + }, + + get resource() { + return this._resource_path; + }, + + set resource(value) { + this._resource_path = value; + this._pixbuf = GdkPixbuf.Pixbuf.new_from_resource(this._resource_path); + this._updateImage(); + }, + + // PRIVATE + + _gtk_align_to_css_align: function(align, orientation) { + switch(align) { + case Gtk.Align.START: + if(orientation == Gtk.Orientation.VERTICAL) + return "top"; + return "left"; + case Gtk.Align.END: + if(orientation == Gtk.Orientation.VERTICAL) + return "bottom"; + return "right"; + } + return "center"; + }, + + _updateImage: function() { + if(this._resource_path === null) + return; + + let context = this.get_style_context(); + + if(this._css_provider !== null) + context.remove_provider(this._css_provider); + + let scaling; + if(this._constraint == Gtk.Orientation.HORIZONTAL) + scaling = "100% auto"; + else + scaling = "auto 100%"; + + let css = BACKGROUND_CSS_TEMPLATE.format(this._resource_path, scaling, + this._gtk_align_to_css_align(this.valign, Gtk.Orientation.VERTICAL), + this._gtk_align_to_css_align(this.halign, + Gtk.Orientation.HORIZONTAL)); + this._css_provider = new Gtk.CssProvider(); + this._css_provider.load_from_data(css); + context.add_provider(this._css_provider, + Gtk.STYLE_PROVIDER_PRIORITY_USER); + } +}); + +// const Gio = imports.gi.Gio; +// Gtk.init(null); +// let resource = Gio.Resource.load('data/endless_brazil.gresource'); +// resource._register(); +// let w = new Gtk.Window(); +// let i = new ScaledImage({ +// resource: '/com/endlessm/brazil/category_images/cuisine.jpg', +// constraint: Gtk.Orientation.HORIZONTAL, +// valign: Gtk.Align.END, +// }); +// w.add(i); +// w.connect('destroy', Gtk.main_quit); +// w.show_all(); +// Gtk.main(); diff --git a/wikipedia/widgets/title_label_view.js b/wikipedia/widgets/title_label_view.js new file mode 100644 index 0000000..f3f599e --- /dev/null +++ b/wikipedia/widgets/title_label_view.js @@ -0,0 +1,95 @@ +const GdkPixbuf = imports.gi.GdkPixbuf; +const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; + +const Utils = imports.wikipedia.utils; + +const TITLE_LABEL_SCREEN_WIDTH_PERCENTAGE = 0.37; +const TITLE_LABEL_LEFT_MARGIN = 20; // pixels +const TITLE_LABEL_BOTTOM_MARGIN = 20; // pixels +const TITLE_LABEL_BENTON_SANS_CORRECTION = 20; // pixels + +const TitleLabelView = new Lang.Class({ + Name: 'TitleLabelView', + Extends: Gtk.Overlay, + Properties: { + 'title': GObject.ParamSpec.string('title', + 'Front page title', + 'Name of the Wikipedia-based application', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, + ''), + 'image-uri': GObject.ParamSpec.string('image-uri', + 'Image URI', + 'Image URI for title image', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, + '') + }, + + _init: function(props) { + this._title = null; + this._image_uri = null; + this._label = new Gtk.Label({ + halign: Gtk.Align.START, + valign: Gtk.Align.END, + margin_left: TITLE_LABEL_LEFT_MARGIN, + margin_bottom: TITLE_LABEL_BOTTOM_MARGIN - TITLE_LABEL_BENTON_SANS_CORRECTION + }); + this._image = new Gtk.Image(); + + let context = this._label.get_style_context() + context.add_class(EndlessWikipedia.STYLE_CLASS_TITLE); + + this.parent(props); + + this.add(this._image); + this.add_overlay(this._label); + }, + + // OVERRIDES + + // Ensure that this widget is 37% of the window's width + vfunc_get_preferred_width: function() { + let toplevel = this.get_toplevel(); + if(toplevel == null) + return this.parent(); + let width = toplevel.get_allocated_width() * TITLE_LABEL_SCREEN_WIDTH_PERCENTAGE; + return [width, width]; + }, + + vfunc_size_allocate: function(allocation) { + this.parent(allocation); + if(this._image_uri !== "" && this._image_uri != null) { + let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), + allocation.width, allocation.height); + this._image.set_from_pixbuf(new_pixbuf); + } + }, + + // PROPERTIES + + get title() { + return this._title; + }, + + set title(value) { + this._title = value; + if(this._label) + this._label.label = value.toUpperCase(); + }, + + get image_uri() { + return this._image_uri; + }, + + set image_uri(value) { + this._image_uri = value; + if(this._image) { + let res_path = Utils.resourceUriToPath(value); + let allocation = this.get_allocation(); + let new_pixbuf = Utils.load_pixbuf_cover(Utils.resourceUriToPath(this._image_uri), + allocation.width, allocation.height); + this._image.set_from_pixbuf(new_pixbuf); + } + } +}); -- cgit v1.2.3