From 2b397ad2a483c684697f5b1685fb5c3b6f2868f8 Mon Sep 17 00:00:00 2001 From: AkshayWarrier Date: Sun, 30 Jul 2023 02:40:34 +0530 Subject: [PATCH 01/14] Improve Documentation Viewer sidebar --- src/Library/demos/Sidebar/main.blp | 46 +++++++++++ src/Library/demos/Sidebar/main.js | 115 ++++++++++++++++++++++++++++ src/Library/demos/Sidebar/main.json | 7 ++ 3 files changed, 168 insertions(+) create mode 100644 src/Library/demos/Sidebar/main.blp create mode 100644 src/Library/demos/Sidebar/main.js create mode 100644 src/Library/demos/Sidebar/main.json diff --git a/src/Library/demos/Sidebar/main.blp b/src/Library/demos/Sidebar/main.blp new file mode 100644 index 000000000..b9fc0d216 --- /dev/null +++ b/src/Library/demos/Sidebar/main.blp @@ -0,0 +1,46 @@ +using Gtk 4.0; +using Adw 1; + +Box { + margin-top: 48; + margin-bottom: 48; + halign: center; + + Box { + orientation: vertical; + spacing: 12; + + SearchEntry { + + } + + ScrolledWindow { + vexpand: true; + width-request: 500; + + Stack stack { + transition-type: slide_left; + visible-child: listbox; + ListBox listbox { + styles ["boxed-list"] + } + + ListView list_view { + enable-rubberband: true; + factory: BuilderListItemFactory { + template ListItem { + child: Gtk.Box { + Gtk.Label { + label: bind template.item as .string; + height-request: 50; + margin-start: 12; + margin-end: 12; + } + }; + } + }; + } + } + } + } +} diff --git a/src/Library/demos/Sidebar/main.js b/src/Library/demos/Sidebar/main.js new file mode 100644 index 000000000..7ed26fc6c --- /dev/null +++ b/src/Library/demos/Sidebar/main.js @@ -0,0 +1,115 @@ +import Adw from "gi://Adw"; +import Gtk from "gi://Gtk"; +import Gio from "gi://Gio"; +import GLib from "gi://GLib"; + +const listbox = workbench.builder.get_object("listbox"); +const base_path = Gio.File.new_for_path("/app/share/doc"); +const stack = workbench.builder.get_object("stack"); + +const dir_list = Gtk.DirectoryList.new("", null); +const list_view = workbench.builder.get_object("list_view"); + +dir_list.connect("notify::loading", () => { + populateList().catch(logError); +}); + +let dir; +listbox.connect("row-selected", (self, row) => { + dir = base_path.resolve_relative_path(row.dir); + dir_list.file = dir; + stack.visible_child = list_view; +}); + +async function populateList() { + const string_list = new Gtk.StringList(); + const enumerator = await dir.enumerate_children_async( + "standard::name", + Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, + GLib.PRIORITY_DEFAULT, + null, + ); + + for await (const info of enumerator) { + string_list.append(info.get_name()); + } + + const model = new Gtk.SingleSelection({ model: string_list }); + list_view.model = model; +} + +getDocs(base_path) + .then((docs) => { + for (const doc of docs) { + const row = new Adw.ActionRow({ + title: doc.title, + }); + row.uri = doc.uri; + row.dir = doc.dir; + row.add_suffix(new Gtk.Image({ icon_name: "go-next-symbolic" })); + listbox.append(row); + } + }) + .catch(logError); + +async function getDocs(base_path) { + const docs = []; + const dirs = await list(base_path); + + for (const dir of dirs) { + const results = await Promise.allSettled([ + readDocIndexJSON(base_path, dir), + readDocIndexHTML(base_path, dir), + ]); + const fulfilled = results.find((result) => result.status === "fulfilled"); + if (!fulfilled) continue; + + const title = fulfilled.value; + const uri = base_path.get_child(dir).get_child("index.html").get_uri(); + + docs.push({ + title, + uri, + dir, + }); + } + + return docs; +} + +async function readDocIndexJSON(base_path, dir) { + const file = base_path.get_child(dir).get_child("index.json"); + const [data] = await file.load_contents_async(null); + const json = JSON.parse(decode(data)); + return `${json["meta"]["ns"]}-${json["meta"]["version"]}`; +} + +async function readDocIndexHTML(base_path, dir) { + const file = base_path.get_child(dir).get_child("api-index-full.html"); + const [data] = await file.load_contents_async(null); + const html = decode(data); + const pattern = /Index: ([^<]+)/; + return html.match(pattern)[1]; +} + +async function list(dir) { + // List all files in dir + const files = []; + const enumerator = await dir.enumerate_children_async( + "standard::name", + Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, + GLib.PRIORITY_DEFAULT, + null, + ); + for await (const info of enumerator) { + files.push(info.get_name()); + } + return files; +} + +function decode(data) { + if (data instanceof GLib.Bytes) { + data = data.toArray(); + } + return new TextDecoder().decode(data); +} diff --git a/src/Library/demos/Sidebar/main.json b/src/Library/demos/Sidebar/main.json new file mode 100644 index 000000000..d203371b0 --- /dev/null +++ b/src/Library/demos/Sidebar/main.json @@ -0,0 +1,7 @@ +{ + "name": "Sidebar", + "category": "user_interface", + "description": "The sidebar for the documentation viewer", + "panels": ["ui", "preview"], + "autorun": true +} From cd8f665b4a4d57adb093853cde671ecf1215f03d Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Fri, 4 Aug 2023 21:49:54 +0530 Subject: [PATCH 02/14] Documentation Viewer: Sidebar with TreeExpanders --- src/DocumentationViewer.blp | 31 +++-- src/DocumentationViewer.js | 263 +++++++++++++++++++++++++----------- 2 files changed, 208 insertions(+), 86 deletions(-) diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index 53049bda8..1a1daa876 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -57,16 +57,27 @@ Window documentation_viewer { } } - ScrolledWindow { - ListBox listbox { - margin-top: 12; - margin-bottom: 12; - margin-start: 12; - margin-end: 12; - hexpand: true; - vexpand: true; - valign: start; - styles ["boxed-list"] + ScrolledWindow { + vexpand: true; + width-request: 400; + + ListView list_view { + enable-rubberband: true; + factory: BuilderListItemFactory { + template ListItem { + child: TreeExpander expander{ + list-row: bind template.item; + child: + Inscription { + hexpand: true; + nat-chars: 10; + text-overflow: ellipsize_end; + text: bind expander.item as <$DocumentationPage>.name; + }; + }; + } + }; + styles ["navigation-sidebar"] } } styles ["background"] diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index 7c25282d1..4ae8bb02c 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -8,34 +8,50 @@ import WebKit from "gi://WebKit"; import { decode } from "./util.js"; import resource from "./DocumentationViewer.blp"; +const DocumentationPage = GObject.registerClass( + { + GTypeName: "DocumentationPage", + Properties: { + name: GObject.ParamSpec.string( + "name", + "name", + "Display name in the sidebar", + GObject.ParamFlags.READWRITE, + "", + ), + uri: GObject.ParamSpec.string( + "uri", + "uri", + "Uri to the documentation page", + GObject.ParamFlags.READWRITE, + "", + ), + children: GObject.ParamSpec.object( + "children", + "children", + null, + GObject.ParamFlags.READWRITE, + Gio.ListStore, + ), + }, + }, + class DocumentationPage extends GObject.Object {}, +); + export default function DocumentationViewer({ application }) { const builder = Gtk.Builder.new_from_resource(resource); const window = builder.get_object("documentation_viewer"); const webview = builder.get_object("webview"); - const listbox = builder.get_object("listbox"); - const search_bar = builder.get_object("search_bar"); - const button_sidebar = builder.get_object("button_sidebar"); - const button_search = builder.get_object("button_search"); - const search_entry = builder.get_object("search_entry"); + const list_view = builder.get_object("list_view"); const button_back = builder.get_object("button_back"); const button_forward = builder.get_object("button_forward"); const base_path = Gio.File.new_for_path("/app/share/doc"); - webview.load_uri( - base_path.resolve_relative_path("gtk4/index.html").get_uri(), - ); - let loaded = false; webview.connect("load-changed", (self, load_event) => { updateButtons(); - - if (load_event === WebKit.LoadEvent.FINISHED) { - loaded = true; - button_sidebar.active = false; - } else { - loaded = false; - } + if (load_event === WebKit.LoadEvent.FINISHED) disableDocSidebar(webview); }); webview.get_back_forward_list().connect("changed", () => { @@ -55,64 +71,44 @@ export default function DocumentationViewer({ application }) { webview.go_forward(); }); - button_sidebar.connect("toggled", () => { - if (loaded) { - if (button_sidebar.active) disableDocSidebar(webview); - else enableDocSidebar(webview); - } - }); - - button_sidebar.connect("toggled", () => { - if (loaded) { - if (button_sidebar.active) disableDocSidebar(webview); - else enableDocSidebar(webview); - } - }); - - search_entry.connect("search-changed", () => { - listbox.invalidate_filter(); - }); - - search_bar.bind_property( - "search-mode-enabled", - button_search, - "active", - GObject.BindingFlags.BIDIRECTIONAL, - ); - - button_search.connect("toggled", () => { - if (button_search.active) button_sidebar.active = true; - }); - - listbox.set_sort_func(sort); - listbox.invalidate_sort(); - listbox.set_filter_func(filter); - listbox.connect("row-selected", (self, row) => { - webview.load_uri(row.uri); - }); - - getDocs(base_path) + getNamespaces(base_path) .then((docs) => { + const root = newListStore(); for (const doc of docs) { - const row = new Adw.ActionRow({ - title: doc.title, - }); - row.uri = doc.uri; - row.add_suffix(new Gtk.Image({ icon_name: "go-next-symbolic" })); - listbox.append(row); + const dir_path = base_path.resolve_relative_path(doc.dir); + getChildren(dir_path) + .then((model) => { + root.append(new DocumentationPage({ + name: doc.title, + uri: doc.uri, + children: model, + })) + }) } + return root; + }) + .then((root) => { + const tree_model = Gtk.TreeListModel.new( + root, + false, + false, + item => item.children, + ) + const sorter = Gtk.TreeListRowSorter.new(Gtk.CustomSorter.new((a,b) => { + const name1 = a.name; + const name2 = b.name; + return name1.localeCompare(name2); + })); + const sort_model = Gtk.SortListModel.new(tree_model, sorter); + const selection_model = new Gtk.SingleSelection({model: sort_model}); + selection_model.connect("notify::selected", () => { + const uri = selection_model.selected_item.item.uri; + if (uri) webview.load_uri(uri); + }) + list_view.model = selection_model; }) .catch(logError); - function sort(a, b) { - return a.title.localeCompare(b.title); - } - - function filter(row) { - const re = new RegExp(search_entry.text, "i"); - return re.test(row.title); - } - const action_documentation = new Gio.SimpleAction({ name: "documentation", parameter_type: null, @@ -123,14 +119,124 @@ export default function DocumentationViewer({ application }) { application.add_action(action_documentation); } -async function getDocs(base_path) { - const docs = []; +async function getChildren(dir) { + const docs = await list(dir); + return createSections(docs, dir); +} + +function createSections(docs, dir) { + const index_html = dir.get_child("index.html").get_uri(); + + const section_name_uri = { + class: ["Classes", "#classes"], + iface: ["Interfaces", "#interfaces"], + struct: ["Structs", "#structs"], + alias: ["Aliases", "#aliases"], + enum: ["Enumerations", "#enums"], + flags: ["Bitfields", "#bitfields"], + error: ["Error Domains", "#domains"], + callback: ["Callbacks", "#callbacks"], + const: ["Constants", "#constants"], + } + + const sections = {}; + for (const section in section_name_uri) + sections[section] = newListStore(); + + const subsection_name_uri = { + ctor: ["Constructors", "#constructors"], + type_func: ["Functions", "#type-functions"], + method: ["Instance Methods", "#methods"], + property: ["Properties", "#properties"], + signal: ["Signals", "#signals"], + class_method: ["Class Methods", "#class-methods"], + vfunc: ["Virtual Methods", "#virtual-methods"], + } + + // Contains all items from the namespace that need a subsection + // A subsection is used to show an item's methods, properties, signals etc + const subsections = {}; + // List of sections that need subsections + const subsections_required = ["class", "iface", "struct", "error"]; + + for (const doc of docs) { + const split_name = doc.split("."); + // If file is of the form xx.xx.html for example class.Button.html + if (split_name.length == 3 && sections[split_name[0]]) { + const doc_page = new DocumentationPage({ + name: split_name[1], + uri: dir.get_child(doc).get_uri(), + // children is set to a non-null value later if it needs subsections + children: null, + }) + + // If an item needs a subsection, then create empty "buckets" for it + if (subsections_required.includes(split_name[0])) { + const subsection = {}; + for (const sub in subsection_name_uri) + subsection[sub] = newListStore() + subsections[split_name[1]] = subsection; + } + // Add file into the corresponding section it belongs to + sections[split_name[0]].append(doc_page) + } + } + + // Iterate through all the files again to add items into the subsection "buckets" + for (const doc of docs) { + const split_name = doc.split("."); + // File is of the form xx.xx.xx.html for example ctor.Button.new.html + if (split_name.length == 4 && subsections[split_name[1]]) { + const doc_page = new DocumentationPage({ + name: split_name[2], + uri: dir.get_child(doc).get_uri(), + children: null, + }) + // Add file to the subsection it belongs to + subsections[split_name[1]][split_name[0]].append(doc_page); + } + } + // Sets the children for items that need subsections + createSubsections(subsections, subsections_required, subsection_name_uri, sections); + + const sections_model = newListStore(); + for (const section in sections) { + sections_model.append(new DocumentationPage({ + name: section_name_uri[section][0], + uri: `${index_html}${section_name_uri[section][1]}`, + children: sections[section], + })) + } + return sections_model +} + +function createSubsections(subsections, subsections_required, subsection_name_uri, sections) { + for (const type of subsections_required) { + for (const item of sections[type]) { + const model = newListStore(); + const name = item.name; + for (const subsection in subsections[name]) { + // If the ListStore is empty then dont create a subsection for it + if (subsections[name][subsection].get_n_items() > 0) + model.append(new DocumentationPage({ + name: subsection_name_uri[subsection][0], + uri: `${item.uri}${subsection_name_uri[subsection][1]}`, + children: subsections[name][subsection], + })) + } + item.children = model; + } + } +} + +async function getNamespaces(base_path) { + const namespaces = []; const dirs = await list(base_path); for (const dir of dirs) { const results = await Promise.allSettled([ - readDocIndexJSON(base_path, dir), - readDocIndexHTML(base_path, dir), + getNamespaceFromIndexJSON(base_path, dir), + getNamespaceFromIndexHTML(base_path, dir), ]); const fulfilled = results.find((result) => result.status === "fulfilled"); @@ -138,23 +244,24 @@ async function getDocs(base_path) { const title = fulfilled.value; const uri = base_path.get_child(dir).get_child("index.html").get_uri(); - docs.push({ + namespaces.push({ title, uri, + dir }); } - return docs; + return namespaces; } -async function readDocIndexJSON(base_path, dir) { +async function getNamespaceFromIndexJSON(base_path, dir) { const file = base_path.get_child(dir).get_child("index.json"); const [data] = await file.load_contents_async(null); const json = JSON.parse(decode(data)); return `${json["meta"]["ns"]}-${json["meta"]["version"]}`; } -async function readDocIndexHTML(base_path, dir) { +async function getNamespaceFromIndexHTML(base_path, dir) { const file = base_path.get_child(dir).get_child("api-index-full.html"); const [data] = await file.load_contents_async(null); const html = decode(data); @@ -202,3 +309,7 @@ async function enableDocSidebar(webview) { } } } + +function newListStore() { + return Gio.ListStore.new(DocumentationPage) +} From b94159fb76f7b3db10d89e5a6b500ceb31cc6e8f Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Fri, 4 Aug 2023 21:54:45 +0530 Subject: [PATCH 03/14] Documentation Viewer: Formatting --- src/DocumentationViewer.js | 96 ++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index 4ae8bb02c..282191e66 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -76,14 +76,15 @@ export default function DocumentationViewer({ application }) { const root = newListStore(); for (const doc of docs) { const dir_path = base_path.resolve_relative_path(doc.dir); - getChildren(dir_path) - .then((model) => { - root.append(new DocumentationPage({ + getChildren(dir_path).then((model) => { + root.append( + new DocumentationPage({ name: doc.title, uri: doc.uri, children: model, - })) - }) + }), + ); + }); } return root; }) @@ -92,19 +93,21 @@ export default function DocumentationViewer({ application }) { root, false, false, - item => item.children, - ) - const sorter = Gtk.TreeListRowSorter.new(Gtk.CustomSorter.new((a,b) => { - const name1 = a.name; - const name2 = b.name; - return name1.localeCompare(name2); - })); + (item) => item.children, + ); + const sorter = Gtk.TreeListRowSorter.new( + Gtk.CustomSorter.new((a, b) => { + const name1 = a.name; + const name2 = b.name; + return name1.localeCompare(name2); + }), + ); const sort_model = Gtk.SortListModel.new(tree_model, sorter); - const selection_model = new Gtk.SingleSelection({model: sort_model}); + const selection_model = new Gtk.SingleSelection({ model: sort_model }); selection_model.connect("notify::selected", () => { const uri = selection_model.selected_item.item.uri; if (uri) webview.load_uri(uri); - }) + }); list_view.model = selection_model; }) .catch(logError); @@ -137,11 +140,10 @@ function createSections(docs, dir) { error: ["Error Domains", "#domains"], callback: ["Callbacks", "#callbacks"], const: ["Constants", "#constants"], - } + }; const sections = {}; - for (const section in section_name_uri) - sections[section] = newListStore(); + for (const section in section_name_uri) sections[section] = newListStore(); const subsection_name_uri = { ctor: ["Constructors", "#constructors"], @@ -151,7 +153,7 @@ function createSections(docs, dir) { signal: ["Signals", "#signals"], class_method: ["Class Methods", "#class-methods"], vfunc: ["Virtual Methods", "#virtual-methods"], - } + }; // Contains all items from the namespace that need a subsection // A subsection is used to show an item's methods, properties, signals etc @@ -168,17 +170,16 @@ function createSections(docs, dir) { uri: dir.get_child(doc).get_uri(), // children is set to a non-null value later if it needs subsections children: null, - }) + }); // If an item needs a subsection, then create empty "buckets" for it if (subsections_required.includes(split_name[0])) { const subsection = {}; - for (const sub in subsection_name_uri) - subsection[sub] = newListStore() + for (const sub in subsection_name_uri) subsection[sub] = newListStore(); subsections[split_name[1]] = subsection; } // Add file into the corresponding section it belongs to - sections[split_name[0]].append(doc_page) + sections[split_name[0]].append(doc_page); } } @@ -186,31 +187,43 @@ function createSections(docs, dir) { for (const doc of docs) { const split_name = doc.split("."); // File is of the form xx.xx.xx.html for example ctor.Button.new.html - if (split_name.length == 4 && subsections[split_name[1]]) { - const doc_page = new DocumentationPage({ + if (split_name.length == 4 && subsections[split_name[1]]) { + const doc_page = new DocumentationPage({ name: split_name[2], uri: dir.get_child(doc).get_uri(), children: null, - }) + }); // Add file to the subsection it belongs to subsections[split_name[1]][split_name[0]].append(doc_page); } } // Sets the children for items that need subsections - createSubsections(subsections, subsections_required, subsection_name_uri, sections); + createSubsections( + subsections, + subsections_required, + subsection_name_uri, + sections, + ); const sections_model = newListStore(); for (const section in sections) { - sections_model.append(new DocumentationPage({ - name: section_name_uri[section][0], - uri: `${index_html}${section_name_uri[section][1]}`, - children: sections[section], - })) + sections_model.append( + new DocumentationPage({ + name: section_name_uri[section][0], + uri: `${index_html}${section_name_uri[section][1]}`, + children: sections[section], + }), + ); } - return sections_model + return sections_model; } -function createSubsections(subsections, subsections_required, subsection_name_uri, sections) { +function createSubsections( + subsections, + subsections_required, + subsection_name_uri, + sections, +) { for (const type of subsections_required) { for (const item of sections[type]) { const model = newListStore(); @@ -218,11 +231,13 @@ function createSubsections(subsections, subsections_required, subsection_name_ur for (const subsection in subsections[name]) { // If the ListStore is empty then dont create a subsection for it if (subsections[name][subsection].get_n_items() > 0) - model.append(new DocumentationPage({ - name: subsection_name_uri[subsection][0], - uri: `${item.uri}${subsection_name_uri[subsection][1]}`, - children: subsections[name][subsection], - })) + model.append( + new DocumentationPage({ + name: subsection_name_uri[subsection][0], + uri: `${item.uri}${subsection_name_uri[subsection][1]}`, + children: subsections[name][subsection], + }), + ); } item.children = model; } @@ -247,7 +262,7 @@ async function getNamespaces(base_path) { namespaces.push({ title, uri, - dir + dir, }); } @@ -311,5 +326,6 @@ async function enableDocSidebar(webview) { } function newListStore() { - return Gio.ListStore.new(DocumentationPage) + return Gio.ListStore.new(DocumentationPage); } + From 680c1d6d201dfb5e13d0deed72d2a848b81070ad Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Fri, 4 Aug 2023 21:55:54 +0530 Subject: [PATCH 04/14] Documentation Viewer: minor changes --- src/DocumentationViewer.blp | 4 ++-- src/DocumentationViewer.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index 1a1daa876..5800dba50 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -52,7 +52,7 @@ Window documentation_viewer { SearchBar search_bar { key-capture-widget: sidebar; - SearchEntry search_entry{ + SearchEntry search_entry { hexpand: true; } } @@ -65,7 +65,7 @@ Window documentation_viewer { enable-rubberband: true; factory: BuilderListItemFactory { template ListItem { - child: TreeExpander expander{ + child: TreeExpander expander { list-row: bind template.item; child: Inscription { diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index 282191e66..ef4a94f13 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -244,6 +244,10 @@ function createSubsections( } } +function newListStore() { + return Gio.ListStore.new(DocumentationPage); +} + async function getNamespaces(base_path) { const namespaces = []; const dirs = await list(base_path); @@ -325,7 +329,3 @@ async function enableDocSidebar(webview) { } } -function newListStore() { - return Gio.ListStore.new(DocumentationPage); -} - From d433d0a2c264e2537eb5925b7778db8b620a4bf5 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Fri, 4 Aug 2023 22:15:56 +0530 Subject: [PATCH 05/14] Dont need this anymore --- src/Library/demos/Sidebar/main.blp | 46 ----------- src/Library/demos/Sidebar/main.js | 115 ---------------------------- src/Library/demos/Sidebar/main.json | 7 -- 3 files changed, 168 deletions(-) delete mode 100644 src/Library/demos/Sidebar/main.blp delete mode 100644 src/Library/demos/Sidebar/main.js delete mode 100644 src/Library/demos/Sidebar/main.json diff --git a/src/Library/demos/Sidebar/main.blp b/src/Library/demos/Sidebar/main.blp deleted file mode 100644 index b9fc0d216..000000000 --- a/src/Library/demos/Sidebar/main.blp +++ /dev/null @@ -1,46 +0,0 @@ -using Gtk 4.0; -using Adw 1; - -Box { - margin-top: 48; - margin-bottom: 48; - halign: center; - - Box { - orientation: vertical; - spacing: 12; - - SearchEntry { - - } - - ScrolledWindow { - vexpand: true; - width-request: 500; - - Stack stack { - transition-type: slide_left; - visible-child: listbox; - ListBox listbox { - styles ["boxed-list"] - } - - ListView list_view { - enable-rubberband: true; - factory: BuilderListItemFactory { - template ListItem { - child: Gtk.Box { - Gtk.Label { - label: bind template.item as <StringObject>.string; - height-request: 50; - margin-start: 12; - margin-end: 12; - } - }; - } - }; - } - } - } - } -} diff --git a/src/Library/demos/Sidebar/main.js b/src/Library/demos/Sidebar/main.js deleted file mode 100644 index 7ed26fc6c..000000000 --- a/src/Library/demos/Sidebar/main.js +++ /dev/null @@ -1,115 +0,0 @@ -import Adw from "gi://Adw"; -import Gtk from "gi://Gtk"; -import Gio from "gi://Gio"; -import GLib from "gi://GLib"; - -const listbox = workbench.builder.get_object("listbox"); -const base_path = Gio.File.new_for_path("/app/share/doc"); -const stack = workbench.builder.get_object("stack"); - -const dir_list = Gtk.DirectoryList.new("", null); -const list_view = workbench.builder.get_object("list_view"); - -dir_list.connect("notify::loading", () => { - populateList().catch(logError); -}); - -let dir; -listbox.connect("row-selected", (self, row) => { - dir = base_path.resolve_relative_path(row.dir); - dir_list.file = dir; - stack.visible_child = list_view; -}); - -async function populateList() { - const string_list = new Gtk.StringList(); - const enumerator = await dir.enumerate_children_async( - "standard::name", - Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, - GLib.PRIORITY_DEFAULT, - null, - ); - - for await (const info of enumerator) { - string_list.append(info.get_name()); - } - - const model = new Gtk.SingleSelection({ model: string_list }); - list_view.model = model; -} - -getDocs(base_path) - .then((docs) => { - for (const doc of docs) { - const row = new Adw.ActionRow({ - title: doc.title, - }); - row.uri = doc.uri; - row.dir = doc.dir; - row.add_suffix(new Gtk.Image({ icon_name: "go-next-symbolic" })); - listbox.append(row); - } - }) - .catch(logError); - -async function getDocs(base_path) { - const docs = []; - const dirs = await list(base_path); - - for (const dir of dirs) { - const results = await Promise.allSettled([ - readDocIndexJSON(base_path, dir), - readDocIndexHTML(base_path, dir), - ]); - const fulfilled = results.find((result) => result.status === "fulfilled"); - if (!fulfilled) continue; - - const title = fulfilled.value; - const uri = base_path.get_child(dir).get_child("index.html").get_uri(); - - docs.push({ - title, - uri, - dir, - }); - } - - return docs; -} - -async function readDocIndexJSON(base_path, dir) { - const file = base_path.get_child(dir).get_child("index.json"); - const [data] = await file.load_contents_async(null); - const json = JSON.parse(decode(data)); - return `${json["meta"]["ns"]}-${json["meta"]["version"]}`; -} - -async function readDocIndexHTML(base_path, dir) { - const file = base_path.get_child(dir).get_child("api-index-full.html"); - const [data] = await file.load_contents_async(null); - const html = decode(data); - const pattern = /<title>Index: ([^<]+)/; - return html.match(pattern)[1]; -} - -async function list(dir) { - // List all files in dir - const files = []; - const enumerator = await dir.enumerate_children_async( - "standard::name", - Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, - GLib.PRIORITY_DEFAULT, - null, - ); - for await (const info of enumerator) { - files.push(info.get_name()); - } - return files; -} - -function decode(data) { - if (data instanceof GLib.Bytes) { - data = data.toArray(); - } - return new TextDecoder().decode(data); -} diff --git a/src/Library/demos/Sidebar/main.json b/src/Library/demos/Sidebar/main.json deleted file mode 100644 index d203371b0..000000000 --- a/src/Library/demos/Sidebar/main.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Sidebar", - "category": "user_interface", - "description": "The sidebar for the documentation viewer", - "panels": ["ui", "preview"], - "autorun": true -} From 9dcc528a5a2bbb9f4a9f3e309a99ab60ccb32ec1 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Fri, 4 Aug 2023 22:33:24 +0530 Subject: [PATCH 06/14] Documentation Viewer: minor change --- src/DocumentationViewer.blp | 42 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index 5800dba50..504cebeec 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -49,7 +49,6 @@ Window documentation_viewer { orientation: vertical; vexpand: true; - SearchBar search_bar { key-capture-widget: sidebar; SearchEntry search_entry { @@ -57,30 +56,29 @@ Window documentation_viewer { } } - ScrolledWindow { - vexpand: true; - width-request: 400; + ScrolledWindow { + vexpand: true; + width-request: 400; - ListView list_view { - enable-rubberband: true; - factory: BuilderListItemFactory { - template ListItem { - child: TreeExpander expander { - list-row: bind template.item; - child: - Inscription { - hexpand: true; - nat-chars: 10; - text-overflow: ellipsize_end; - text: bind expander.item as <$DocumentationPage>.name; + ListView list_view { + enable-rubberband: true; + factory: BuilderListItemFactory { + template ListItem { + child: TreeExpander expander { + list-row: bind template.item; + child: + Inscription { + hexpand: true; + nat-chars: 10; + text-overflow: ellipsize_end; + text: bind expander.item as <$DocumentationPage>.name; + }; }; - }; - } - }; - styles ["navigation-sidebar"] + } + }; + styles ["navigation-sidebar"] + } } - } - styles ["background"] } } } From b325cb0bde24a31c0709eb5c9f06576f3578f5e5 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Fri, 4 Aug 2023 23:21:37 +0530 Subject: [PATCH 07/14] Documentation Viewer: More formatting --- src/DocumentationViewer.blp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index 504cebeec..dd7fb7cca 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -63,22 +63,23 @@ Window documentation_viewer { ListView list_view { enable-rubberband: true; factory: BuilderListItemFactory { - template ListItem { - child: TreeExpander expander { - list-row: bind template.item; - child: - Inscription { - hexpand: true; - nat-chars: 10; - text-overflow: ellipsize_end; - text: bind expander.item as <$DocumentationPage>.name; - }; + + template ListItem { + child: TreeExpander expander { + list-row: bind template.item; + child: + Inscription { + hexpand: true; + nat-chars: 10; + text-overflow: ellipsize_end; + text: bind expander.item as <$DocumentationPage>.name; }; - } - }; - styles ["navigation-sidebar"] - } + }; + } + }; + styles ["navigation-sidebar"] } + } } } } From 1790dc0cd868bac52cc3e4e371cc14837dc43fa2 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Sat, 5 Aug 2023 13:35:11 +0530 Subject: [PATCH 08/14] Documentation Viewer: Minor changes --- src/DocumentationViewer.blp | 3 +-- src/DocumentationViewer.js | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index dd7fb7cca..35ffe1ce9 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -67,8 +67,7 @@ Window documentation_viewer { template ListItem { child: TreeExpander expander { list-row: bind template.item; - child: - Inscription { + child: Inscription { hexpand: true; nat-chars: 10; text-overflow: ellipsize_end; diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index ac734f39a..511ec327d 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -144,6 +144,7 @@ function createSections(docs, dir) { alias: ["Aliases", "#aliases"], enum: ["Enumerations", "#enums"], flags: ["Bitfields", "#bitfields"], + func: ["Functions", "#functions"], error: ["Error Domains", "#domains"], callback: ["Callbacks", "#callbacks"], const: ["Constants", "#constants"], From 84f2fcf27c452865a08cdf43fb8f817594ae502f Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Mon, 14 Aug 2023 02:59:52 +0530 Subject: [PATCH 09/14] Documentation Viewer: Port to GNOME 45 --- .vscode/settings.json | 5 +- src/DocumentationViewer.blp | 123 ++++++++++++++++-------------------- src/workbench | 3 +- 3 files changed, 61 insertions(+), 70 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4e7f58a5f..f20804c4c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,8 +20,9 @@ "[json]": { "editor.defaultFormatter": "rome.rome" }, - "vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh", "mesonbuild.configureOnOpen": false, "mesonbuild.buildFolder": "_build", - "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh" + "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh", + "vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh", + "C_Cpp.default.compileCommands": "_build/compile_commands.json" } diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index 35ffe1ce9..1d0e34ca9 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -3,82 +3,71 @@ using Adw 1; using GObject 2.0; using WebKit 6.0; -Window documentation_viewer { - title: _("Documentation"); +Adw.Window documentation_viewer { + width-request: 400; + height-request: 400; default-width: 1024; default-height: 768; hide-on-close: true; - titlebar: Adw.HeaderBar { - Button button_back{ - icon-name: "go-previous-symbolic"; - tooltip-text: _("Back"); - styles ["flat"] - } - Button button_forward { - icon-name: "go-next-symbolic"; - tooltip-text: _("Forward"); - styles ["flat"] - } - ToggleButton button_sidebar { - active: false; - icon-name: "dock-left-symbolic"; - tooltip-text: _("Toggle Sidepanel"); - } - ToggleButton button_search { - icon-name: "loupe-large-symbolic"; - tooltip-text: _("Search"); - } - }; + content: Adw.NavigationSplitView split_view{ + sidebar: Adw.NavigationPage { + child: Adw.ToolbarView { + [top] + Adw.HeaderBar { + Button button_back { + icon-name: "go-previous-symbolic"; + tooltip-text: _("Back"); + styles ["flat"] + } + Button button_forward { + icon-name: "go-next-symbolic"; + tooltip-text: _("Forward"); + styles ["flat"] + } + } - Paned documentation_paned { - position: 400; + content: ScrolledWindow { + vexpand: true; + width-request: 400; - [end] - WebKit.WebView webview { - settings: WebKit.Settings { - enable-back-forward-navigation-gestures: true; - enable-developer-extras: true; - enable-smooth-scrolling: true; - }; - } + ListView list_view { + enable-rubberband: true; + factory: BuilderListItemFactory { - [start] - Box sidebar { - visible: bind button_sidebar.active; - orientation: vertical; - vexpand: true; + template ListItem { + child: TreeExpander expander { + list-row: bind template.item; + child: Inscription { + hexpand: true; + nat-chars: 10; + text-overflow: ellipsize_end; + text: bind expander.item as <$DocumentationPage>.name; + }; + }; + } + }; + styles ["navigation-sidebar"] + } + }; + }; + }; - SearchBar search_bar { - key-capture-widget: sidebar; - SearchEntry search_entry { - hexpand: true; + content: Adw.NavigationPage { + child: Adw.ToolbarView { + [top] + Adw.HeaderBar { + show-title: false; } - } - - ScrolledWindow { - vexpand: true; - width-request: 400; - ListView list_view { - enable-rubberband: true; - factory: BuilderListItemFactory { - - template ListItem { - child: TreeExpander expander { - list-row: bind template.item; - child: Inscription { - hexpand: true; - nat-chars: 10; - text-overflow: ellipsize_end; - text: bind expander.item as <$DocumentationPage>.name; - }; - }; - } + content: WebKit.WebView webview { + settings: WebKit.Settings { + enable-back-forward-navigation-gestures: true; + enable-developer-extras: true; + enable-smooth-scrolling: true; }; - styles ["navigation-sidebar"] - } - } - } - } + }; + }; + }; + }; } diff --git a/src/workbench b/src/workbench index 475a2c1c1..73f475a29 100755 --- a/src/workbench +++ b/src/workbench @@ -1,4 +1,5 @@ #!/bin/sh -SHELL=/bin/sh script --flush --quiet --return /var/tmp/workbench --command "@app_id@ $@" + +SHELL=/bin/sh script --flush --quiet --return /var/tmp/workbench --command "WEBKIT_DISABLE_DMABUF_RENDERER=1 @app_id@ $@" # SHELL=/bin/sh script --flush --quiet --return /var/tmp/workbench --command "G_MESSAGES_DEBUG=\"@app_id@\" @app_id@ $@" From 70483db7b4a60332b0ebcd1477423ee24a456e13 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Mon, 14 Aug 2023 23:32:25 +0530 Subject: [PATCH 10/14] Documentation Viewer: Minor changes --- src/DocumentationViewer.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index 511ec327d..178fb0b07 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -2,9 +2,7 @@ import GObject from "gi://GObject"; import Gtk from "gi://Gtk"; import Gio from "gi://Gio"; import GLib from "gi://GLib"; -import Adw from "gi://Adw"; import WebKit from "gi://WebKit"; - import { decode } from "./util.js"; import resource from "./DocumentationViewer.blp"; @@ -163,8 +161,6 @@ function createSections(docs, dir) { vfunc: ["Virtual Methods", "#virtual-methods"], }; - // Contains all items from the namespace that need a subsection - // A subsection is used to show an item's methods, properties, signals etc const subsections = {}; // List of sections that need subsections const subsections_required = ["class", "iface", "struct", "error"]; @@ -172,7 +168,7 @@ function createSections(docs, dir) { for (const doc of docs) { const split_name = doc.split("."); // If file is of the form xx.xx.html for example class.Button.html - if (split_name.length == 3 && sections[split_name[0]]) { + if (split_name.length === 3 && sections[split_name[0]]) { const doc_page = new DocumentationPage({ name: split_name[1], uri: dir.get_child(doc).get_uri(), @@ -191,11 +187,10 @@ function createSections(docs, dir) { } } - // Iterate through all the files again to add items into the subsection "buckets" for (const doc of docs) { const split_name = doc.split("."); // File is of the form xx.xx.xx.html for example ctor.Button.new.html - if (split_name.length == 4 && subsections[split_name[1]]) { + if (split_name.length === 4 && subsections[split_name[1]]) { const doc_page = new DocumentationPage({ name: split_name[2], uri: dir.get_child(doc).get_uri(), @@ -259,7 +254,7 @@ function newListStore() { async function getNamespaces(base_path, filter_docs) { const namespaces = []; const dirs = await list(base_path); - const filtered = dirs.filter(dir => !(filter_docs.includes(dir))) + const filtered = dirs.filter((dir) => !filter_docs.includes(dir)); for (const dir of filtered) { const results = await Promise.allSettled([ @@ -337,4 +332,3 @@ async function enableDocSidebar(webview) { } } } - From 9efbe4dc5c8ee2acd7de06cd3f839d63a78d95a1 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Tue, 15 Aug 2023 01:09:42 +0530 Subject: [PATCH 11/14] Revert accidental change --- .vscode/settings.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f20804c4c..4e7f58a5f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,9 +20,8 @@ "[json]": { "editor.defaultFormatter": "rome.rome" }, + "vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh", "mesonbuild.configureOnOpen": false, "mesonbuild.buildFolder": "_build", - "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh", - "vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh", - "C_Cpp.default.compileCommands": "_build/compile_commands.json" + "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh" } From 72a342124a59bf4a7ec1fc7b8b5130032b8f3768 Mon Sep 17 00:00:00 2001 From: Sonny Piers <sonny@fastmail.net> Date: Sat, 19 Aug 2023 15:03:34 +0200 Subject: [PATCH 12/14] Manuals --- src/DocumentationViewer.blp | 30 ++++++++++++++++++------------ src/Library/Library.blp | 6 +++--- src/window.blp | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/DocumentationViewer.blp b/src/DocumentationViewer.blp index 1d0e34ca9..01fcef0d8 100644 --- a/src/DocumentationViewer.blp +++ b/src/DocumentationViewer.blp @@ -9,22 +9,16 @@ Adw.Window documentation_viewer { default-width: 1024; default-height: 768; hide-on-close: true; + title: _("Manuals"); - content: Adw.NavigationSplitView split_view{ + content: Adw.NavigationSplitView split_view { sidebar: Adw.NavigationPage { child: Adw.ToolbarView { [top] Adw.HeaderBar { - Button button_back { - icon-name: "go-previous-symbolic"; - tooltip-text: _("Back"); - styles ["flat"] - } - Button button_forward { - icon-name: "go-next-symbolic"; - tooltip-text: _("Forward"); - styles ["flat"] - } + title-widget: Adw.WindowTitle { + title: bind documentation_viewer.title; + }; } content: ScrolledWindow { @@ -57,7 +51,19 @@ Adw.Window documentation_viewer { child: Adw.ToolbarView { [top] Adw.HeaderBar { - show-title: false; + //show-title: false; + [start] + Button button_back { + icon-name: "go-previous-symbolic"; + tooltip-text: _("Back"); + styles ["flat"] + } + [start] + Button button_forward { + icon-name: "go-next-symbolic"; + tooltip-text: _("Forward"); + styles ["flat"] + } } content: WebKit.WebView webview { diff --git a/src/Library/Library.blp b/src/Library/Library.blp index 901a982e9..f2edafbcd 100644 --- a/src/Library/Library.blp +++ b/src/Library/Library.blp @@ -11,7 +11,7 @@ Adw.PreferencesWindow library { Adw.PreferencesPage { Adw.PreferencesGroup { Label { - label: _('Learn, Test, Remix'); + label: _("Learn, Test, Remix"); styles ["title-1"] } } @@ -44,11 +44,11 @@ Adw.PreferencesWindow library { } Adw.PreferencesGroup library_user_interface { - title: "User Interface"; + title: _("User Interface"); } Adw.PreferencesGroup library_platform { - title: "Platform APIs"; + title: _("Platform APIs"); } Adw.PreferencesGroup { diff --git a/src/window.blp b/src/window.blp index 79abf8b48..da6ba6be2 100644 --- a/src/window.blp +++ b/src/window.blp @@ -549,7 +549,7 @@ menu menu_app { } item { - label: _("Documentation"); + label: _("Manuals"); action: "app.documentation"; } From 8712d1fe6ccf4be1b681b9a0e7bff2ba8d8781c0 Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Sun, 20 Aug 2023 10:28:01 +0530 Subject: [PATCH 13/14] Documentation Viewer: Remove empty sections from sidebar --- src/DocumentationViewer.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index 178fb0b07..5728fc2b7 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -172,7 +172,7 @@ function createSections(docs, dir) { const doc_page = new DocumentationPage({ name: split_name[1], uri: dir.get_child(doc).get_uri(), - // children is set to a non-null value later if it needs subsections + // children is set to a non-null value later if it nif (subsections[name][subsection].get_n_items() > 0)eeds subsections children: null, }); @@ -210,13 +210,15 @@ function createSections(docs, dir) { const sections_model = newListStore(); for (const section in sections) { - sections_model.append( - new DocumentationPage({ - name: section_name_uri[section][0], - uri: `${index_html}${section_name_uri[section][1]}`, - children: sections[section], - }), - ); + // If the ListStore is empty then dont create a section for it + if (sections[section].get_n_items() > 0) + sections_model.append( + new DocumentationPage({ + name: section_name_uri[section][0], + uri: `${index_html}${section_name_uri[section][1]}`, + children: sections[section], + }), + ); } return sections_model; } From b0bd16373b08394ef6a255f3450fd98ed0060d4e Mon Sep 17 00:00:00 2001 From: AkshayWarrier <aksmen121@gmail.com> Date: Sun, 20 Aug 2023 10:30:13 +0530 Subject: [PATCH 14/14] Documentation Viewer: Minor change --- src/DocumentationViewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DocumentationViewer.js b/src/DocumentationViewer.js index 5728fc2b7..602165a8a 100644 --- a/src/DocumentationViewer.js +++ b/src/DocumentationViewer.js @@ -172,7 +172,7 @@ function createSections(docs, dir) { const doc_page = new DocumentationPage({ name: split_name[1], uri: dir.get_child(doc).get_uri(), - // children is set to a non-null value later if it nif (subsections[name][subsection].get_n_items() > 0)eeds subsections + // children is set to a non-null value later if it needs subsections children: null, });