From 098c946ffd4557f0a3eb9be7a6602f85a98d71e4 Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Mon, 6 Mar 2023 01:38:27 +0530 Subject: [PATCH 01/19] Added Screenshot Demo using Libportal --- src/Library/Library.blp | 4 ++++ src/Library/demos/Screenshot/main.blp | 28 ++++++++++++++++++++++ src/Library/demos/Screenshot/main.js | 33 ++++++++++++++++++++++++++ src/Library/demos/Screenshot/main.json | 10 ++++++++ 4 files changed, 75 insertions(+) create mode 100644 src/Library/demos/Screenshot/main.blp create mode 100644 src/Library/demos/Screenshot/main.js create mode 100644 src/Library/demos/Screenshot/main.json diff --git a/src/Library/Library.blp b/src/Library/Library.blp index 264d102ec..5ffb72d3e 100644 --- a/src/Library/Library.blp +++ b/src/Library/Library.blp @@ -31,6 +31,10 @@ Adw.PreferencesWindow library { title: "User Interface"; } + Adw.PreferencesGroup library_portal { + title: "User Interface"; + } + Adw.PreferencesGroup { vexpand: true; valign: end; diff --git a/src/Library/demos/Screenshot/main.blp b/src/Library/demos/Screenshot/main.blp new file mode 100644 index 000000000..448d6f2c8 --- /dev/null +++ b/src/Library/demos/Screenshot/main.blp @@ -0,0 +1,28 @@ +using Gtk 4.0; +using Adw 1; + +Adw.StatusPage { + title: "Screenshot"; + description: _("Keep a snapshot of your current screen"); + + Box { + orientation: vertical; + halign: center; + + Button Screenshot { + icon-name: "camera"; + margin-bottom: 40; + } + + LinkButton { + label: "API Reference"; + uri: "https://libportal.org/"; + } + + LinkButton { + label: "Source Code"; + uri: "https://developer.gnome.org/hig/patterns/controls/sliders.html"; + } + } +}) + diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js new file mode 100644 index 000000000..cc443db88 --- /dev/null +++ b/src/Library/demos/Screenshot/main.js @@ -0,0 +1,33 @@ +import Xdp from "gi://Xdp"; +import XdpGtk from "gi://XdpGtk4"; +const { GdkPixbuf, Gio, GLib, GObject, Gst, Gtk, Pango, PangoCairo } = + imports.gi; + +const Screenshot = workbench.builder.get_object("Screenshot"); +const portal = new Xdp.Portal(); +const parent = XdpGtk.parent_new_gtk(workbench.window); + +function _takeScreenshot() { + let flags = Xdp.ScreenshotFlags.NONE; + + portal.take_screenshot(parent, flags, null, (portal, result) => { + let path = null; + + try { + const uri = this._portal.take_screenshot_finish(result); + const [path] = GLib.filename_from_uri(uri); + const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + path, + 60, + 40, + true, + ); + portal.image.set_from_pixbuf(pixbuf); + } catch (e) { + logError(e); + return; + } + }); +} + +Screenshot.connect("clicked", _takeScreenshot); diff --git a/src/Library/demos/Screenshot/main.json b/src/Library/demos/Screenshot/main.json new file mode 100644 index 000000000..a849b84eb --- /dev/null +++ b/src/Library/demos/Screenshot/main.json @@ -0,0 +1,10 @@ +{ + "name": "Screenshot", + "category": "portal", + "description": "Keep a snapshot of your current screen", + "panels": [ + "ui", + "preview" + ], + "autorun": true +} From 147ded93cb19c6776978a7f558875efbb4e688bc Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Tue, 7 Mar 2023 00:27:27 +0100 Subject: [PATCH 02/19] fix --- src/Library/demos/Screenshot/main.blp | 5 ++++- src/Library/demos/Screenshot/main.js | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Library/demos/Screenshot/main.blp b/src/Library/demos/Screenshot/main.blp index 448d6f2c8..a9ea92472 100644 --- a/src/Library/demos/Screenshot/main.blp +++ b/src/Library/demos/Screenshot/main.blp @@ -9,6 +9,9 @@ Adw.StatusPage { orientation: vertical; halign: center; + Picture picture { + } + Button Screenshot { icon-name: "camera"; margin-bottom: 40; @@ -24,5 +27,5 @@ Adw.StatusPage { uri: "https://developer.gnome.org/hig/patterns/controls/sliders.html"; } } -}) +} diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index cc443db88..25e636351 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -14,7 +14,7 @@ function _takeScreenshot() { let path = null; try { - const uri = this._portal.take_screenshot_finish(result); + const uri = portal.take_screenshot_finish(result); const [path] = GLib.filename_from_uri(uri); const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( path, @@ -22,7 +22,7 @@ function _takeScreenshot() { 40, true, ); - portal.image.set_from_pixbuf(pixbuf); + workbench.builder.get_object("picture").set_pixbuf(pixbuf); } catch (e) { logError(e); return; @@ -31,3 +31,4 @@ function _takeScreenshot() { } Screenshot.connect("clicked", _takeScreenshot); + From cf745fff01686cdde3d546bb9cff75641dc07252 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <58233418+AkshayWarrier@users.noreply.github.com> Date: Tue, 7 Mar 2023 01:32:16 +0530 Subject: [PATCH 03/19] library: Add Avatar entry (#197) --- src/Library/demos/Avatar/main.blp | 53 ++++++++++++++++++++++++++++++ src/Library/demos/Avatar/main.js | 30 +++++++++++++++++ src/Library/demos/Avatar/main.json | 7 ++++ src/Previewer/previewer.vala | 1 + 4 files changed, 91 insertions(+) create mode 100644 src/Library/demos/Avatar/main.blp create mode 100644 src/Library/demos/Avatar/main.js create mode 100644 src/Library/demos/Avatar/main.json diff --git a/src/Library/demos/Avatar/main.blp b/src/Library/demos/Avatar/main.blp new file mode 100644 index 000000000..b8ae5d291 --- /dev/null +++ b/src/Library/demos/Avatar/main.blp @@ -0,0 +1,53 @@ +using Gtk 4.0; +using Adw 1; + +Adw.StatusPage { + title: "Avatar"; + description: "Display a round avatar for image or initials."; + + Box { + orientation: vertical; + halign: center; + + Adw.Avatar { + size: 96; + show-initials: true; + text: "Jay Doe"; + margin-bottom: 6; + } + + Label { + label: "Initials"; + margin-bottom: 30; + } + + Adw.Avatar avatar_image { + size: 96; + margin-bottom: 6; + } + + Button button { + label: "Image…"; + margin-bottom: 30; + + styles [ + "pill", + ] + } + + Adw.Avatar { + size: 96; + margin-bottom: 6; + } + + Label { + label: "Fallback"; + margin-bottom: 30; + } + + LinkButton { + label: "API Reference"; + uri: "https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.2/class.Avatar.html"; + } + } +} diff --git a/src/Library/demos/Avatar/main.js b/src/Library/demos/Avatar/main.js new file mode 100644 index 000000000..792d27028 --- /dev/null +++ b/src/Library/demos/Avatar/main.js @@ -0,0 +1,30 @@ +import Gtk from "gi://Gtk"; +import Gdk from "gi://Gdk"; +import Gio from "gi://Gio"; + +Gio._promisify(Gtk.FileDialog.prototype, "open", "open_finish"); + +const avatar_image = workbench.builder.get_object("avatar_image"); +const button = workbench.builder.get_object("button"); + +const filter = new Gtk.FileFilter({ + name: "Images", +}); +filter.add_pixbuf_formats(); + +const dialog = new Gtk.FileDialog({ + title: "Select an Avatar", + modal: true, + default_filter: filter, +}); + +button.connect("clicked", async () => { + try { + const file = await dialog.open(workbench.window, null); + const texture = Gdk.Texture.new_from_file(file); + avatar_image.set_custom_image(texture); + } catch (err) { + logError(err); + } +}); + diff --git a/src/Library/demos/Avatar/main.json b/src/Library/demos/Avatar/main.json new file mode 100644 index 000000000..0e9e7ae8b --- /dev/null +++ b/src/Library/demos/Avatar/main.json @@ -0,0 +1,7 @@ +{ + "name": "Avatar", + "category": "user_interface", + "description": "Display a round avatar for image or initials.", + "panels": ["ui", "preview"], + "autorun": true +} diff --git a/src/Previewer/previewer.vala b/src/Previewer/previewer.vala index 9665e16d0..f6352df95 100644 --- a/src/Previewer/previewer.vala +++ b/src/Previewer/previewer.vala @@ -218,3 +218,4 @@ namespace Workbench { loop.run(); } } + From 6748bee5aa532f1c03a4a0fda46baae018a03d1c Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Tue, 7 Mar 2023 19:53:22 +0100 Subject: [PATCH 04/19] Make Library window non modal (#201) Allows for split view and navigating entries easily --- src/Library/Library.blp | 2 +- src/Library/Library.js | 9 +++++---- src/Previewer/Internal.js | 3 +-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Library/Library.blp b/src/Library/Library.blp index 5ffb72d3e..a7ea10813 100644 --- a/src/Library/Library.blp +++ b/src/Library/Library.blp @@ -3,7 +3,7 @@ using Adw 1; Adw.PreferencesWindow library { hide-on-close: true; - modal: true; + modal: false; title: _("Library"); default-height: 700; default-width: 700; diff --git a/src/Library/Library.js b/src/Library/Library.js index c0628ae8f..654fb36f2 100644 --- a/src/Library/Library.js +++ b/src/Library/Library.js @@ -13,12 +13,11 @@ const demo_dir = Gio.File.new_for_path( export default function Library({ openDemo, - window: appliation_window, + window: application_window, application, }) { const builder = Gtk.Builder.new_from_resource(resource); const window = builder.get_object("library"); - window.set_transient_for(appliation_window); let last_selected; @@ -38,7 +37,9 @@ export default function Library({ widget.connect("activated", () => { last_selected = widget; openDemo(demo.name) - .then(() => window.close()) + .then(() => { + application_window.present(); + }) .catch(logError); }); @@ -53,7 +54,7 @@ export default function Library({ window.present(); last_selected?.grab_focus(); }); - appliation_window.add_action(action_library); + application_window.add_action(action_library); application.set_accels_for_action("win.library", ["O"]); } diff --git a/src/Previewer/Internal.js b/src/Previewer/Internal.js index 4705f0b23..4e6e7b1c6 100644 --- a/src/Previewer/Internal.js +++ b/src/Previewer/Internal.js @@ -1,5 +1,4 @@ import Gtk from "gi://Gtk"; -import Gdk from "gi://Gdk"; import * as postcss from "../lib/postcss.js"; import GLib from "gi://GLib"; import Graphene from "gi://Graphene"; @@ -50,7 +49,7 @@ export default function Internal({ } } - object_root.present_with_time(Gdk.CURRENT_TIME); + object_root.present(); onWindowChange(true); } From 503f16834056bd8454409de8852117419a3b1593 Mon Sep 17 00:00:00 2001 From: Brage Fuglseth <91388039+bragefuglseth@users.noreply.github.com> Date: Tue, 7 Mar 2023 20:37:45 +0100 Subject: [PATCH 05/19] library: Add Text Colors demo (#206) --- src/Library/demos/Text Colors/main.blp | 27 +++++++++++++ src/Library/demos/Text Colors/main.js | 53 +++++++++++++++++++++++++ src/Library/demos/Text Colors/main.json | 7 ++++ src/about.js | 1 + 4 files changed, 88 insertions(+) create mode 100644 src/Library/demos/Text Colors/main.blp create mode 100644 src/Library/demos/Text Colors/main.js create mode 100644 src/Library/demos/Text Colors/main.json diff --git a/src/Library/demos/Text Colors/main.blp b/src/Library/demos/Text Colors/main.blp new file mode 100644 index 000000000..d08f6714d --- /dev/null +++ b/src/Library/demos/Text Colors/main.blp @@ -0,0 +1,27 @@ +using Gtk 4.0; + +Box main { + orientation: vertical; + halign: center; + valign: center; + spacing: 12; + + Label label { + label: bind entry.text; + + styles [ + "title-1", + ] + } + + Label { + label: "Colored with Pango attributes"; + } + + Entry entry { + margin-top: 12; + text: "Hello, Rainbow!"; + halign: center; + } +} + diff --git a/src/Library/demos/Text Colors/main.js b/src/Library/demos/Text Colors/main.js new file mode 100644 index 000000000..d8d0b9b72 --- /dev/null +++ b/src/Library/demos/Text Colors/main.js @@ -0,0 +1,53 @@ +// Pango is a text layout library. It can be used for e.g. formatting text +// https://gjs-docs.gnome.org/pango10~1.0/ +import Pango from "gi://Pango?version=1.0"; + +const label = workbench.builder.get_object("label"); +label.connect("notify::label", updateAttributes); +updateAttributes(); + +function updateAttributes() { + // A Pango Attribute List is used to style the label + label.attributes = rainbow_attributes(label.label); +} + +// Generates an Attribute List that styles the label in rainbow colors. +// The `str` parameter is needed to detect string length + position of spaces +function rainbow_attributes(str) { + const RAINBOW_COLORS = [ + "#D00", + "#C50", + "#E90", + "#090", + "#24E", + "#55E", + "#C3C", + ]; + + // Create a color array with the length needed to color all the letters + let colorArray = []; + for (let i = 0; i < str.length; i = colorArray.length) { + colorArray.push(...RAINBOW_COLORS); + } + // Independent variable from `i` in the following loop to avoid spaces "consuming" a color + let colorIdx = 0; + + let attrListString = ""; + for (let i = 0; i < str.length; i++) { + // Skip space characters + if (str[i] !== " ") { + let startIdx = i; + let endIdx = [i + 1]; + + let color = colorArray[colorIdx]; + colorIdx++; + // See comment below + attrListString += `${startIdx} ${endIdx} foreground ${color},`; + } + } + + // For more info about the syntax for this function, see: + // https://docs.gtk.org/Pango/method.AttrList.to_string.html + return Pango.attr_list_from_string(attrListString); +} + diff --git a/src/Library/demos/Text Colors/main.json b/src/Library/demos/Text Colors/main.json new file mode 100644 index 000000000..e039b97ad --- /dev/null +++ b/src/Library/demos/Text Colors/main.json @@ -0,0 +1,7 @@ +{ + "name": "Text Colors", + "category": "user_interface", + "description": "Color text programatically with Pango", + "panels": ["code", "preview"], + "autorun": true +} diff --git a/src/about.js b/src/about.js index c4e469290..ba69184e2 100644 --- a/src/about.js +++ b/src/about.js @@ -51,6 +51,7 @@ ${getBlueprintVersion()} dialog.add_credit_section(_("Contributors"), [ "Ben Foote http://www.bengineeri.ng", + "Brage Fuglseth https://bragefuglseth.dev", "Hari Rana (TheEvilSkeleton) https://theevilskeleton.gitlab.io", "Sriyansh Shivam https://linktr.ee/sonic_here", // Add yourself as From 2653fd47e2cfc6abb86f2ca4025a79ff1afddc39 Mon Sep 17 00:00:00 2001 From: Hemish Date: Wed, 8 Mar 2023 15:35:56 +0530 Subject: [PATCH 06/19] Fix typo "Information about Blueprint" (#212) --- src/window.blp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window.blp b/src/window.blp index 5a2992b8c..a9448685d 100644 --- a/src/window.blp +++ b/src/window.blp @@ -233,7 +233,7 @@ Gtk.ApplicationWindow window { } Button button_ui_experimental_blueprint { icon-name: "applications-science-symbolic"; - tooltip-text: _("Informaton about Blueprint"); + tooltip-text: _("Information about Blueprint"); } } } From 146647a0f5b62def23f2d979cca9c62b2368b034 Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam <96797205+SoNiC-HeRE@users.noreply.github.com> Date: Wed, 8 Mar 2023 21:01:14 +0530 Subject: [PATCH 07/19] library: Add Scale entry (#204) --- src/Library/demos/Scale/main.blp | 61 +++++++++++++++++++++++++++++++ src/Library/demos/Scale/main.js | 32 ++++++++++++++++ src/Library/demos/Scale/main.json | 10 +++++ 3 files changed, 103 insertions(+) create mode 100644 src/Library/demos/Scale/main.blp create mode 100644 src/Library/demos/Scale/main.js create mode 100644 src/Library/demos/Scale/main.json diff --git a/src/Library/demos/Scale/main.blp b/src/Library/demos/Scale/main.blp new file mode 100644 index 000000000..87ffdf8ff --- /dev/null +++ b/src/Library/demos/Scale/main.blp @@ -0,0 +1,61 @@ +using Gtk 4.0; +using Adw 1; + +Adw.StatusPage { + title: "Scale"; + description: _("Slider control to select a value from a range"); + + Box { + orientation: vertical; + halign: center; + + Box{ + halign: center; + + Scale one { + orientation: horizontal; + margin-bottom: 18; + width-request: 130; + draw-value: true; + margin-end: 36; + adjustment: Gtk.Adjustment { + lower: 0; + upper: 100; + value: 0; + }; + } + + Scale two { + orientation: vertical; + margin-bottom: 18; + height-request: 140; + adjustment: Gtk.Adjustment { + lower: 0; + upper: 100; + value: 0; + }; + } + } + Scale disabled { + orientation: horizontal; + sensitive: false; + margin-bottom: 18; + show-fill-level: true; + adjustment: Gtk.Adjustment { + lower: 0; + upper: 50; + value: 25; + }; + } + + LinkButton { + label: "API Reference"; + uri: "https://docs.gtk.org/gtk4/class.Scale.html"; + } + + LinkButton { + label: "Human Interface Guidelines"; + uri: "https://developer.gnome.org/hig/patterns/controls/sliders.html"; + } + } +} diff --git a/src/Library/demos/Scale/main.js b/src/Library/demos/Scale/main.js new file mode 100644 index 000000000..1079b0c86 --- /dev/null +++ b/src/Library/demos/Scale/main.js @@ -0,0 +1,32 @@ +import Gtk from "gi://Gtk"; + +const scale_one = workbench.builder.get_object("one"); +const scale_two = workbench.builder.get_object("two"); + +const marks = { + 0: "A", + 50: "B", + 100: "C", +}; + +for (const [value, label] of Object.entries(marks)) { + scale_two.add_mark(value, Gtk.PositionType.RIGHT, label); +} +scale_two.set_increments(25, 100); + +scale_one.connect("value-changed", () => { + const scale_value = scale_one.get_value(); + if (scale_value === scale_one.adjustment.upper) { + console.log("Maximum value reached"); + } else if (scale_value === scale_one.adjustment.lower) { + console.log("Minimum value reached"); + } +}); + +scale_two.connect("value-changed", () => { + const scale_value = scale_two.get_value(); + const label = marks[scale_value]; + if (!label) return; + + console.log(`Mark ${label} reached`); +}); diff --git a/src/Library/demos/Scale/main.json b/src/Library/demos/Scale/main.json new file mode 100644 index 000000000..ff9cb9ca9 --- /dev/null +++ b/src/Library/demos/Scale/main.json @@ -0,0 +1,10 @@ +{ + "name": "Scale", + "category": "user_interface", + "description": "Slider control to select a value from a range", + "panels": [ + "ui", + "preview" + ], + "autorun": true +} From 541117ea4016f8940a5ffeb7366978547e903b0a Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Sat, 11 Mar 2023 14:17:09 +0100 Subject: [PATCH 08/19] Use org.gnome.Sdk//44 --- CONTRIBUTING.md | 3 +-- re.sonny.Workbench.Devel.json | 12 ++++++------ src/Previewer/previewer.vala | 2 +- src/Previewer/utils.js | 11 +++++++---- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d147dd4b..39bdcc803 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,6 @@ Thank you for considering contributing to Workbench. Feel free to [get in touch] The following is the recommended setup: 1. Install [GNOME Builder from Flathub](https://flathub.org/apps/details/org.gnome.Builder) -2. [Enable GNOME Nightly repository](https://wiki.gnome.org/Apps/Nightly#Setting_up_GNOME_nightlies) 2. Open Builder and select "Clone Repository..." 3. Clone `https://github.com/sonnyp/Workbench.git` (or your fork) 4. Press the Run ▶ button @@ -55,7 +54,7 @@ Some guidelines Once you're satisfied with the result - you can send a pull request to include it in Workbench. All you need to do is add the files to [`src/Library/demos`](./src/Library/demos). -Make sure it's working by rebuilding Workbench and launching your entry via the Library. If not - double check what you did and compare with other Library entries. +Make sure it's working by running Workbench and launching your entry via the Library. If not - double check what you did and compare with other Library entries. ## Submitting a contribution diff --git a/re.sonny.Workbench.Devel.json b/re.sonny.Workbench.Devel.json index cae0f2647..f162fe306 100644 --- a/re.sonny.Workbench.Devel.json +++ b/re.sonny.Workbench.Devel.json @@ -1,14 +1,14 @@ { "id": "re.sonny.Workbench.Devel", "runtime": "org.gnome.Sdk", - "runtime-version": "master", + "runtime-version": "44beta", "sdk": "org.gnome.Sdk", - "sdk-extensions" : [ + "sdk-extensions": [ "org.freedesktop.Sdk.Extension.vala" ], - "build-options" : { - "append-path" : "/usr/lib/sdk/vala/bin", - "append-ld-library-path" : "/usr/lib/sdk/vala/lib" + "build-options": { + "append-path": "/usr/lib/sdk/vala/bin", + "append-ld-library-path": "/usr/lib/sdk/vala/lib" }, "command": "workbench", "finish-args": [ @@ -162,4 +162,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/src/Previewer/previewer.vala b/src/Previewer/previewer.vala index f6352df95..d37b82139 100644 --- a/src/Previewer/previewer.vala +++ b/src/Previewer/previewer.vala @@ -91,7 +91,7 @@ namespace Workbench { var end = section.get_end_location(); this.css_parser_error(error.message, (int)start.lines, (int)start.line_chars, (int)end.lines, (int)end.line_chars); }); - this.css.load_from_data (content); + this.css.load_from_data (content.data); Gtk.StyleContext.add_provider_for_display (Gdk.Display.get_default (), this.css , Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } diff --git a/src/Previewer/utils.js b/src/Previewer/utils.js index 7d8ae3302..a4ef585e8 100644 --- a/src/Previewer/utils.js +++ b/src/Previewer/utils.js @@ -1,7 +1,12 @@ import GObject from "gi://GObject"; import Gtk from "gi://Gtk"; import Gio from "gi://Gio"; -import { promiseTask } from "../../troll/src/util.js"; + +Gio._promisify( + Gio.Subprocess.prototype, + "wait_check_async", + "wait_check_finish", +); export function getObjectClass(class_name) { const split = class_name.split(/(?=[A-Z])/); @@ -25,7 +30,5 @@ export async function isBuilderable(str) { const flags = Gio.SubprocessFlags.STDOUT_SILENCE | Gio.SubprocessFlags.STDERR_SILENCE; const proc = Gio.Subprocess.new(["workbench-crasher", str], flags); - await promiseTask(proc, "wait_async", "wait_finish", null); - if (!proc.get_if_exited()) return false; - return proc.get_exit_status() === 0; + return proc.wait_check_async(null); } From 74b72b712c29072ad6842960d09ee2588266b3d5 Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Sun, 12 Mar 2023 10:55:42 +0100 Subject: [PATCH 09/19] Improve contributing guide --- .vscode/settings.json | 4 +- CONTRIBUTING.md | 72 +++++++++++++++++++-------- src/Library/demos/Avatar/main.js | 1 - src/Library/demos/Button/main.js | 1 - src/Library/demos/Text Colors/main.js | 1 - 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ddbbd43a7..e96b60377 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,11 +17,11 @@ "[javascript]": { "editor.defaultFormatter": "rome.rome" }, - "vala.languageServerPath": "/home/sonny/Projects/Workbench/.flatpak/vala-language-server.sh", "[json]": { "editor.defaultFormatter": "vscode.json-language-features" }, "mesonbuild.configureOnOpen": false, "mesonbuild.buildFolder": "_build", - "mesonbuild.mesonPath": "/home/sonny/Projects/Workbench/.flatpak/meson.sh" + "mesonbuild.mesonPath": "/home/sonny/Projects/Workbench/.flatpak/meson.sh", + "vala.languageServerPath": "/home/sonny/Projects/Workbench/.flatpak/vala-language-server.sh" } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39bdcc803..fc1057d12 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,7 @@ Thank you for considering contributing to Workbench. Feel free to [get in touch](https://matrix.to/#/%23workbench:gnome.org). -## Development - -### Setup +## Setup The following is the recommended setup: @@ -19,19 +17,30 @@ Make sure that you're building the development target `re.sonny.Workbench.Devel` If you know what you are doing you can also use VSCode with the extensions recommended in this workspace or anything else you are comfortable with. Don't forget to fetch the submodules. -### Learn +## Getting started -Here is a compilation of resources to learn more about the GNOME platform. +If you're completely new to GNOME development this is for you. -* [Workbench](https://github.com/sonnyp/Workbench) 😉 -* [GObject](https://gjs.guide/guides/gobject/basics.html#gobject-construction) -* [GTK4 + GJS Book](https://rmnvgr.gitlab.io/gtk4-gjs-book/) -* [Asynchronous programming](https://gjs.guide/guides/gjs/asynchronous-programming.html#the-main-loop) -* [API references](https://gjs-docs.gnome.org/) make sure to enable at least GTK, GJS, GLib, Gio -* [GJS docs](https://gitlab.gnome.org/GNOME/gjs/-/tree/master/doc) -* [GJS examples](https://gitlab.gnome.org/GNOME/gjs/-/tree/master/examples) +Open the "Welcome" example from Workbench Library. -### Library +Important fundamentals are + * objects + * properties + * signals + +Every widget in GTK is an object. For example, `Gtk.Box`, `Gtk.Button`, ... + +Properties affect an object to change its appearance or behavior. + +Signals are events that can be listened to. Like `clicked` on `Gtk.Button`. + +The Welcome example in the Library has all 3. Play with it, try to understand and make changes. If you break things you can always go back by select "Welcome" example from the Workbench Library again. + +Once you understand these 3 things, try creating something new. There are plenty of widgets and patterns to explore. + +## Your first contribution + +Your first contribution should be a new example or demo for the Wokbench library. Library examples and demos have 3 functions @@ -39,7 +48,11 @@ Library examples and demos have 3 functions 2. Teach how to use the APIs, patterns and widgets 3. Provide functional snippets ready to use -The easiest way to get started is to write an entry within Workbench directly. Check [here for ideas](https://github.com/sonnyp/Workbench/issues/69) and [here for examples](https://github.com/sonnyp/Workbench/issues?q=label%3A%22Library+%F0%9F%93%9A%EF%B8%8F%22). +Pick a widget and make a Library demo for it within Workbench directly. + +Check [here for ideas](https://github.com/sonnyp/Workbench/issues/69) and [here for examples](https://github.com/sonnyp/Workbench/issues?q=label%3A%22Library+%F0%9F%93%9A%EF%B8%8F%22). + +Feel free to reach out and ask for a topic or feedback. Some guidelines @@ -51,16 +64,29 @@ Some guidelines * Add links to follow up on the topic covered * Follow the patterns of existing entries * in `Code`, use properties (`widget.senstitive`) over methods (`widget.set/get_sensitive`) +## Submitting a contribution -Once you're satisfied with the result - you can send a pull request to include it in Workbench. All you need to do is add the files to [`src/Library/demos`](./src/Library/demos). +Once you're satisfied with the result - you can send a pull request to include it in Workbench. You will need to copy the code into new files in [`src/Library/demos`](./src/Library/demos). Make sure it's working by running Workbench and launching your entry via the Library. If not - double check what you did and compare with other Library entries. -## Submitting a contribution +Some guidelines: -1. Unless you don't want too - add your name to [the list of contributors](./src/about.js) -2. Use a short PR title - eg. "library: Add Video entry" - it will be used as commit message -3. If relevant, mention the related issue in the PR description +* Unless you don't want too - add your name to [the list of contributors](./src/about.js) +* Use a short PR title - eg. "library: Add Video entry" - it will be used as commit message +* If relevant, mention the related issue in the PR description + +## Learn + +Here is a compilation of resources to learn more about the GNOME platform. + +* [Workbench](https://github.com/sonnyp/Workbench) 😉 +* [GObject](https://gjs.guide/guides/gobject/basics.html#gobject-construction) +* [GTK4 + GJS Book](https://rmnvgr.gitlab.io/gtk4-gjs-book/) +* [Asynchronous programming](https://gjs.guide/guides/gjs/asynchronous-programming.html#the-main-loop) +* [API references](https://gjs-docs.gnome.org/) make sure to enable at least GTK, GJS, GLib, Gio +* [GJS docs](https://gitlab.gnome.org/GNOME/gjs/-/tree/master/doc) +* [GJS examples](https://gitlab.gnome.org/GNOME/gjs/-/tree/master/examples) ## Debugging @@ -91,9 +117,13 @@ Thank you for your help! ### The app won't build/run anymore - even on clean `main` -Clean the build directory. +Clean the build directory. On GNOME Builder, open the search palette with `Ctrl+Enter` and search/select `Clean`. + +If that doesn't solve it - remove the GNOME Builder cache directory -On GNOME Builder, open the search palette with `Ctrl+Enter` and search/select `Clean`. +``` +rm -r ~/.var/app/org.gnome.Builder/cache/ +``` ### git says `blueprint-compiler` is modified diff --git a/src/Library/demos/Avatar/main.js b/src/Library/demos/Avatar/main.js index 792d27028..e8615d18d 100644 --- a/src/Library/demos/Avatar/main.js +++ b/src/Library/demos/Avatar/main.js @@ -27,4 +27,3 @@ button.connect("clicked", async () => { logError(err); } }); - diff --git a/src/Library/demos/Button/main.js b/src/Library/demos/Button/main.js index a6a870165..5909b4404 100644 --- a/src/Library/demos/Button/main.js +++ b/src/Library/demos/Button/main.js @@ -20,4 +20,3 @@ for (const id of button_ids) { function onClicked(button) { console.log(`${button.name} clicked`); } - diff --git a/src/Library/demos/Text Colors/main.js b/src/Library/demos/Text Colors/main.js index d8d0b9b72..a75153ef5 100644 --- a/src/Library/demos/Text Colors/main.js +++ b/src/Library/demos/Text Colors/main.js @@ -50,4 +50,3 @@ function rainbow_attributes(str) { // https://docs.gtk.org/Pango/method.AttrList.to_string.html return Pango.attr_list_from_string(attrListString); } - From f1c9b614f94017ff9d75263f5e1c1b37adb743bb Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Sun, 12 Mar 2023 12:19:41 +0100 Subject: [PATCH 10/19] Use css_classes property and add_css_class method --- src/Library/demos/Welcome/main.js | 2 +- src/PanelCode.js | 2 +- src/PanelUI.js | 2 +- src/Previewer/Previewer.js | 5 +---- src/WorkbenchHoverProvider.js | 6 ++---- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Library/demos/Welcome/main.js b/src/Library/demos/Welcome/main.js index ccf8b8205..98be413a3 100644 --- a/src/Library/demos/Welcome/main.js +++ b/src/Library/demos/Welcome/main.js @@ -8,8 +8,8 @@ const subtitle_box = workbench.builder.get_object("subtitle"); const button = new Gtk.Button({ label: "Press me", margin_top: 6, + css_classes: ["suggested-action"], }); -button.get_style_context().add_class("suggested-action"); button.connect("clicked", greet); subtitle_box.append(button); diff --git a/src/PanelCode.js b/src/PanelCode.js index 8641b178a..bec951053 100644 --- a/src/PanelCode.js +++ b/src/PanelCode.js @@ -20,7 +20,7 @@ export default function PanelCode({ const dropdown_code_lang = builder.get_object("dropdown_code_lang"); // TODO: File a bug libadwaita // flat does nothing on GtkDropdown or GtkComboBox or GtkComboBoxText - dropdown_code_lang.get_first_child().get_style_context().add_class("flat"); + dropdown_code_lang.get_first_child().add_css_class("flat"); settings.bind( "show-code", diff --git a/src/PanelUI.js b/src/PanelUI.js index b885885af..18881f94f 100644 --- a/src/PanelUI.js +++ b/src/PanelUI.js @@ -48,7 +48,7 @@ export default function PanelUI({ const dropdown_ui_lang = builder.get_object("dropdown_ui_lang"); // TODO: File a bug libadwaita // flat does nothing on GtkDropdown or GtkComboBox or GtkComboBoxText - dropdown_ui_lang.get_first_child().get_style_context().add_class("flat"); + dropdown_ui_lang.get_first_child().add_css_class("flat"); const blueprint = setupBlueprint({ data_dir, diff --git a/src/Previewer/Previewer.js b/src/Previewer/Previewer.js index 42b106c51..363cf0c6f 100644 --- a/src/Previewer/Previewer.js +++ b/src/Previewer/Previewer.js @@ -40,10 +40,7 @@ export default function Previewer({ const dropdown_preview_align = builder.get_object("dropdown_preview_align"); // TODO: File a bug libadwaita // flat does nothing on GtkDropdown or GtkComboBox or GtkComboBoxText - dropdown_preview_align - .get_first_child() - .get_style_context() - .add_class("flat"); + dropdown_preview_align.get_first_child().add_css_class("flat"); const internal = Internal({ onWindowChange(open) { diff --git a/src/WorkbenchHoverProvider.js b/src/WorkbenchHoverProvider.js index 0dc0e9283..a8d4e8dc5 100644 --- a/src/WorkbenchHoverProvider.js +++ b/src/WorkbenchHoverProvider.js @@ -26,17 +26,15 @@ class WorkbenchHoverProvider extends GObject.Object { const container = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, spacing: 4, + css_classes: ["hoverdisplay", "osd", "frame"], }); - container.add_css_class("hoverdisplay"); - container.add_css_class("osd"); - container.add_css_class("frame"); for (const { message } of diagnostics) { const label = new Gtk.Label({ halign: Gtk.Align.START, label: `${message}`, + css_classes: ["body"], }); - label.add_css_class("body"); container.append(label); } From d9f1389ea3339db9b42dcbc3858bc2a0176a48c7 Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Sun, 12 Mar 2023 14:35:19 +0100 Subject: [PATCH 11/19] Fix mechanism to return on UI syntax conversion error --- src/PanelUI.js | 40 +++++++++++++++++++--------------------- src/util.js | 28 ++++++++++++++-------------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/PanelUI.js b/src/PanelUI.js index 18881f94f..797368829 100644 --- a/src/PanelUI.js +++ b/src/PanelUI.js @@ -67,17 +67,7 @@ export default function PanelUI({ term_console.clear(); settings.set_boolean("show-console", true); - let blp; - - try { - blp = await blueprint.decompile(buffer_xml.text); - } catch (err) { - if (err instanceof LSPError) { - logBlueprintError(err); - return; - } - throw err; - } + const blp = await blueprint.decompile(buffer_xml.text); code_view_blueprint.replaceText(blp); } @@ -144,25 +134,38 @@ export default function PanelUI({ } } + dropdown_ui_lang.set_selected(settings.get_int("ui-language")); + const dropdown_selected_signal = listenProperty( + dropdown_ui_lang, + "selected", + (value) => { + onChangeLang(value).catch(logError); + }, + ); + async function onChangeLang(value) { if (value === 0) { try { await convertToXML(); } catch (err) { logError(err); - dropdown_ui_lang.block(); + dropdown_selected_signal.block(); dropdown_ui_lang.set_selected(1); - dropdown_ui_lang.unblock(); + dropdown_selected_signal.unblock(); return; } } else if (value === 1) { try { await convertToBlueprint(); } catch (err) { - logError(err); - dropdown_ui_lang.block(); + if (err instanceof LSPError) { + logBlueprintError(err); + } else { + logError(err); + } + dropdown_selected_signal.block(); dropdown_ui_lang.set_selected(0); - dropdown_ui_lang.unblock(); + dropdown_selected_signal.unblock(); return; } } @@ -171,11 +174,6 @@ export default function PanelUI({ setupLanguage(); } - dropdown_ui_lang.set_selected(settings.get_int("ui-language")); - listenProperty(dropdown_ui_lang, "selected", (value) => { - onChangeLang(value).catch(logError); - }); - function setupLanguage() { start(); stack_ui.set_visible_child_name(lang.id); diff --git a/src/util.js b/src/util.js index f97fbddc2..70cd07538 100644 --- a/src/util.js +++ b/src/util.js @@ -1,6 +1,7 @@ import GLib from "gi://GLib"; import Gio from "gi://Gio"; import Xdp from "gi://Xdp"; +import GObject from "gi://GObject"; export const portal = new Xdp.Portal(); @@ -113,25 +114,24 @@ export function getLanguageForFile(file) { }); } -export function listenProperty( - object, - property, - fn, - { initial = false, equal = true } = {}, -) { - let value = object[property]; +export function listenProperty(object, property, fn, { initial = false } = {}) { if (initial) { - fn(value); + fn(object[property]); } - const signal_spec = object.connect(`notify::${property}`, () => { - const new_value = object[property]; - if (equal && new_value === value) return; - value = new_value; - fn(value); + + const signal = `notify::${property}`; + const handler_id = object.connect(signal, () => { + fn(object[property]); }); return { + block() { + GObject.signal_handler_block(object, handler_id); + }, + unblock() { + GObject.signal_handler_unblock(object, handler_id); + }, disconnect() { - return signal_spec.disconnect(); + return object.disconnect(handler_id); }, }; } From 21ffd6d9bf53599d6970f3ebf839b515c6dedc11 Mon Sep 17 00:00:00 2001 From: halfmexican <103920890+halfmexican@users.noreply.github.com> Date: Sun, 12 Mar 2023 18:22:51 -0500 Subject: [PATCH 12/19] Library: Add Dialogs entry (#211) --- src/Library/demos/Dialogs/main.blp | 40 +++++++++++++ src/Library/demos/Dialogs/main.js | 86 ++++++++++++++++++++++++++++ src/Library/demos/Dialogs/main.json | 10 ++++ src/Library/demos/Dialogs/main.vala | 89 +++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 src/Library/demos/Dialogs/main.blp create mode 100644 src/Library/demos/Dialogs/main.js create mode 100644 src/Library/demos/Dialogs/main.json create mode 100644 src/Library/demos/Dialogs/main.vala diff --git a/src/Library/demos/Dialogs/main.blp b/src/Library/demos/Dialogs/main.blp new file mode 100644 index 000000000..e35e6d42d --- /dev/null +++ b/src/Library/demos/Dialogs/main.blp @@ -0,0 +1,40 @@ +using Gtk 4.0; +using Adw 1; + +Adw.StatusPage { + title: "Dialogs"; + description: _("Present options, choices or information to users"); + + Box { + orientation: vertical; + halign: center; + + Button button_confirmation { + label: "Confirmation Dialog"; + margin-bottom: 30; + styles ["pill"] + } + + Button button_error { + label: "Error Dialog"; + margin-bottom: 30; + styles ["pill"] + } + + Button button_advanced { + label: "Advanced Error Dialog"; + margin-bottom: 30; + styles ["pill"] + } + + LinkButton { + label: "API Reference"; + uri: "https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.2/class.MessageDialog.html"; + } + + LinkButton { + label: "Human Interface Guidelines"; + uri: "https://developer.gnome.org/hig/patterns/feedback/dialogs.html"; + } + } +} diff --git a/src/Library/demos/Dialogs/main.js b/src/Library/demos/Dialogs/main.js new file mode 100644 index 000000000..f839ec193 --- /dev/null +++ b/src/Library/demos/Dialogs/main.js @@ -0,0 +1,86 @@ +import Adw from "gi://Adw"; +import Gtk from "gi://Gtk"; + +const button_confirmation = workbench.builder.get_object("button_confirmation"); +const button_error = workbench.builder.get_object("button_error"); +const button_advanced = workbench.builder.get_object("button_advanced"); +const window = button_confirmation.get_ancestor(Gtk.Window); + +function createConfirmationDialog() { + let dialog = new Adw.MessageDialog({ + heading: "Replace File?", + body: 'A file named "example.png" already exists. Do you want to replace it?', + close_response: "cancel", + modal: true, + transient_for: window, + }); + + dialog.add_response("cancel", "Cancel"); + dialog.add_response("replace", "Replace"); + + // Use DESTRUCTIVE appearance to draw attention to the potentially damaging consequences of this action + dialog.set_response_appearance("replace", Adw.ResponseAppearance.DESTRUCTIVE); + + dialog.connect("response", (dialog, response) => { + console.log(`Selected "${response}" response.`); + }); + + dialog.present(); +} + +function createErrorDialog() { + let dialog = new Adw.MessageDialog({ + heading: "Critical Error", + body: "You did something you should not have", + close_response: "okay", + modal: true, + transient_for: window, + }); + + dialog.add_response("okay", "Okay"); + + dialog.connect("response", (dialog, response) => { + console.log(`Selected "${response}" response.`); + }); + + dialog.present(); +} + +//Creates a message dialog with an extra child +function createAdvancedDialog() { + let dialog = new Adw.MessageDialog({ + heading: "Login", + body: "A valid password is needed to continue", + close_response: "cancel", + modal: true, + transient_for: window, + }); + + dialog.add_response("cancel", "Cancel"); + dialog.add_response("login", "Login"); + + // Use SUGGESTED appearance to mark important responses such as the affirmative action + dialog.set_response_appearance("login", Adw.ResponseAppearance.SUGGESTED); + + let entry = new Gtk.PasswordEntry({ + show_peek_icon: true, + }); + + dialog.set_extra_child(entry); + + dialog.connect("response", (dialog, response) => { + if (dialog.get_response_label(response) === "Login") { + console.log( + `Selected "${response}" response with password "${entry.get_text()}"`, + ); + } else { + console.log(`Selected "${response}" response.`); + } + }); + + dialog.present(); +} + +button_confirmation.connect("clicked", createConfirmationDialog); +button_error.connect("clicked", createErrorDialog); +button_advanced.connect("clicked", createAdvancedDialog); diff --git a/src/Library/demos/Dialogs/main.json b/src/Library/demos/Dialogs/main.json new file mode 100644 index 000000000..b39ef0af9 --- /dev/null +++ b/src/Library/demos/Dialogs/main.json @@ -0,0 +1,10 @@ +{ + "name": "Dialogs", + "category": "user_interface", + "description": "Present options, choices or information to users", + "panels": [ + "code", + "preview" + ], + "autorun": true +} \ No newline at end of file diff --git a/src/Library/demos/Dialogs/main.vala b/src/Library/demos/Dialogs/main.vala new file mode 100644 index 000000000..6d4e9aa68 --- /dev/null +++ b/src/Library/demos/Dialogs/main.vala @@ -0,0 +1,89 @@ +#! /usr/bin/env -S vala workbench.vala --pkg gtk4 --pkg libadwaita-1 + +public void main() { + var button_confirmation = workbench.builder.get_object("button_confirmation") as Gtk.Button; + var button_error = workbench.builder.get_object("button_error") as Gtk.Button; + var button_advanced = workbench.builder.get_object("button_advanced") as Gtk.Button; + + button_confirmation.clicked.connect(_create_confirmation_dialog); + button_error.clicked.connect(_create_error_dialog); + button_advanced.clicked.connect(_create_advanced_dialog); +} + +private void _create_confirmation_dialog (Gtk.Button button) { + // Get the parent window + var window_type = GLib.Type.from_name("GtkWindow"); + Gtk.Window window = button.get_ancestor(window_type) as Gtk.Window; + + Adw.MessageDialog dialog = new Adw.MessageDialog + (window, + "Replace File?", + """A file named "example.png" already exists. Do you want to replace it?"""); + + dialog.close_response = "replace"; + + dialog.add_response("cancel", "Cancel"); + dialog.add_response("replace", "Replace"); + + //Use DESTRUCTIVE to draw attention to the potentially damaging consequences of using response. + dialog.set_response_appearance("replace", Adw.ResponseAppearance.DESTRUCTIVE); + + dialog.response.connect((response) => { + message("Selected \"%s\" response.\n", response); + }); + +dialog.present(); + +} + +private void _create_error_dialog (Gtk.Button button) { + // Get the parent window + var window_type = GLib.Type.from_name("GtkWindow"); + Gtk.Window window = button.get_ancestor(window_type) as Gtk.Window; + + Adw.MessageDialog dialog = new Adw.MessageDialog + (window, + "Critical Error", + "You did something you should not have"); + + dialog.close_response = "okay"; + + dialog.add_response("okay", "Okay"); + + dialog.response.connect((response) => { + message("Selected \"%s\" response.\n", response); + }); + +dialog.present(); + +} + +private void _create_advanced_dialog(Gtk.Button button) { + // Get the parent window + var window_type = GLib.Type.from_name("GtkWindow"); + Gtk.Window window = button.get_ancestor(window_type) as Gtk.Window; + + Adw.MessageDialog dialog = new Adw.MessageDialog ( + window, + "Login", + "A valid password is needed to continue"); + + dialog.close_response = "cancel"; + dialog.add_response("cancel", "Cancel"); + dialog.add_response("login", "Login"); + dialog.set_response_appearance("login", Adw.ResponseAppearance.SUGGESTED); + + var entry = new Gtk.PasswordEntry() {show_peek_icon = true}; + + dialog.set_extra_child(entry); + + dialog.response.connect((response) => { + if (dialog.get_response_label(response) == "Login") { + message("Selected \"%s\" response with password \"%s\".", response, entry.get_text()); + } else { + message("Selected \"%s\" response.", response); + } + }); + + dialog.present(); +} From fd8dd03136fdede84cfe6c2a377cbfc03547842d Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Sun, 28 May 2023 19:54:45 +0530 Subject: [PATCH 13/19] Updated code and improved UI --- src/Library/Library.blp | 4 ++-- src/Library/demos/Screenshot/main.blp | 22 ++++++++++--------- src/Library/demos/Screenshot/main.js | 29 +++++++++++++------------- src/Library/demos/Screenshot/main.json | 6 +++--- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/Library/Library.blp b/src/Library/Library.blp index c3dbe0d41..625288e93 100644 --- a/src/Library/Library.blp +++ b/src/Library/Library.blp @@ -43,8 +43,8 @@ Adw.PreferencesWindow library { title: "User Interface"; } - Adw.PreferencesGroup library_portal { - title: "User Interface"; + Adw.PreferencesGroup library_platform { + title: "Platform APIs"; } Adw.PreferencesGroup { diff --git a/src/Library/demos/Screenshot/main.blp b/src/Library/demos/Screenshot/main.blp index a9ea92472..53c6f4ac0 100644 --- a/src/Library/demos/Screenshot/main.blp +++ b/src/Library/demos/Screenshot/main.blp @@ -3,29 +3,31 @@ using Adw 1; Adw.StatusPage { title: "Screenshot"; - description: _("Keep a snapshot of your current screen"); + description: _("Take a picture of the screen"); + margin-top: 48; Box { orientation: vertical; halign: center; Picture picture { + margin-bottom: 12; + valign: center; } - Button Screenshot { - icon-name: "camera"; - margin-bottom: 40; + Button button { + icon-name: "screenshooter-symbolic"; + margin-bottom: 42; + width-request: 18; + height-request: 18; } LinkButton { label: "API Reference"; - uri: "https://libportal.org/"; - } - - LinkButton { - label: "Source Code"; - uri: "https://developer.gnome.org/hig/patterns/controls/sliders.html"; + uri: "https://libportal.org/method.Portal.take_screenshot.html"; } } } + + diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index 25e636351..d5aea4a94 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -1,13 +1,14 @@ import Xdp from "gi://Xdp"; import XdpGtk from "gi://XdpGtk4"; -const { GdkPixbuf, Gio, GLib, GObject, Gst, Gtk, Pango, PangoCairo } = - imports.gi; +import Gio from "gi://Gio"; +import GLib from "gi://GLib"; +import Gtk from "gi://Gtk"; -const Screenshot = workbench.builder.get_object("Screenshot"); +const button = workbench.builder.get_object("button"); const portal = new Xdp.Portal(); const parent = XdpGtk.parent_new_gtk(workbench.window); - -function _takeScreenshot() { +const picture = workbench.builder.get_object("picture"); +function takeScreenshot() { let flags = Xdp.ScreenshotFlags.NONE; portal.take_screenshot(parent, flags, null, (portal, result) => { @@ -15,14 +16,10 @@ function _takeScreenshot() { try { const uri = portal.take_screenshot_finish(result); - const [path] = GLib.filename_from_uri(uri); - const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( - path, - 60, - 40, - true, - ); - workbench.builder.get_object("picture").set_pixbuf(pixbuf); + const file = Gio.File.new_for_uri(uri); + picture.width_request = 180; + picture.height_request = 180; + picture.set_file(file); } catch (e) { logError(e); return; @@ -30,5 +27,7 @@ function _takeScreenshot() { }); } -Screenshot.connect("clicked", _takeScreenshot); - +button.connect("clicked", () => { + takeScreenshot(); + console.log("Screenshot Captured"); +}); diff --git a/src/Library/demos/Screenshot/main.json b/src/Library/demos/Screenshot/main.json index a849b84eb..dd9e649d1 100644 --- a/src/Library/demos/Screenshot/main.json +++ b/src/Library/demos/Screenshot/main.json @@ -1,9 +1,9 @@ { "name": "Screenshot", - "category": "portal", - "description": "Keep a snapshot of your current screen", + "category": "platform", + "description": "Take a picture of the screen", "panels": [ - "ui", + "code", "preview" ], "autorun": true From b92d7cddfa00da0c4779a5a517cdee24527e63c2 Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Mon, 29 May 2023 21:31:57 +0530 Subject: [PATCH 14/19] Updated JS Code --- src/Library/demos/Screenshot/main.js | 42 +++++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index d5aea4a94..565bf92e3 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -8,26 +8,34 @@ const button = workbench.builder.get_object("button"); const portal = new Xdp.Portal(); const parent = XdpGtk.parent_new_gtk(workbench.window); const picture = workbench.builder.get_object("picture"); -function takeScreenshot() { - let flags = Xdp.ScreenshotFlags.NONE; - portal.take_screenshot(parent, flags, null, (portal, result) => { - let path = null; - - try { - const uri = portal.take_screenshot_finish(result); - const file = Gio.File.new_for_uri(uri); - picture.width_request = 180; - picture.height_request = 180; - picture.set_file(file); - } catch (e) { - logError(e); - return; - } +function takeScreenshotAsync(parent, flags) { + return new Promise((resolve, reject) => { + portal.take_screenshot(parent, flags, null, (portal, result) => { + try { + const uri = portal.take_screenshot_finish(result); + const file = Gio.File.new_for_uri(uri); + resolve(file); + } catch (error) { + console.log(error); + } + }); }); } -button.connect("clicked", () => { - takeScreenshot(); +async function fitPicture() { + const flags = Xdp.ScreenshotFlags.NONE; + try { + const file = await takeScreenshotAsync(parent, flags); + picture.width_request = 180; + picture.height_request = 180; + picture.set_file(file); + } catch (error) { + console.error("Error capturing screenshot:", error); + } +} + +button.connect("clicked", async () => { + await fitPicture(); console.log("Screenshot Captured"); }); From 88a6568d3874c9131cd784a9786116a7c49f73c5 Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Tue, 30 May 2023 05:46:34 +0530 Subject: [PATCH 15/19] Improved JS Code and applied changes --- src/Library/demos/Screenshot/main.blp | 6 +-- src/Library/demos/Screenshot/main.js | 60 ++++++++++++++++----------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/Library/demos/Screenshot/main.blp b/src/Library/demos/Screenshot/main.blp index 53c6f4ac0..c082ca57f 100644 --- a/src/Library/demos/Screenshot/main.blp +++ b/src/Library/demos/Screenshot/main.blp @@ -12,14 +12,13 @@ Adw.StatusPage { Picture picture { margin-bottom: 12; - valign: center; } Button button { icon-name: "screenshooter-symbolic"; margin-bottom: 42; - width-request: 18; - height-request: 18; + width-request: 6; + height-request: 48; } LinkButton { @@ -31,3 +30,4 @@ Adw.StatusPage { + diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index 565bf92e3..c6985a55c 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -3,39 +3,49 @@ import XdpGtk from "gi://XdpGtk4"; import Gio from "gi://Gio"; import GLib from "gi://GLib"; import Gtk from "gi://Gtk"; +import Adw from "gi://Adw"; const button = workbench.builder.get_object("button"); const portal = new Xdp.Portal(); const parent = XdpGtk.parent_new_gtk(workbench.window); const picture = workbench.builder.get_object("picture"); +const window = button.get_ancestor(Gtk.Window); -function takeScreenshotAsync(parent, flags) { - return new Promise((resolve, reject) => { - portal.take_screenshot(parent, flags, null, (portal, result) => { - try { - const uri = portal.take_screenshot_finish(result); - const file = Gio.File.new_for_uri(uri); - resolve(file); - } catch (error) { - console.log(error); - } - }); +Gio._promisify( + Xdp.Portal.prototype, + "take_screenshot", + "take_screenshot_finish", +); + +function createErrorDialog() { + const dialog = new Adw.MessageDialog({ + heading: "Permission Error", + body: "Ensure Screenshot permission is enabled in Settings>Apps>Workbench", + close_response: "Okay", + modal: true, + transient_for: window, }); -} -async function fitPicture() { - const flags = Xdp.ScreenshotFlags.NONE; - try { - const file = await takeScreenshotAsync(parent, flags); - picture.width_request = 180; - picture.height_request = 180; - picture.set_file(file); - } catch (error) { - console.error("Error capturing screenshot:", error); - } + dialog.add_response("Okay", "Okay"); + dialog.present(); } -button.connect("clicked", async () => { - await fitPicture(); - console.log("Screenshot Captured"); +button.connect("clicked", () => { + const flags = Xdp.ScreenshotFlags.NONE; + portal.take_screenshot(parent, flags, null, (portal, result) => { + try { + const uri = portal.take_screenshot_finish(result); + const file = Gio.File.new_for_uri(uri); + picture.width_request = 180; + picture.height_request = 180; + picture.set_file(file); + console.log("Screenshot Captured"); + } catch (error) { + if (error instanceof Gio.IOErrorEnum) { + createErrorDialog(); + } else { + logError(error); + } + } + }); }); From 03695f83aa08f56d95917c54b2e8c4418815e275 Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Tue, 30 May 2023 06:18:50 +0530 Subject: [PATCH 16/19] Improved Code --- src/Library/demos/Screenshot/main.js | 38 +++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index c6985a55c..7f23f339e 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -30,22 +30,26 @@ function createErrorDialog() { dialog.present(); } -button.connect("clicked", () => { - const flags = Xdp.ScreenshotFlags.NONE; - portal.take_screenshot(parent, flags, null, (portal, result) => { - try { - const uri = portal.take_screenshot_finish(result); - const file = Gio.File.new_for_uri(uri); - picture.width_request = 180; - picture.height_request = 180; - picture.set_file(file); - console.log("Screenshot Captured"); - } catch (error) { - if (error instanceof Gio.IOErrorEnum) { - createErrorDialog(); - } else { - logError(error); - } +button.connect("clicked", async () => { + try { + const flags = Xdp.ScreenshotFlags.NONE; + const result = await new Promise((resolve, reject) => { + portal.take_screenshot(parent, flags, null, (portal, result) => { + resolve(result); + }); + }); + + const uri = await portal.take_screenshot_finish(result); + const file = Gio.File.new_for_uri(uri); + picture.width_request = 180; + picture.height_request = 180; + picture.set_file(file); + console.log("Screenshot Captured"); + } catch (error) { + if (error instanceof Gio.IOErrorEnum) { + createErrorDialog(); + } else { + logError(error); } - }); + } }); From bd9340638b3f8b511d1dc8dfb258b21ae7432cca Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Tue, 30 May 2023 06:41:46 +0530 Subject: [PATCH 17/19] Revert "Improved Code" This reverts commit 03695f83aa08f56d95917c54b2e8c4418815e275. --- src/Library/demos/Screenshot/main.js | 38 +++++++++++++--------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index 7f23f339e..c6985a55c 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -30,26 +30,22 @@ function createErrorDialog() { dialog.present(); } -button.connect("clicked", async () => { - try { - const flags = Xdp.ScreenshotFlags.NONE; - const result = await new Promise((resolve, reject) => { - portal.take_screenshot(parent, flags, null, (portal, result) => { - resolve(result); - }); - }); - - const uri = await portal.take_screenshot_finish(result); - const file = Gio.File.new_for_uri(uri); - picture.width_request = 180; - picture.height_request = 180; - picture.set_file(file); - console.log("Screenshot Captured"); - } catch (error) { - if (error instanceof Gio.IOErrorEnum) { - createErrorDialog(); - } else { - logError(error); +button.connect("clicked", () => { + const flags = Xdp.ScreenshotFlags.NONE; + portal.take_screenshot(parent, flags, null, (portal, result) => { + try { + const uri = portal.take_screenshot_finish(result); + const file = Gio.File.new_for_uri(uri); + picture.width_request = 180; + picture.height_request = 180; + picture.set_file(file); + console.log("Screenshot Captured"); + } catch (error) { + if (error instanceof Gio.IOErrorEnum) { + createErrorDialog(); + } else { + logError(error); + } } - } + }); }); From 8d438a4a9628fcef56c6016a8f582a3871fdffb5 Mon Sep 17 00:00:00 2001 From: Sriyansh Shivam Date: Tue, 30 May 2023 06:44:58 +0530 Subject: [PATCH 18/19] Minor Fixes --- src/Library/demos/Screenshot/main.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index c6985a55c..8d194e11a 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -11,12 +11,6 @@ const parent = XdpGtk.parent_new_gtk(workbench.window); const picture = workbench.builder.get_object("picture"); const window = button.get_ancestor(Gtk.Window); -Gio._promisify( - Xdp.Portal.prototype, - "take_screenshot", - "take_screenshot_finish", -); - function createErrorDialog() { const dialog = new Adw.MessageDialog({ heading: "Permission Error", From 0f2239006e295771b54b742944c9c478a2bcfa81 Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Tue, 30 May 2023 11:27:39 +0200 Subject: [PATCH 19/19] Improvements --- src/Library/demos/Screenshot/main.blp | 15 +++---- src/Library/demos/Screenshot/main.js | 61 ++++++++++++++------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/Library/demos/Screenshot/main.blp b/src/Library/demos/Screenshot/main.blp index c082ca57f..fc58b839e 100644 --- a/src/Library/demos/Screenshot/main.blp +++ b/src/Library/demos/Screenshot/main.blp @@ -10,15 +10,18 @@ Adw.StatusPage { orientation: vertical; halign: center; - Picture picture { + Box { + Picture picture {} margin-bottom: 12; + width-request: 256; + height-request: 256; } Button button { - icon-name: "screenshooter-symbolic"; + label: _("Take Screenshot"); margin-bottom: 42; - width-request: 6; - height-request: 48; + halign: center; + styles ["suggested-action"] } LinkButton { @@ -27,7 +30,3 @@ Adw.StatusPage { } } } - - - - diff --git a/src/Library/demos/Screenshot/main.js b/src/Library/demos/Screenshot/main.js index 8d194e11a..50179f75d 100644 --- a/src/Library/demos/Screenshot/main.js +++ b/src/Library/demos/Screenshot/main.js @@ -1,45 +1,48 @@ import Xdp from "gi://Xdp"; import XdpGtk from "gi://XdpGtk4"; import Gio from "gi://Gio"; -import GLib from "gi://GLib"; -import Gtk from "gi://Gtk"; import Adw from "gi://Adw"; -const button = workbench.builder.get_object("button"); const portal = new Xdp.Portal(); const parent = XdpGtk.parent_new_gtk(workbench.window); + +const button = workbench.builder.get_object("button"); const picture = workbench.builder.get_object("picture"); -const window = button.get_ancestor(Gtk.Window); -function createErrorDialog() { +Gio._promisify( + Xdp.Portal.prototype, + "take_screenshot", + "take_screenshot_finish", +); + +button.connect("clicked", () => { + takeScreenshot().catch(logError); +}); + +async function takeScreenshot() { + const flags = Xdp.ScreenshotFlags.NONE; + + let uri; + try { + uri = await portal.take_screenshot(parent, flags, null); + } catch (err) { + showPermissionError(); + return; + } + + const file = Gio.File.new_for_uri(uri); + picture.set_file(file); +} + +function showPermissionError() { const dialog = new Adw.MessageDialog({ heading: "Permission Error", - body: "Ensure Screenshot permission is enabled in Settings>Apps>Workbench", - close_response: "Okay", + body: "Ensure Screenshot permission is enabled in\nSettings → Apps → Workbench", + close_response: "ok", modal: true, - transient_for: window, + transient_for: workbench.window, }); - dialog.add_response("Okay", "Okay"); + dialog.add_response("ok", "OK"); dialog.present(); } - -button.connect("clicked", () => { - const flags = Xdp.ScreenshotFlags.NONE; - portal.take_screenshot(parent, flags, null, (portal, result) => { - try { - const uri = portal.take_screenshot_finish(result); - const file = Gio.File.new_for_uri(uri); - picture.width_request = 180; - picture.height_request = 180; - picture.set_file(file); - console.log("Screenshot Captured"); - } catch (error) { - if (error instanceof Gio.IOErrorEnum) { - createErrorDialog(); - } else { - logError(error); - } - } - }); -});