From 6d523817215f903d4bb9e5926619c59196c421c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Tue, 1 Dec 2020 20:09:58 +0100 Subject: [PATCH 01/11] implementation of config tree traversal --- jack/jackdbus.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++- jack/test_cfg.js | 32 +++++----------------------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/jack/jackdbus.js b/jack/jackdbus.js index e98b29b..80b630f 100644 --- a/jack/jackdbus.js +++ b/jack/jackdbus.js @@ -1,4 +1,4 @@ -const {Gio} = imports.gi; +const {Gio, GObject} = imports.gi; /* exported JackControl, JackConfigure, JackPatchbay, JackSession, JackTransport */ @@ -270,6 +270,59 @@ var JackConfigure = class JackConfigure extends _JackdbusProxyConfigure { 'org.jackaudio.service', '/org/jackaudio/Controller' ); + + // stupid Gio.DBusProxy doesn't make this an actual class so we can't use regular class methods + this._fullConfigurationTree = () => { + const _traverseObjReducer = (acc, curr) => acc ? acc[curr] : undefined; + + const pathStack = [[]], + tree = {}; + try { + // basically a DFS through the configuration tree + // probably quite heavy so maybe don't run this too often + while (pathStack.length > 0) { + const path = pathStack.pop(); + const [isLeaf, nodes] = this.ReadContainerSync(path); + const currentNode = path.reduce(_traverseObjReducer, tree); + if (!isLeaf) { + nodes.forEach(node => { + const newPath = path.concat(node); + // log(`push ${JSON.stringify(newPath)}`); + pathStack.push(newPath); + currentNode[node] = {}; + }); + } else { + nodes.forEach(node => { + const newPath = path.concat(node); + // TODO refactor this please + // TODO use real values instead of VariantType.print() + const [paramInfo] = this.GetParameterInfoSync(newPath); + const paramValue = this.GetParameterValueSync(newPath); + const paramConstraint = this.GetParameterConstraintSync(newPath); + + currentNode[node] = { + name: paramInfo[1], + desc: paramInfo[2], + default: paramValue[2].print(true), + value: paramValue[2].print(true), + constraint: { + isRange: paramConstraint[0], + isStrict: paramConstraint[1], + isFakeValue: paramConstraint[2], + values: paramConstraint[3].map(v => ({ + key: v[0].print(true), + name: v[1], + })), + }, + }; + }); + } + } + } catch (e) { + logError(e, 'gsjackctl JackConfigure.fullConfigurationTree'); + } + return tree; + }; } }; diff --git a/jack/test_cfg.js b/jack/test_cfg.js index 5bbc143..8321523 100644 --- a/jack/test_cfg.js +++ b/jack/test_cfg.js @@ -1,36 +1,14 @@ // gjs -I .. test_cfg.js +// prints the full configuration tree as json. It's recommended to be run through a json pretty-printer, like +// gjs -I .. test_cfg.js | jsonpp + const {JackConfigure} = imports.jack.jackdbus; -const traverseObjReducer = (acc, curr) => acc ? acc[curr] : undefined; try { - print('JackConfigure:'); const jackcfg = new JackConfigure(); - const pathStack = [[]], - root = {}; - while (pathStack.length > 0) { - const path = pathStack.pop(); - const [isLeaf, nodes] = jackcfg.ReadContainerSync(path); - const currentNode = path.reduce(traverseObjReducer, root); - if (!isLeaf) { - nodes.forEach(node => { - const newPath = path.concat(node); - pathStack.push(newPath); - currentNode[node] = {}; - }); - } else { - nodes.forEach(node => { - const newPath = path.concat(node); - const nodeValue = jackcfg.GetParameterValueSync(newPath); - if (nodeValue[0]) { // isSet - currentNode[node] = nodeValue[2]; // value - } else { - currentNode[node] = nodeValue[1]; // default - } - }); - } - } - print(JSON.stringify(root)); + const configtree = jackcfg._fullConfigurationTree(); + print(JSON.stringify(configtree)); } catch (e) { print(`Error: ${e}`); From c65d230b41ff86b2d8a1df4772c4c4c4d9670bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Tue, 1 Dec 2020 20:33:57 +0100 Subject: [PATCH 02/11] remove GObject import --- jack/jackdbus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jack/jackdbus.js b/jack/jackdbus.js index 80b630f..d0d0057 100644 --- a/jack/jackdbus.js +++ b/jack/jackdbus.js @@ -1,4 +1,4 @@ -const {Gio, GObject} = imports.gi; +const {Gio} = imports.gi; /* exported JackControl, JackConfigure, JackPatchbay, JackSession, JackTransport */ From 29601b443b525274d4b81fe4c8c709be6242a46c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Wed, 23 Dec 2020 01:47:01 +0100 Subject: [PATCH 03/11] example prefs window --- gsjackctl/extension.js | 8 +++ prefs.js | 123 +++++++++++++++++++++++++++++++++++++++++ test/test_cfg.js | 1 - 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 prefs.js mode change 100644 => 100755 test/test_cfg.js diff --git a/gsjackctl/extension.js b/gsjackctl/extension.js index 6350c93..4694254 100644 --- a/gsjackctl/extension.js +++ b/gsjackctl/extension.js @@ -11,6 +11,8 @@ const Local = imports.ui.main.extensionManager.lookup('gsjackctl@cbix.de'); const {GLib, GObject} = imports.gi; const Main = imports.ui.main; +const ExtensionUtils = imports.misc.extensionUtils; +const PopupMenu = imports.ui.popupMenu; const {Indicator} = Local.imports.gsjackctl.indicator; const {Status} = Local.imports.gsjackctl.status; @@ -40,6 +42,7 @@ var Extension = class Extension { this._indicator = new Indicator(); this._status = new Status(); this._control = new Control(); + this._prefs = new PopupMenu.PopupMenuItem('Settings'); this._indicator.menu.addMenuItem(this._status); try { this._a2jControl = new A2jControl(); @@ -48,6 +51,7 @@ var Extension = class Extension { this._a2jControl = false; log('gsjackctl: a2jmidid not available'); } + this._indicator.menu.addMenuItem(this._prefs); this._indicator.menu.addMenuItem(this._control); Main.panel.addToStatusArea(this._uuid, this._indicator); @@ -87,6 +91,10 @@ var Extension = class Extension { } }); + this._prefs.connect('activate', () => { + ExtensionUtils.openPrefs(); + }); + this._control.connect('start-jack', () => { try { this._jackctl.StartServerSync(); diff --git a/prefs.js b/prefs.js new file mode 100644 index 0000000..ca5c039 --- /dev/null +++ b/prefs.js @@ -0,0 +1,123 @@ +'use strict'; + +imports.gi.versions.Gtk = '3.0'; + +const {GLib, GObject, Gtk} = imports.gi; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = ExtensionUtils.getCurrentExtension(); + + +// Like `extension.js` this is used for any one-time setup like translations. +function init() { + log(`initializing ${Me.metadata.name} Preferences`); +} + +var PrefsWidget = GObject.registerClass( +class PrefsWidget extends Gtk.Grid { + _init(params) { + super._init(params); + + log('building prefs widget...'); + + // example from https://developer.gnome.org/gnome-devel-demos/stable/treeview_simple_liststore.js.html.en + + // Create the underlying liststore for the phonebook + this._listStore = new Gtk.ListStore(); + this._listStore.set_column_types([ + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + ]); + + // Data to go in the phonebook + this._phonebook = + [{name: 'Jurg', surname: 'Billeter', phone: '555-0123', + description: 'A friendly person.'}, + {name: 'Johannes', surname: 'Schmid', phone: '555-1234', + description: 'Easy phone number to remember.'}, + {name: 'Julita', surname: 'Inca', phone: '555-2345', + description: 'Another friendly person.'}, + {name: 'Javier', surname: 'Jardon', phone: '555-3456', + description: 'Bring fish for his penguins.'}, + {name: 'Jason', surname: 'Clinton', phone: '555-4567', + description: "His cake's not a lie."}, + {name: 'Random J.', surname: 'Hacker', phone: '555-5678', + description: 'Very random!'}]; + + log(`_phonebook = ${JSON.stringify(this._phonebook)}`); + + // Put the data in the phonebook + for (let i = 0; i < this._phonebook.length; i++) { + const contact = this._phonebook[i]; + this._listStore.set(this._listStore.append(), [0, 1, 2, 3], + [contact.name, contact.surname, contact.phone, contact.description]); + } + + log(`_listStore = ${JSON.stringify(this._listStore)}`); + + // Create the treeview + this._treeView = new Gtk.TreeView({expand: true, + model: this._listStore}); + + log(`_treeView = ${JSON.stringify(this._treeView)}`); + + // Create the columns for the address book + this._firstName = new Gtk.TreeViewColumn({title: 'First Name'}); + this._lastName = new Gtk.TreeViewColumn({title: 'Last Name'}); + this._phone = new Gtk.TreeViewColumn({title: 'Phone Number'}); + + // Create a cell renderer for when bold text is needed + // const bold = new Gtk.CellRendererText({weight: Pango.Weight.BOLD}); + + // Create a cell renderer for normal text + this._normal = new Gtk.CellRendererText(); + + // Pack the cell renderers into the columns + this._firstName.pack_start(this._normal, true); + this._lastName.pack_start(this._normal, true); + this._phone.pack_start(this._normal, true); + + // Set each column to pull text from the TreeView's model + this._firstName.add_attribute(this._normal, 'text', 0); + this._lastName.add_attribute(this._normal, 'text', 1); + this._phone.add_attribute(this._normal, 'text', 2); + + // Insert the columns into the treeview + this._treeView.insert_column(this._firstName, 0); + this._treeView.insert_column(this._lastName, 1); + this._treeView.insert_column(this._phone, 2); + + // Create the label that shows details for the name you select + this._label = new Gtk.Label({label: ''}); + + // Get which item is selected + this._selection = this._treeView.get_selection(); + + // When something new is selected, call _on_changed + this._selection.connect('changed', () => { + const [, , iter] = this._selection.get_selected(); + + // Set the label to read off the values stored in the current selection + this._label.set_label('\n' + + this._listStore.get_value(iter, 0) + ' ' + + this._listStore.get_value(iter, 1) + ' ' + + this._listStore.get_value(iter, 2) + '\n' + + this._listStore.get_value(iter, 3) + ); + + }); + this.attach(this._treeView, 0, 0, 1, 1); + this.attach(this._label, 0, 1, 1, 1); + } +} +); + +function buildPrefsWidget() { + const widget = new PrefsWidget(); + widget.show_all(); + return widget; +} + +// vim: set sw=4 ts=4 : diff --git a/test/test_cfg.js b/test/test_cfg.js old mode 100644 new mode 100755 index 34967b0..cd1515d --- a/test/test_cfg.js +++ b/test/test_cfg.js @@ -10,7 +10,6 @@ try { const jackcfg = new JackConfigure(); const configtree = jackcfg._fullConfigurationTree(); print(JSON.stringify(configtree)); - } catch (e) { print(`Error: ${e}`); } From 5a739a016d229935b0692b1570369906929c0b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Wed, 23 Dec 2020 15:26:32 +0100 Subject: [PATCH 04/11] remove import dependency --- prefs.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prefs.js b/prefs.js index ca5c039..0e3b7fe 100644 --- a/prefs.js +++ b/prefs.js @@ -4,13 +4,13 @@ imports.gi.versions.Gtk = '3.0'; const {GLib, GObject, Gtk} = imports.gi; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +// const ExtensionUtils = imports.misc.extensionUtils; +// const Me = ExtensionUtils.getCurrentExtension(); // Like `extension.js` this is used for any one-time setup like translations. function init() { - log(`initializing ${Me.metadata.name} Preferences`); + log('initializing gsjackctl Preferences'); } var PrefsWidget = GObject.registerClass( From af964cba2c201abc054dc2c01b19e3b08d650e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Wed, 23 Dec 2020 15:26:48 +0100 Subject: [PATCH 05/11] TreeView test --- test/widget.js | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100755 test/widget.js diff --git a/test/widget.js b/test/widget.js new file mode 100755 index 0000000..bfc3e1b --- /dev/null +++ b/test/widget.js @@ -0,0 +1,100 @@ +#!/usr/bin/env -S gjs -I .. + +imports.gi.versions.Gtk = '3.0'; + +const {GObject, Gtk, Pango} = imports.gi; + +const COL_KEY = 0, + COL_VALUE = 1, + COL_DEFAULT = 2, + COL_DESC = 3; + +class TestWindow { + constructor() { + this.application = new Gtk.Application({ + application_id: 'de.cbix.gsjackctl.prefswindow', + }); + this.application.connect('activate', this._onActivate.bind(this)); + this.application.connect('startup', this._onStartup.bind(this)); + } + + _onActivate() { + this._window.present(); + } + + _onStartup() { + this._buildUI(); + } + + _buildUI() { + this._window = new Gtk.ApplicationWindow({ + application: this.application, + window_position: Gtk.WindowPosition.CENTER, + default_height: 250, + default_width: 100, + title: 'gsjackctl prefs window test', + }); + + this._treeStore = new Gtk.TreeStore(); + const colTypes = []; + colTypes[COL_KEY] = GObject.TYPE_STRING; + colTypes[COL_VALUE] = GObject.TYPE_VARIANT; + colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; + colTypes[COL_DESC] = GObject.TYPE_STRING; + + this._treeStore.set_column_types(colTypes); + + const itA = this._treeStore.append(null); + const itB = this._treeStore.append(null); + const itFoo = this._treeStore.append(itA); + const itBar = this._treeStore.append(itB); + const itBaz = this._treeStore.append(itBar); + this._treeStore.set(itA, [COL_KEY, COL_DESC], ['a', 'A']); + this._treeStore.set(itB, [COL_KEY, COL_DESC], ['b', 'B']); + this._treeStore.set(itFoo, [COL_KEY, COL_DESC], ['foo', 'Foo']); + this._treeStore.set(itBar, [COL_KEY, COL_DESC], ['bar', 'Bar']); + this._treeStore.set(itBaz, [COL_KEY, COL_DESC], ['baz', 'Baz']); + + this._treeView = new Gtk.TreeView({ + expand: true, + model: this._treeStore, + }); + + const colKey = new Gtk.TreeViewColumn({title: 'Key'}); + const colValue = new Gtk.TreeViewColumn({title: 'Value'}); + const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); + const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); + + const normalRenderer = new Gtk.CellRendererText(); + + colKey.pack_start(normalRenderer, true); + colDescription.pack_start(normalRenderer, true); + + colKey.add_attribute(normalRenderer, 'text', COL_KEY); + colDescription.pack_start(normalRenderer, 'text', COL_DESC); + + this._treeView.insert_column(colKey, COL_KEY); + this._treeView.insert_column(colValue, COL_VALUE); + this._treeView.insert_column(colDefault, COL_DEFAULT); + this._treeView.insert_column(colDescription, COL_DESC); + + this._window.add(this._treeView); + this._window.show_all(); + } +} + +try { + // const Prefs = imports.prefs; + try { + print('test widget window'); + + const app = new TestWindow(); + app.application.run(ARGV); + } catch (e) { + logError(e); + } +} catch (e) { + print(e.message); +} + +// vim: set sw=4 ts=4 : From 41d436aeff7d82da7a7a9309f4453915e7ac5aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Wed, 23 Dec 2020 22:35:59 +0100 Subject: [PATCH 06/11] test TreeView CellRendererToggle --- test/widget.js | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/test/widget.js b/test/widget.js index bfc3e1b..e77cd17 100755 --- a/test/widget.js +++ b/test/widget.js @@ -7,7 +7,8 @@ const {GObject, Gtk, Pango} = imports.gi; const COL_KEY = 0, COL_VALUE = 1, COL_DEFAULT = 2, - COL_DESC = 3; + COL_DESC = 3, + COL_STATE = 4; class TestWindow { constructor() { @@ -41,6 +42,7 @@ class TestWindow { colTypes[COL_VALUE] = GObject.TYPE_VARIANT; colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; colTypes[COL_DESC] = GObject.TYPE_STRING; + colTypes[COL_STATE] = GObject.TYPE_BOOLEAN; this._treeStore.set_column_types(colTypes); @@ -49,12 +51,24 @@ class TestWindow { const itFoo = this._treeStore.append(itA); const itBar = this._treeStore.append(itB); const itBaz = this._treeStore.append(itBar); + this._treeStore.set(itA, [COL_KEY, COL_DESC], ['a', 'A']); this._treeStore.set(itB, [COL_KEY, COL_DESC], ['b', 'B']); this._treeStore.set(itFoo, [COL_KEY, COL_DESC], ['foo', 'Foo']); this._treeStore.set(itBar, [COL_KEY, COL_DESC], ['bar', 'Bar']); this._treeStore.set(itBaz, [COL_KEY, COL_DESC], ['baz', 'Baz']); + this._comboStore = new Gtk.ListStore(); + this._comboStore.set_column_types([ + GObject.TYPE_STRING, + GObject.TYPE_STRING, + ]); + this._comboStore.set(this._comboStore.append(), [0, 1], ['es', 'Spain']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['pt', 'Portugal']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['de', 'Germany']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['pl', 'Poland']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['sv', 'Sweden']); + this._treeView = new Gtk.TreeView({ expand: true, model: this._treeStore, @@ -64,19 +78,41 @@ class TestWindow { const colValue = new Gtk.TreeViewColumn({title: 'Value'}); const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); + const colState = new Gtk.TreeViewColumn({ + title: 'State', + }); const normalRenderer = new Gtk.CellRendererText(); + const comboRenderer = new Gtk.CellRendererCombo({ + model: this._comboStore, + }); + const toggleRenderer = new Gtk.CellRendererToggle({ + activatable: true, + }); + toggleRenderer.connect('toggled', (rend, path) => { + const [ok, it] = this._treeStore.get_iter_from_string(path); + print(`toggled: ${rend.active}, ${ok}`); + if (ok) + this._treeStore.set(it, [COL_STATE], [!rend.active]); + + // const currentState = this._treeStore. + }); colKey.pack_start(normalRenderer, true); + colValue.pack_start(comboRenderer, true); + colDefault.pack_start(comboRenderer, true); colDescription.pack_start(normalRenderer, true); + colState.pack_start(toggleRenderer, true); colKey.add_attribute(normalRenderer, 'text', COL_KEY); - colDescription.pack_start(normalRenderer, 'text', COL_DESC); + colDescription.add_attribute(normalRenderer, 'text', COL_DESC); + colState.add_attribute(toggleRenderer, 'active', COL_STATE); this._treeView.insert_column(colKey, COL_KEY); this._treeView.insert_column(colValue, COL_VALUE); this._treeView.insert_column(colDefault, COL_DEFAULT); this._treeView.insert_column(colDescription, COL_DESC); + this._treeView.insert_column(colState, COL_STATE); this._window.add(this._treeView); this._window.show_all(); From 8fdff7f1ac855095750bc0e4366518b59c175847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Thu, 24 Dec 2020 15:52:33 +0100 Subject: [PATCH 07/11] Test TreeView widget with configuration from jackdbus: ![cfg widget](https://user-images.githubusercontent.com/1295945/103095155-ed96eb80-45ff-11eb-9f5e-a61154f90fae.png) --- test/cfg_widget.js | 193 ++++++++++++++++++++++++++++++ test/{test_cfg.js => dump_cfg.js} | 2 +- 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100755 test/cfg_widget.js rename test/{test_cfg.js => dump_cfg.js} (94%) diff --git a/test/cfg_widget.js b/test/cfg_widget.js new file mode 100755 index 0000000..ef7ba10 --- /dev/null +++ b/test/cfg_widget.js @@ -0,0 +1,193 @@ +#!/usr/bin/env -S gjs -I .. + +imports.gi.versions.Gtk = '3.0'; + +const {GObject, Gtk, Pango} = imports.gi; + +const {JackConfigure} = imports.jack.jackdbus; + +const COL_KEY = 0, + COL_VALUE = 1, + COL_DEFAULT = 2, + COL_DESC = 3, + COL_LEAF = 4, + COL_ISRANGE = 5, + COL_ISSTRICT = 6, + COL_ISFAKEVALUE = 7, + COL_VALUES = 8; + +class TestWindow { + constructor() { + this.application = new Gtk.Application({ + application_id: 'de.cbix.gsjackctl.prefswindow', + }); + this.application.connect('activate', this._onActivate.bind(this)); + this.application.connect('startup', this._onStartup.bind(this)); + } + + _onActivate() { + this._window.present(); + } + + _onStartup() { + this._buildUI(); + } + + _buildUI() { + const jackcfg = new JackConfigure(); + this._window = new Gtk.ApplicationWindow({ + application: this.application, + window_position: Gtk.WindowPosition.CENTER, + default_height: 250, + default_width: 100, + title: 'gsjackctl prefs window test', + }); + + this._treeStore = new Gtk.TreeStore(); + const colTypes = []; + colTypes[COL_KEY] = GObject.TYPE_STRING; + colTypes[COL_VALUE] = GObject.TYPE_VARIANT; + colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; + colTypes[COL_DESC] = GObject.TYPE_STRING; + colTypes[COL_LEAF] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISFAKEVALUE] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISRANGE] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISSTRICT] = GObject.TYPE_BOOLEAN; + colTypes[COL_VALUES] = GObject.TYPE_VARIANT; + + this._treeStore.set_column_types(colTypes); + + /* + const itA = this._treeStore.append(null); + const itB = this._treeStore.append(null); + const itFoo = this._treeStore.append(itA); + const itBar = this._treeStore.append(itB); + const itBaz = this._treeStore.append(itBar); + + this._treeStore.set(itA, [COL_KEY, COL_DESC, COL_LEAF], ['a', 'A', false]); + this._treeStore.set(itB, [COL_KEY, COL_DESC, COL_LEAF], ['b', 'B', false]); + this._treeStore.set(itFoo, [COL_KEY, COL_DESC, COL_LEAF], ['foo', 'Foo', true]); + this._treeStore.set(itBar, [COL_KEY, COL_DESC, COL_LEAF], ['bar', 'Bar', false]); + this._treeStore.set(itBaz, [COL_KEY, COL_DESC, COL_LEAF], ['baz', 'Baz', true]); + */ + + // adaptation of _fullConfigurationTree() traversing for TreeStore + const pathStack = [[]], + iterStack = [null]; + while (pathStack.length > 0) { + const path = pathStack.pop(); + const iter = iterStack.pop() || null; + const [isLeaf, nodes] = jackcfg.ReadContainerSync(path); + + // set leaf flag in store, except for root node + if (iter) + this._treeStore.set(iter, [COL_LEAF], [isLeaf]); + + if (!isLeaf) { + nodes.forEach(node => { + const newPath = path.concat(node); + pathStack.push(newPath); + const newIter = this._treeStore.append(iter); + iterStack.push(newIter); + this._treeStore.set(newIter, [COL_KEY], [node]); + }); + } else { + nodes.forEach(node => { + const newPath = path.concat(node); + const [paramInfo] = jackcfg.GetParameterInfoSync(newPath); + // const paramValue = jackcfg.GetParameterValueSync(newPath); + const paramConstraint = jackcfg.GetParameterConstraintSync(newPath); + const newIter = this._treeStore.append(iter); + this._treeStore.set(newIter, [ + COL_KEY, + COL_DESC, + COL_ISRANGE, + COL_ISRANGE, + COL_ISFAKEVALUE, + ], [ + paramInfo[1], + paramInfo[2], + paramConstraint[0], + paramConstraint[1], + paramConstraint[2], + ]); + }); + } + } + this._comboStore = new Gtk.ListStore(); + this._comboStore.set_column_types([ + GObject.TYPE_STRING, + GObject.TYPE_STRING, + ]); + this._comboStore.set(this._comboStore.append(), [0, 1], ['es', 'Spain']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['pt', 'Portugal']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['de', 'Germany']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['pl', 'Poland']); + this._comboStore.set(this._comboStore.append(), [0, 1], ['sv', 'Sweden']); + + this._treeView = new Gtk.TreeView({ + expand: true, + model: this._treeStore, + }); + + const colKey = new Gtk.TreeViewColumn({title: 'Key'}); + const colValue = new Gtk.TreeViewColumn({title: 'Value'}); + const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); + const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); + const colState = new Gtk.TreeViewColumn({ + title: 'State', + }); + + const normalRenderer = new Gtk.CellRendererText(); + const comboRenderer = new Gtk.CellRendererCombo({ + model: this._comboStore, + }); + const toggleRenderer = new Gtk.CellRendererToggle({ + activatable: true, + }); + toggleRenderer.connect('toggled', (rend, path) => { + const [ok, it] = this._treeStore.get_iter_from_string(path); + print(`toggled: ${rend.active}, ${ok}`); + if (ok) + // test + this._treeStore.set(it, [COL_ISSTRICT], [!rend.active]); + + // const currentState = this._treeStore. + }); + + colKey.pack_start(normalRenderer, true); + colValue.pack_start(comboRenderer, true); + colDefault.pack_start(comboRenderer, true); + colDescription.pack_start(normalRenderer, true); + colState.pack_start(toggleRenderer, true); + + colKey.add_attribute(normalRenderer, 'text', COL_KEY); + colDescription.add_attribute(normalRenderer, 'text', COL_DESC); + // TESTING + // colState.add_attribute(toggleRenderer, 'active', COL_ISSTRICT); + // colState.add_attribute(toggleRenderer, 'visible', COL_LEAF); + + this._treeView.insert_column(colKey, 0); + this._treeView.insert_column(colDescription, 1); + this._treeView.insert_column(colState, 2); + + this._window.add(this._treeView); + this._window.show_all(); + } +} + +try { + // const Prefs = imports.prefs; + try { + print('test widget window'); + + const app = new TestWindow(); + app.application.run(ARGV); + } catch (e) { + logError(e); + } +} catch (e) { + print(e.message); +} + +// vim: set sw=4 ts=4 : diff --git a/test/test_cfg.js b/test/dump_cfg.js similarity index 94% rename from test/test_cfg.js rename to test/dump_cfg.js index cd1515d..8562fc7 100755 --- a/test/test_cfg.js +++ b/test/dump_cfg.js @@ -1,7 +1,7 @@ #!/usr/bin/env -S gjs -I .. // prints the full configuration tree as json. It's recommended to be run through a json pretty-printer, like -// ./test_cfg.js | jsonpp +// ./dump_cfg.js | jsonpp try { const {JackConfigure} = imports.jack.jackdbus; From bcda742bac63387739448cb746d033dd6ba9a0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Thu, 24 Dec 2020 23:38:21 +0100 Subject: [PATCH 08/11] display config in prefs window --- prefs.js | 192 +++++++++++++++++++++++++-------------------- test/cfg_widget.js | 14 ---- 2 files changed, 107 insertions(+), 99 deletions(-) diff --git a/prefs.js b/prefs.js index 0e3b7fe..d2468d7 100644 --- a/prefs.js +++ b/prefs.js @@ -4,8 +4,20 @@ imports.gi.versions.Gtk = '3.0'; const {GLib, GObject, Gtk} = imports.gi; -// const ExtensionUtils = imports.misc.extensionUtils; -// const Me = ExtensionUtils.getCurrentExtension(); +const ExtensionUtils = imports.misc.extensionUtils; +const Local = ExtensionUtils.getCurrentExtension(); + +const {JackConfigure} = Local.imports.jack.jackdbus; + +const COL_KEY = 0, + COL_VALUE = 1, + COL_DEFAULT = 2, + COL_DESC = 3, + COL_LEAF = 4, + COL_ISRANGE = 5, + COL_ISSTRICT = 6, + COL_ISFAKEVALUE = 7, + COL_VALUES = 8; // Like `extension.js` this is used for any one-time setup like translations. @@ -14,102 +26,112 @@ function init() { } var PrefsWidget = GObject.registerClass( -class PrefsWidget extends Gtk.Grid { +class PrefsWidget extends Gtk.ScrolledWindow { _init(params) { super._init(params); + this._jackcfg = new JackConfigure(); + log('building prefs widget...'); - // example from https://developer.gnome.org/gnome-devel-demos/stable/treeview_simple_liststore.js.html.en - - // Create the underlying liststore for the phonebook - this._listStore = new Gtk.ListStore(); - this._listStore.set_column_types([ - GObject.TYPE_STRING, - GObject.TYPE_STRING, - GObject.TYPE_STRING, - GObject.TYPE_STRING, - ]); - - // Data to go in the phonebook - this._phonebook = - [{name: 'Jurg', surname: 'Billeter', phone: '555-0123', - description: 'A friendly person.'}, - {name: 'Johannes', surname: 'Schmid', phone: '555-1234', - description: 'Easy phone number to remember.'}, - {name: 'Julita', surname: 'Inca', phone: '555-2345', - description: 'Another friendly person.'}, - {name: 'Javier', surname: 'Jardon', phone: '555-3456', - description: 'Bring fish for his penguins.'}, - {name: 'Jason', surname: 'Clinton', phone: '555-4567', - description: "His cake's not a lie."}, - {name: 'Random J.', surname: 'Hacker', phone: '555-5678', - description: 'Very random!'}]; - - log(`_phonebook = ${JSON.stringify(this._phonebook)}`); - - // Put the data in the phonebook - for (let i = 0; i < this._phonebook.length; i++) { - const contact = this._phonebook[i]; - this._listStore.set(this._listStore.append(), [0, 1, 2, 3], - [contact.name, contact.surname, contact.phone, contact.description]); + this._treeStore = new Gtk.TreeStore(); + const colTypes = []; + colTypes[COL_KEY] = GObject.TYPE_STRING; + colTypes[COL_VALUE] = GObject.TYPE_VARIANT; + colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; + colTypes[COL_DESC] = GObject.TYPE_STRING; + colTypes[COL_LEAF] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISFAKEVALUE] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISRANGE] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISSTRICT] = GObject.TYPE_BOOLEAN; + colTypes[COL_VALUES] = GObject.TYPE_VARIANT; + + this._treeStore.set_column_types(colTypes); + + // adaptation of _fullConfigurationTree() traversing for TreeStore + const pathStack = [[]], + iterStack = [null]; + while (pathStack.length > 0) { + const path = pathStack.pop(); + const iter = iterStack.pop() || null; + const [isLeaf, nodes] = this._jackcfg.ReadContainerSync(path); + + // set leaf flag in store, except for root node + if (iter) + this._treeStore.set(iter, [COL_LEAF], [isLeaf]); + + if (!isLeaf) { + nodes.forEach(node => { + const newPath = path.concat(node); + pathStack.push(newPath); + const newIter = this._treeStore.append(iter); + iterStack.push(newIter); + this._treeStore.set(newIter, [COL_KEY], [node]); + }); + } else { + nodes.forEach(node => { + const newPath = path.concat(node); + const [paramInfo] = this._jackcfg.GetParameterInfoSync(newPath); + // const paramValue = this._jackcfg.GetParameterValueSync(newPath); + const paramConstraint = this._jackcfg.GetParameterConstraintSync(newPath); + const newIter = this._treeStore.append(iter); + this._treeStore.set(newIter, [ + COL_KEY, + COL_DESC, + COL_ISRANGE, + COL_ISRANGE, + COL_ISFAKEVALUE, + ], [ + paramInfo[1], + paramInfo[2], + paramConstraint[0], + paramConstraint[1], + paramConstraint[2], + ]); + }); + } } - log(`_listStore = ${JSON.stringify(this._listStore)}`); - - // Create the treeview - this._treeView = new Gtk.TreeView({expand: true, - model: this._listStore}); - - log(`_treeView = ${JSON.stringify(this._treeView)}`); - - // Create the columns for the address book - this._firstName = new Gtk.TreeViewColumn({title: 'First Name'}); - this._lastName = new Gtk.TreeViewColumn({title: 'Last Name'}); - this._phone = new Gtk.TreeViewColumn({title: 'Phone Number'}); - - // Create a cell renderer for when bold text is needed - // const bold = new Gtk.CellRendererText({weight: Pango.Weight.BOLD}); - - // Create a cell renderer for normal text - this._normal = new Gtk.CellRendererText(); - - // Pack the cell renderers into the columns - this._firstName.pack_start(this._normal, true); - this._lastName.pack_start(this._normal, true); - this._phone.pack_start(this._normal, true); - - // Set each column to pull text from the TreeView's model - this._firstName.add_attribute(this._normal, 'text', 0); - this._lastName.add_attribute(this._normal, 'text', 1); - this._phone.add_attribute(this._normal, 'text', 2); + this._treeView = new Gtk.TreeView({ + expand: true, + model: this._treeStore, + }); - // Insert the columns into the treeview - this._treeView.insert_column(this._firstName, 0); - this._treeView.insert_column(this._lastName, 1); - this._treeView.insert_column(this._phone, 2); + const colKey = new Gtk.TreeViewColumn({title: 'Key'}); + // const colValue = new Gtk.TreeViewColumn({title: 'Value'}); + // const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); + const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); + const colState = new Gtk.TreeViewColumn({ + title: 'State', + }); - // Create the label that shows details for the name you select - this._label = new Gtk.Label({label: ''}); + const normalRenderer = new Gtk.CellRendererText(); + const toggleRenderer = new Gtk.CellRendererToggle({ + activatable: true, + }); + toggleRenderer.connect('toggled', (rend, path) => { + const [ok, it] = this._treeStore.get_iter_from_string(path); + print(`toggled: ${rend.active}, ${ok}`); + if (ok) + // test + this._treeStore.set(it, [COL_ISSTRICT], [!rend.active]); + + // const currentState = this._treeStore. + }); - // Get which item is selected - this._selection = this._treeView.get_selection(); + colKey.pack_start(normalRenderer, true); + colDescription.pack_start(normalRenderer, true); + colState.pack_start(toggleRenderer, true); - // When something new is selected, call _on_changed - this._selection.connect('changed', () => { - const [, , iter] = this._selection.get_selected(); + colKey.add_attribute(normalRenderer, 'text', COL_KEY); + colDescription.add_attribute(normalRenderer, 'text', COL_DESC); - // Set the label to read off the values stored in the current selection - this._label.set_label('\n' + - this._listStore.get_value(iter, 0) + ' ' + - this._listStore.get_value(iter, 1) + ' ' + - this._listStore.get_value(iter, 2) + '\n' + - this._listStore.get_value(iter, 3) - ); + this._treeView.insert_column(colKey, 0); + this._treeView.insert_column(colDescription, 1); + this._treeView.insert_column(colState, 2); - }); - this.attach(this._treeView, 0, 0, 1, 1); - this.attach(this._label, 0, 1, 1, 1); + this.add(this._treeView); + this.show_all(); } } ); diff --git a/test/cfg_widget.js b/test/cfg_widget.js index ef7ba10..6921cff 100755 --- a/test/cfg_widget.js +++ b/test/cfg_widget.js @@ -57,20 +57,6 @@ class TestWindow { this._treeStore.set_column_types(colTypes); - /* - const itA = this._treeStore.append(null); - const itB = this._treeStore.append(null); - const itFoo = this._treeStore.append(itA); - const itBar = this._treeStore.append(itB); - const itBaz = this._treeStore.append(itBar); - - this._treeStore.set(itA, [COL_KEY, COL_DESC, COL_LEAF], ['a', 'A', false]); - this._treeStore.set(itB, [COL_KEY, COL_DESC, COL_LEAF], ['b', 'B', false]); - this._treeStore.set(itFoo, [COL_KEY, COL_DESC, COL_LEAF], ['foo', 'Foo', true]); - this._treeStore.set(itBar, [COL_KEY, COL_DESC, COL_LEAF], ['bar', 'Bar', false]); - this._treeStore.set(itBaz, [COL_KEY, COL_DESC, COL_LEAF], ['baz', 'Baz', true]); - */ - // adaptation of _fullConfigurationTree() traversing for TreeStore const pathStack = [[]], iterStack = [null]; From 13d26c1988edb67c2f566b6810712aa32e3a4f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Sat, 26 Dec 2020 19:08:41 +0100 Subject: [PATCH 09/11] show config value/default string in prefs view --- prefs.js | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/prefs.js b/prefs.js index d2468d7..4af348a 100644 --- a/prefs.js +++ b/prefs.js @@ -17,7 +17,9 @@ const COL_KEY = 0, COL_ISRANGE = 5, COL_ISSTRICT = 6, COL_ISFAKEVALUE = 7, - COL_VALUES = 8; + COL_VALUES = 8, + COL_VALUESTRING = 9, + COL_DEFAULTSTRING = 10; // Like `extension.js` this is used for any one-time setup like translations. @@ -25,6 +27,18 @@ function init() { log('initializing gsjackctl Preferences'); } +var CellRendererVariant = GObject.registerClass( +class CellRendererVariant extends Gtk.CellRenderer { + constructor(params) { + super(params); + this._textRenderer = new Gtk.CellRendererText(); + this._toggleRenderer = new Gtk.CellRendererToggle(); + this._comboRenderer = new Gtk.CellRendererCombo(); + this._spinRenderer = new Gtk.CellRendererSpin(); + } +} +); + var PrefsWidget = GObject.registerClass( class PrefsWidget extends Gtk.ScrolledWindow { _init(params) { @@ -45,6 +59,8 @@ class PrefsWidget extends Gtk.ScrolledWindow { colTypes[COL_ISRANGE] = GObject.TYPE_BOOLEAN; colTypes[COL_ISSTRICT] = GObject.TYPE_BOOLEAN; colTypes[COL_VALUES] = GObject.TYPE_VARIANT; + colTypes[COL_VALUESTRING] = GObject.TYPE_STRING; + colTypes[COL_DEFAULTSTRING] = GObject.TYPE_STRING; this._treeStore.set_column_types(colTypes); @@ -72,17 +88,21 @@ class PrefsWidget extends Gtk.ScrolledWindow { nodes.forEach(node => { const newPath = path.concat(node); const [paramInfo] = this._jackcfg.GetParameterInfoSync(newPath); - // const paramValue = this._jackcfg.GetParameterValueSync(newPath); + const paramValue = this._jackcfg.GetParameterValueSync(newPath); const paramConstraint = this._jackcfg.GetParameterConstraintSync(newPath); const newIter = this._treeStore.append(iter); this._treeStore.set(newIter, [ COL_KEY, + COL_VALUESTRING, + COL_DEFAULTSTRING, COL_DESC, COL_ISRANGE, COL_ISRANGE, COL_ISFAKEVALUE, ], [ paramInfo[1], + paramValue[2].print(true), + paramValue[1].print(true), paramInfo[2], paramConstraint[0], paramConstraint[1], @@ -98,13 +118,16 @@ class PrefsWidget extends Gtk.ScrolledWindow { }); const colKey = new Gtk.TreeViewColumn({title: 'Key'}); - // const colValue = new Gtk.TreeViewColumn({title: 'Value'}); - // const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); + const colValue = new Gtk.TreeViewColumn({title: 'Value'}); + const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); const colState = new Gtk.TreeViewColumn({ title: 'State', }); + // dynamic renderer + const variantRenderer = new CellRendererVariant(); + const normalRenderer = new Gtk.CellRendererText(); const toggleRenderer = new Gtk.CellRendererToggle({ activatable: true, @@ -120,15 +143,21 @@ class PrefsWidget extends Gtk.ScrolledWindow { }); colKey.pack_start(normalRenderer, true); + colValue.pack_start(normalRenderer, true); + colDefault.pack_start(normalRenderer, true); colDescription.pack_start(normalRenderer, true); colState.pack_start(toggleRenderer, true); colKey.add_attribute(normalRenderer, 'text', COL_KEY); + colValue.add_attribute(normalRenderer, 'text', COL_VALUESTRING); + colDefault.add_attribute(normalRenderer, 'text', COL_DEFAULTSTRING); colDescription.add_attribute(normalRenderer, 'text', COL_DESC); this._treeView.insert_column(colKey, 0); - this._treeView.insert_column(colDescription, 1); - this._treeView.insert_column(colState, 2); + this._treeView.insert_column(colValue, 1); + this._treeView.insert_column(colDefault, 2); + this._treeView.insert_column(colDescription, 3); + this._treeView.insert_column(colState, 4); this.add(this._treeView); this.show_all(); From 65d49babe5be7b8e4959c0e38a6a114b48931e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Sat, 26 Dec 2020 21:29:38 +0100 Subject: [PATCH 10/11] print type string --- prefs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prefs.js b/prefs.js index 4af348a..f36c0ec 100644 --- a/prefs.js +++ b/prefs.js @@ -101,8 +101,8 @@ class PrefsWidget extends Gtk.ScrolledWindow { COL_ISFAKEVALUE, ], [ paramInfo[1], - paramValue[2].print(true), - paramValue[1].print(true), + `${paramValue[2].get_type_string()}:${paramValue[2].print(false)}`, + `${paramValue[1].get_type_string()}:${paramValue[1].print(false)}`, paramInfo[2], paramConstraint[0], paramConstraint[1], From 1f0a5af3a69850926040b5dc452e194bf395669d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Sun, 3 Jan 2021 19:20:25 +0100 Subject: [PATCH 11/11] basic CellRendererVariant --- gsjackctl/prefsWidget.js | 206 +++++++++++++++++++++++++++++++++++++++ prefs.js | 156 +---------------------------- test/cfg_widget.js | 132 +------------------------ 3 files changed, 212 insertions(+), 282 deletions(-) create mode 100644 gsjackctl/prefsWidget.js diff --git a/gsjackctl/prefsWidget.js b/gsjackctl/prefsWidget.js new file mode 100644 index 0000000..40ea958 --- /dev/null +++ b/gsjackctl/prefsWidget.js @@ -0,0 +1,206 @@ +'use strict'; + +imports.gi.versions.Gtk = '3.0'; + +const {GLib, GObject, Gtk} = imports.gi; + +let JackConfigure; + +try { + // const Local = imports.ui.main.extensionManager.lookup('gsjackctl@cbix.de'); + const ExtensionUtils = imports.misc.extensionUtils; + const Local = ExtensionUtils.getCurrentExtension(); + JackConfigure = Local.imports.jack.jackdbus.JackConfigure; +} catch (e) { + logError(e, 'gsjackctl.prefsWidget'); + JackConfigure = imports.jack.jackdbus.JackConfigure; +} + +const COL_KEY = 0, + COL_VALUE = 1, + COL_DEFAULT = 2, + COL_DESC = 3, + COL_LEAF = 4, + COL_ISRANGE = 5, + COL_ISSTRICT = 6, + COL_ISFAKEVALUE = 7, + COL_VALUES = 8, + COL_VALUESTRING = 9, + COL_DEFAULTSTRING = 10; + +/** + * CellRendererVariant is a custom Gtk.CellRenderer wrapping different cell + * renderers and rendering one based on the parameter value and constraint. + * bool → CellRendererToggle + * numeric → CellRendererSpin + * constraint values → CellRendererCombo + * string → CellRendererText + */ +var CellRendererVariant = GObject.registerClass({ + GTypeName: 'CellRendererVariant', + Properties: { + 'value': GObject.param_spec_variant('value', 'Value', 'Configuration Value', new GLib.VariantType('v'), null, GObject.ParamFlags.READWRITE), + 'default': GObject.param_spec_variant('default', 'Default', 'Default Configuration Value', new GLib.VariantType('v'), null, GObject.ParamFlags.READWRITE), + }, +}, class CellRendererVariant extends Gtk.CellRenderer { + constructor(params) { + super(params); + this._textRenderer = new Gtk.CellRendererText(); + this._toggleRenderer = new Gtk.CellRendererToggle(); + this._comboRenderer = new Gtk.CellRendererCombo(); + this._spinRenderer = new Gtk.CellRendererSpin(); + } + + set value(value) { + log(`set value: ${value}`); + this._value = value; + if (!value) + return; + + log(`value type: ${value.type_string}`); + } + + set default(value) { + log(`set default: ${value}`); + this._default = value; + if (!value) + return; + + log(`default type: ${value.type_string}`); + } +} +); + +var PrefsWidget = GObject.registerClass( +class PrefsWidget extends Gtk.ScrolledWindow { + _init(params) { + super._init(params); + + this._jackcfg = new JackConfigure(); + + log('building prefs widget...'); + + this._treeStore = new Gtk.TreeStore(); + const colTypes = []; + colTypes[COL_KEY] = GObject.TYPE_STRING; + colTypes[COL_VALUE] = GObject.TYPE_VARIANT; + colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; + colTypes[COL_DESC] = GObject.TYPE_STRING; + colTypes[COL_LEAF] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISFAKEVALUE] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISRANGE] = GObject.TYPE_BOOLEAN; + colTypes[COL_ISSTRICT] = GObject.TYPE_BOOLEAN; + colTypes[COL_VALUES] = GObject.TYPE_VARIANT; + colTypes[COL_VALUESTRING] = GObject.TYPE_STRING; + colTypes[COL_DEFAULTSTRING] = GObject.TYPE_STRING; + + this._treeStore.set_column_types(colTypes); + + // adaptation of _fullConfigurationTree() traversing for TreeStore + const pathStack = [[]], + iterStack = [null]; + while (pathStack.length > 0) { + const path = pathStack.pop(); + const iter = iterStack.pop() || null; + const [isLeaf, nodes] = this._jackcfg.ReadContainerSync(path); + + // set leaf flag in store, except for root node + if (iter) + this._treeStore.set(iter, [COL_LEAF], [isLeaf]); + + if (!isLeaf) { + nodes.forEach(node => { + const newPath = path.concat(node); + pathStack.push(newPath); + const newIter = this._treeStore.append(iter); + iterStack.push(newIter); + this._treeStore.set(newIter, [COL_KEY], [node]); + }); + } else { + nodes.forEach(node => { + const newPath = path.concat(node); + const [paramInfo] = this._jackcfg.GetParameterInfoSync(newPath); + const paramValue = this._jackcfg.GetParameterValueSync(newPath); + const paramConstraint = this._jackcfg.GetParameterConstraintSync(newPath); + const newIter = this._treeStore.append(iter); + this._treeStore.set(newIter, [ + COL_KEY, + COL_VALUE, + COL_DEFAULT, + COL_VALUESTRING, + COL_DEFAULTSTRING, + COL_DESC, + COL_ISRANGE, + COL_ISRANGE, + COL_ISFAKEVALUE, + ], [ + paramInfo[1], + paramValue[2], + paramValue[1], + `${paramValue[2].get_type_string()}:${paramValue[2].print(false)}`, + `${paramValue[1].get_type_string()}:${paramValue[1].print(false)}`, + paramInfo[2], + paramConstraint[0], + paramConstraint[1], + paramConstraint[2], + ]); + }); + } + } + + this._treeView = new Gtk.TreeView({ + expand: true, + model: this._treeStore, + }); + + const colKey = new Gtk.TreeViewColumn({title: 'Key'}); + const colValue = new Gtk.TreeViewColumn({title: 'Value'}); + const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); + const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); + const colTest = new Gtk.TreeViewColumn({ + title: 'Test', + }); + + // dynamic renderer + const variantRenderer = new CellRendererVariant(); + + const normalRenderer = new Gtk.CellRendererText(); + const toggleRenderer = new Gtk.CellRendererToggle({ + activatable: true, + }); + toggleRenderer.connect('toggled', (rend, path) => { + const [ok, it] = this._treeStore.get_iter_from_string(path); + print(`toggled: ${rend.active}, ${ok}`); + if (ok) + // test + this._treeStore.set(it, [COL_ISSTRICT], [!rend.active]); + + // const currentState = this._treeStore. + }); + + colKey.pack_start(normalRenderer, true); + colValue.pack_start(normalRenderer, true); + colDefault.pack_start(normalRenderer, true); + colDescription.pack_start(normalRenderer, true); + colTest.pack_start(variantRenderer, true); + + colKey.add_attribute(normalRenderer, 'text', COL_KEY); + colValue.add_attribute(normalRenderer, 'text', COL_VALUESTRING); + colDefault.add_attribute(normalRenderer, 'text', COL_DEFAULTSTRING); + colDescription.add_attribute(normalRenderer, 'text', COL_DESC); + colTest.add_attribute(variantRenderer, 'value', COL_VALUE); + colTest.add_attribute(variantRenderer, 'default', COL_DEFAULT); + + this._treeView.insert_column(colKey, 0); + this._treeView.insert_column(colValue, 1); + this._treeView.insert_column(colDefault, 2); + this._treeView.insert_column(colDescription, 3); + this._treeView.insert_column(colTest, 4); + + this.add(this._treeView); + this.show_all(); + } +} +); + +// vim: set sw=4 ts=4 : diff --git a/prefs.js b/prefs.js index f36c0ec..50e18b9 100644 --- a/prefs.js +++ b/prefs.js @@ -2,169 +2,15 @@ imports.gi.versions.Gtk = '3.0'; -const {GLib, GObject, Gtk} = imports.gi; - const ExtensionUtils = imports.misc.extensionUtils; const Local = ExtensionUtils.getCurrentExtension(); -const {JackConfigure} = Local.imports.jack.jackdbus; - -const COL_KEY = 0, - COL_VALUE = 1, - COL_DEFAULT = 2, - COL_DESC = 3, - COL_LEAF = 4, - COL_ISRANGE = 5, - COL_ISSTRICT = 6, - COL_ISFAKEVALUE = 7, - COL_VALUES = 8, - COL_VALUESTRING = 9, - COL_DEFAULTSTRING = 10; - +const {PrefsWidget} = Local.imports.gsjackctl.prefsWidget; -// Like `extension.js` this is used for any one-time setup like translations. function init() { log('initializing gsjackctl Preferences'); } -var CellRendererVariant = GObject.registerClass( -class CellRendererVariant extends Gtk.CellRenderer { - constructor(params) { - super(params); - this._textRenderer = new Gtk.CellRendererText(); - this._toggleRenderer = new Gtk.CellRendererToggle(); - this._comboRenderer = new Gtk.CellRendererCombo(); - this._spinRenderer = new Gtk.CellRendererSpin(); - } -} -); - -var PrefsWidget = GObject.registerClass( -class PrefsWidget extends Gtk.ScrolledWindow { - _init(params) { - super._init(params); - - this._jackcfg = new JackConfigure(); - - log('building prefs widget...'); - - this._treeStore = new Gtk.TreeStore(); - const colTypes = []; - colTypes[COL_KEY] = GObject.TYPE_STRING; - colTypes[COL_VALUE] = GObject.TYPE_VARIANT; - colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; - colTypes[COL_DESC] = GObject.TYPE_STRING; - colTypes[COL_LEAF] = GObject.TYPE_BOOLEAN; - colTypes[COL_ISFAKEVALUE] = GObject.TYPE_BOOLEAN; - colTypes[COL_ISRANGE] = GObject.TYPE_BOOLEAN; - colTypes[COL_ISSTRICT] = GObject.TYPE_BOOLEAN; - colTypes[COL_VALUES] = GObject.TYPE_VARIANT; - colTypes[COL_VALUESTRING] = GObject.TYPE_STRING; - colTypes[COL_DEFAULTSTRING] = GObject.TYPE_STRING; - - this._treeStore.set_column_types(colTypes); - - // adaptation of _fullConfigurationTree() traversing for TreeStore - const pathStack = [[]], - iterStack = [null]; - while (pathStack.length > 0) { - const path = pathStack.pop(); - const iter = iterStack.pop() || null; - const [isLeaf, nodes] = this._jackcfg.ReadContainerSync(path); - - // set leaf flag in store, except for root node - if (iter) - this._treeStore.set(iter, [COL_LEAF], [isLeaf]); - - if (!isLeaf) { - nodes.forEach(node => { - const newPath = path.concat(node); - pathStack.push(newPath); - const newIter = this._treeStore.append(iter); - iterStack.push(newIter); - this._treeStore.set(newIter, [COL_KEY], [node]); - }); - } else { - nodes.forEach(node => { - const newPath = path.concat(node); - const [paramInfo] = this._jackcfg.GetParameterInfoSync(newPath); - const paramValue = this._jackcfg.GetParameterValueSync(newPath); - const paramConstraint = this._jackcfg.GetParameterConstraintSync(newPath); - const newIter = this._treeStore.append(iter); - this._treeStore.set(newIter, [ - COL_KEY, - COL_VALUESTRING, - COL_DEFAULTSTRING, - COL_DESC, - COL_ISRANGE, - COL_ISRANGE, - COL_ISFAKEVALUE, - ], [ - paramInfo[1], - `${paramValue[2].get_type_string()}:${paramValue[2].print(false)}`, - `${paramValue[1].get_type_string()}:${paramValue[1].print(false)}`, - paramInfo[2], - paramConstraint[0], - paramConstraint[1], - paramConstraint[2], - ]); - }); - } - } - - this._treeView = new Gtk.TreeView({ - expand: true, - model: this._treeStore, - }); - - const colKey = new Gtk.TreeViewColumn({title: 'Key'}); - const colValue = new Gtk.TreeViewColumn({title: 'Value'}); - const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); - const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); - const colState = new Gtk.TreeViewColumn({ - title: 'State', - }); - - // dynamic renderer - const variantRenderer = new CellRendererVariant(); - - const normalRenderer = new Gtk.CellRendererText(); - const toggleRenderer = new Gtk.CellRendererToggle({ - activatable: true, - }); - toggleRenderer.connect('toggled', (rend, path) => { - const [ok, it] = this._treeStore.get_iter_from_string(path); - print(`toggled: ${rend.active}, ${ok}`); - if (ok) - // test - this._treeStore.set(it, [COL_ISSTRICT], [!rend.active]); - - // const currentState = this._treeStore. - }); - - colKey.pack_start(normalRenderer, true); - colValue.pack_start(normalRenderer, true); - colDefault.pack_start(normalRenderer, true); - colDescription.pack_start(normalRenderer, true); - colState.pack_start(toggleRenderer, true); - - colKey.add_attribute(normalRenderer, 'text', COL_KEY); - colValue.add_attribute(normalRenderer, 'text', COL_VALUESTRING); - colDefault.add_attribute(normalRenderer, 'text', COL_DEFAULTSTRING); - colDescription.add_attribute(normalRenderer, 'text', COL_DESC); - - this._treeView.insert_column(colKey, 0); - this._treeView.insert_column(colValue, 1); - this._treeView.insert_column(colDefault, 2); - this._treeView.insert_column(colDescription, 3); - this._treeView.insert_column(colState, 4); - - this.add(this._treeView); - this.show_all(); - } -} -); - function buildPrefsWidget() { const widget = new PrefsWidget(); widget.show_all(); diff --git a/test/cfg_widget.js b/test/cfg_widget.js index 6921cff..3c4dc1a 100755 --- a/test/cfg_widget.js +++ b/test/cfg_widget.js @@ -2,19 +2,9 @@ imports.gi.versions.Gtk = '3.0'; -const {GObject, Gtk, Pango} = imports.gi; +const {Gtk} = imports.gi; -const {JackConfigure} = imports.jack.jackdbus; - -const COL_KEY = 0, - COL_VALUE = 1, - COL_DEFAULT = 2, - COL_DESC = 3, - COL_LEAF = 4, - COL_ISRANGE = 5, - COL_ISSTRICT = 6, - COL_ISFAKEVALUE = 7, - COL_VALUES = 8; +const {PrefsWidget} = imports.gsjackctl.prefsWidget; class TestWindow { constructor() { @@ -34,7 +24,6 @@ class TestWindow { } _buildUI() { - const jackcfg = new JackConfigure(); this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, @@ -43,121 +32,10 @@ class TestWindow { title: 'gsjackctl prefs window test', }); - this._treeStore = new Gtk.TreeStore(); - const colTypes = []; - colTypes[COL_KEY] = GObject.TYPE_STRING; - colTypes[COL_VALUE] = GObject.TYPE_VARIANT; - colTypes[COL_DEFAULT] = GObject.TYPE_VARIANT; - colTypes[COL_DESC] = GObject.TYPE_STRING; - colTypes[COL_LEAF] = GObject.TYPE_BOOLEAN; - colTypes[COL_ISFAKEVALUE] = GObject.TYPE_BOOLEAN; - colTypes[COL_ISRANGE] = GObject.TYPE_BOOLEAN; - colTypes[COL_ISSTRICT] = GObject.TYPE_BOOLEAN; - colTypes[COL_VALUES] = GObject.TYPE_VARIANT; - - this._treeStore.set_column_types(colTypes); - - // adaptation of _fullConfigurationTree() traversing for TreeStore - const pathStack = [[]], - iterStack = [null]; - while (pathStack.length > 0) { - const path = pathStack.pop(); - const iter = iterStack.pop() || null; - const [isLeaf, nodes] = jackcfg.ReadContainerSync(path); - - // set leaf flag in store, except for root node - if (iter) - this._treeStore.set(iter, [COL_LEAF], [isLeaf]); - - if (!isLeaf) { - nodes.forEach(node => { - const newPath = path.concat(node); - pathStack.push(newPath); - const newIter = this._treeStore.append(iter); - iterStack.push(newIter); - this._treeStore.set(newIter, [COL_KEY], [node]); - }); - } else { - nodes.forEach(node => { - const newPath = path.concat(node); - const [paramInfo] = jackcfg.GetParameterInfoSync(newPath); - // const paramValue = jackcfg.GetParameterValueSync(newPath); - const paramConstraint = jackcfg.GetParameterConstraintSync(newPath); - const newIter = this._treeStore.append(iter); - this._treeStore.set(newIter, [ - COL_KEY, - COL_DESC, - COL_ISRANGE, - COL_ISRANGE, - COL_ISFAKEVALUE, - ], [ - paramInfo[1], - paramInfo[2], - paramConstraint[0], - paramConstraint[1], - paramConstraint[2], - ]); - }); - } - } - this._comboStore = new Gtk.ListStore(); - this._comboStore.set_column_types([ - GObject.TYPE_STRING, - GObject.TYPE_STRING, - ]); - this._comboStore.set(this._comboStore.append(), [0, 1], ['es', 'Spain']); - this._comboStore.set(this._comboStore.append(), [0, 1], ['pt', 'Portugal']); - this._comboStore.set(this._comboStore.append(), [0, 1], ['de', 'Germany']); - this._comboStore.set(this._comboStore.append(), [0, 1], ['pl', 'Poland']); - this._comboStore.set(this._comboStore.append(), [0, 1], ['sv', 'Sweden']); - - this._treeView = new Gtk.TreeView({ - expand: true, - model: this._treeStore, - }); - - const colKey = new Gtk.TreeViewColumn({title: 'Key'}); - const colValue = new Gtk.TreeViewColumn({title: 'Value'}); - const colDefault = new Gtk.TreeViewColumn({title: 'Default'}); - const colDescription = new Gtk.TreeViewColumn({title: 'Description'}); - const colState = new Gtk.TreeViewColumn({ - title: 'State', - }); - - const normalRenderer = new Gtk.CellRendererText(); - const comboRenderer = new Gtk.CellRendererCombo({ - model: this._comboStore, - }); - const toggleRenderer = new Gtk.CellRendererToggle({ - activatable: true, - }); - toggleRenderer.connect('toggled', (rend, path) => { - const [ok, it] = this._treeStore.get_iter_from_string(path); - print(`toggled: ${rend.active}, ${ok}`); - if (ok) - // test - this._treeStore.set(it, [COL_ISSTRICT], [!rend.active]); - - // const currentState = this._treeStore. - }); - - colKey.pack_start(normalRenderer, true); - colValue.pack_start(comboRenderer, true); - colDefault.pack_start(comboRenderer, true); - colDescription.pack_start(normalRenderer, true); - colState.pack_start(toggleRenderer, true); - - colKey.add_attribute(normalRenderer, 'text', COL_KEY); - colDescription.add_attribute(normalRenderer, 'text', COL_DESC); - // TESTING - // colState.add_attribute(toggleRenderer, 'active', COL_ISSTRICT); - // colState.add_attribute(toggleRenderer, 'visible', COL_LEAF); - - this._treeView.insert_column(colKey, 0); - this._treeView.insert_column(colDescription, 1); - this._treeView.insert_column(colState, 2); + const widget = new PrefsWidget(); + widget.show_all(); - this._window.add(this._treeView); + this._window.add(widget); this._window.show_all(); } }