Feature: Add move up and down buttons to make the prefs keyboard access.

Don't use CONSTRUCT_ONLY anymore to be able to use private properties
(or any properties at all it seems).
This commit is contained in:
Julian 2023-10-04 03:28:59 +02:00
parent a1188d5684
commit 9b7ab0614c
Signed by: julian
GPG Key ID: 094C2AC34192FA11
6 changed files with 219 additions and 34 deletions

View File

@ -42,6 +42,16 @@
</child> </child>
</template> </template>
<menu id="optionsMenuModel"> <menu id="optionsMenuModel">
<section>
<item>
<attribute name="label">Move Up</attribute>
<attribute name="action">row.move-up</attribute>
</item>
<item>
<attribute name="label">Move Down</attribute>
<attribute name="action">row.move-down</attribute>
</item>
</section>
<section> <section>
<item> <item>
<attribute name="label">Forget</attribute> <attribute name="label">Forget</attribute>

View File

@ -13,8 +13,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="PrefsBoxOrderListBox"> <object class="PrefsBoxOrderListBox" id="left-box-order-list-box">
<property name="box-order">left-box-order</property> <property name="box-order">left-box-order</property>
<signal name="row-move" handler="onRowMove"/>
</object> </object>
</child> </child>
<child> <child>
@ -26,8 +27,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="PrefsBoxOrderListBox"> <object class="PrefsBoxOrderListBox" id="center-box-order-list-box">
<property name="box-order">center-box-order</property> <property name="box-order">center-box-order</property>
<signal name="row-move" handler="onRowMove"/>
</object> </object>
</child> </child>
<child> <child>
@ -39,8 +41,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="PrefsBoxOrderListBox"> <object class="PrefsBoxOrderListBox" id="right-box-order-list-box">
<property name="box-order">right-box-order</property> <property name="box-order">right-box-order</property>
<signal name="row-move" handler="onRowMove"/>
</object> </object>
</child> </child>
</object> </object>

View File

@ -7,13 +7,24 @@ import GObject from "gi://GObject";
import Adw from "gi://Adw"; import Adw from "gi://Adw";
import GLib from "gi://GLib"; import GLib from "gi://GLib";
const PrefsBoxOrderItemRow = GObject.registerClass({ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
GTypeName: "PrefsBoxOrderItemRow", static {
Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-item-row.ui", GLib.UriFlags.NONE), GObject.registerClass({
InternalChildren: [ GTypeName: "PrefsBoxOrderItemRow",
"item-name-display-label" Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-item-row.ui", GLib.UriFlags.NONE),
] InternalChildren: [
}, class PrefsBoxOrderItemRow extends Adw.ActionRow { "item-name-display-label"
],
Signals: {
"move": {
param_types: [GObject.TYPE_STRING]
}
}
}, this);
this.install_action("row.move-up", null, (self, _actionName, _param) => self.emit("move", "up"));
this.install_action("row.move-down", null, (self, _actionName, _param) => self.emit("move", "down"));
}
#drag_starting_point_x; #drag_starting_point_x;
#drag_starting_point_y; #drag_starting_point_y;
@ -52,8 +63,9 @@ const PrefsBoxOrderItemRow = GObject.registerClass({
}); });
forgetAction.connect("activate", (_action, _params) => { forgetAction.connect("activate", (_action, _params) => {
const parentListBox = this.get_parent(); const parentListBox = this.get_parent();
parentListBox.remove(this); parentListBox.removeRow(this);
parentListBox.saveBoxOrderToSettings(); parentListBox.saveBoxOrderToSettings();
parentListBox.determineRowMoveActionEnable();
}); });
actionGroup.add_action(forgetAction); actionGroup.add_action(forgetAction);
@ -101,7 +113,7 @@ const PrefsBoxOrderItemRow = GObject.registerClass({
const valuePosition = value.get_index(); const valuePosition = value.get_index();
// Remove the drop value from its list box. // Remove the drop value from its list box.
valueListBox.remove(value); valueListBox.removeRow(value);
// Since an element got potentially removed from the list of `this`, // Since an element got potentially removed from the list of `this`,
// get the position of `this` again. // get the position of `this` again.
@ -115,31 +127,31 @@ const PrefsBoxOrderItemRow = GObject.registerClass({
|| (ownListBox.boxOrder === "center-box-order" && valueListBox.boxOrder === "left-box-order")) { || (ownListBox.boxOrder === "center-box-order" && valueListBox.boxOrder === "left-box-order")) {
// If the list box of the drop value comes before the list // If the list box of the drop value comes before the list
// box of `this`, add the drop value after `this`. // box of `this`, add the drop value after `this`.
ownListBox.insert(value, updatedOwnPosition + 1); ownListBox.insertRow(value, updatedOwnPosition + 1);
} else { } else {
// Otherwise, add the drop value where `this` currently is. // Otherwise, add the drop value where `this` currently is.
ownListBox.insert(value, updatedOwnPosition); ownListBox.insertRow(value, updatedOwnPosition);
} }
} else { } else {
if (valuePosition < ownPosition) { if (valuePosition < ownPosition) {
// If the drop value was before `this`, add the drop value // If the drop value was before `this`, add the drop value
// after `this`. // after `this`.
ownListBox.insert(value, updatedOwnPosition + 1); ownListBox.insertRow(value, updatedOwnPosition + 1);
} else { } else {
// Otherwise, add the drop value where `this` currently is. // Otherwise, add the drop value where `this` currently is.
ownListBox.insert(value, updatedOwnPosition); ownListBox.insertRow(value, updatedOwnPosition);
} }
} }
/// Finally save the box order(/s) to settings. /// Finally save the box order(/s) to settings and make sure move
/// actions are correctly enabled/disabled.
ownListBox.saveBoxOrderToSettings(); ownListBox.saveBoxOrderToSettings();
// If the list boxes of `this` and the drop value were different, ownListBox.determineRowMoveActionEnable();
// save an updated box order for the list were the drop value was in // If the list boxes of `this` and the drop value were different, handle
// as well. // the former list box of the drop value as well.
if (ownListBox !== valueListBox) { if (ownListBox !== valueListBox) {
valueListBox.saveBoxOrderToSettings(); valueListBox.saveBoxOrderToSettings();
valueListBox.determineRowMoveActionEnable();
} }
} }
}); }
export default PrefsBoxOrderItemRow;

View File

@ -17,12 +17,18 @@ const PrefsBoxOrderListBox = GObject.registerClass({
"box-order", "box-order",
"Box Order", "Box Order",
"The box order this PrefsBoxOrderListBox is associated with.", "The box order this PrefsBoxOrderListBox is associated with.",
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GObject.ParamFlags.READWRITE,
"" ""
) )
},
Signals: {
"row-move": {
param_types: [PrefsBoxOrderItemRow, GObject.TYPE_STRING]
}
} }
}, class PrefsBoxOrderListBox extends Gtk.ListBox { }, class PrefsBoxOrderListBox extends Gtk.ListBox {
#settings; #settings;
#rowSignalHandlerIds = new Map();
/** /**
* @param {Object} params * @param {Object} params
@ -45,21 +51,49 @@ const PrefsBoxOrderListBox = GObject.registerClass({
set boxOrder(value) { set boxOrder(value) {
this._boxOrder = value; this._boxOrder = value;
// Load the settings here as well, since a `CONSTRUCT_ONLY` property
// apparently can't access `this.#settings`.
const settings = ExtensionPreferences.lookupByURL(import.meta.url).getSettings();
// Get the actual box order for the given box order name from settings. // Get the actual box order for the given box order name from settings.
const boxOrder = settings.get_strv(this._boxOrder); const boxOrder = this.#settings.get_strv(this._boxOrder);
// Populate this GtkListBox with GtkListBoxRows for the items of the // Populate this GtkListBox with GtkListBoxRows for the items of the
// given configured box order. // given configured box order.
for (const item of boxOrder) { for (const item of boxOrder) {
const listBoxRow = new PrefsBoxOrderItemRow({}, item); const row = new PrefsBoxOrderItemRow({}, item);
this.append(listBoxRow); this.insertRow(row, -1);
} }
this.determineRowMoveActionEnable();
this.notify("box-order"); this.notify("box-order");
} }
/**
* Inserts the given PrefsBoxOrderItemRow to this list box at the given
* position.
* Also handles stuff like connecting signals.
*/
insertRow(row, position) {
this.insert(row, position);
const signalHandlerIds = [];
signalHandlerIds.push(row.connect("move", (row, direction) => {
this.emit("row-move", row, direction);
}));
this.#rowSignalHandlerIds.set(row, signalHandlerIds);
}
/**
* Removes the given PrefsBoxOrderItemRow from this list box.
* Also handles stuff like disconnecting signals.
*/
removeRow(row) {
const signalHandlerIds = this.#rowSignalHandlerIds.get(row);
for (const id of signalHandlerIds) {
row.disconnect(id);
}
this.remove(row);
}
/** /**
* Saves the box order represented by `this` (and its * Saves the box order represented by `this` (and its
* `PrefsBoxOrderItemRows`) to settings. * `PrefsBoxOrderItemRows`) to settings.
@ -77,6 +111,38 @@ const PrefsBoxOrderListBox = GObject.registerClass({
} }
this.#settings.set_strv(this.boxOrder, currentBoxOrder); this.#settings.set_strv(this.boxOrder, currentBoxOrder);
} }
/**
* Determines whether or not each move action of each PrefsBoxOrderItemRow
* should be enabled or disabled.
*/
determineRowMoveActionEnable() {
for (let potentialPrefsBoxOrderItemRow of this) {
// Only process PrefsBoxOrderItemRows.
if (potentialPrefsBoxOrderItemRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") {
continue;
}
const row = potentialPrefsBoxOrderItemRow;
// If the current row is the topmost row in the topmost list box,
// then disable the move-up action.
if (row.get_index() === 0 && this.boxOrder === "left-box-order") {
row.action_set_enabled("row.move-up", false);
} else { // Else enable it.
row.action_set_enabled("row.move-up", true);
}
// If the current row is the bottommost row in the bottommost list
// box, then disable the move-down action.
const rowNextSibling = row.get_next_sibling();
if ((rowNextSibling instanceof PrefsBoxOrderListEmptyPlaceholder || rowNextSibling === null) && this.boxOrder === "right-box-order") {
row.action_set_enabled("row.move-down", false);
} else { // Else enable it.
row.action_set_enabled("row.move-down", true);
}
}
}
}); });
export default PrefsBoxOrderListBox; export default PrefsBoxOrderListBox;

View File

@ -16,14 +16,17 @@ const PrefsBoxOrderListEmptyPlaceholder = GObject.registerClass({
const valueListBox = value.get_parent(); const valueListBox = value.get_parent();
// Remove the drop value from its list box. // Remove the drop value from its list box.
valueListBox.remove(value); valueListBox.removeRow(value);
// Insert the drop value into the list box of `this`. // Insert the drop value into the list box of `this`.
ownListBox.insert(value, 0); ownListBox.insertRow(value, 0);
/// Finally save the box orders to settings. /// Finally save the box orders to settings and make sure move actions
/// are correctly enabled/disabled.
ownListBox.saveBoxOrderToSettings(); ownListBox.saveBoxOrderToSettings();
ownListBox.determineRowMoveActionEnable();
valueListBox.saveBoxOrderToSettings(); valueListBox.saveBoxOrderToSettings();
valueListBox.determineRowMoveActionEnable();
} }
}); });

View File

@ -6,13 +6,19 @@ import Adw from "gi://Adw";
import GLib from "gi://GLib"; import GLib from "gi://GLib";
import ScrollManager from "./ScrollManager.js"; import ScrollManager from "./ScrollManager.js";
import PrefsBoxOrderListEmptyPlaceholder from "./PrefsBoxOrderListEmptyPlaceholder.js";
// Imports to make UI file work. // Imports to make UI file work.
import PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js"; import PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js";
const PrefsPage = GObject.registerClass({ const PrefsPage = GObject.registerClass({
GTypeName: "PrefsPage", GTypeName: "PrefsPage",
Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-page.ui", GLib.UriFlags.NONE) Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-page.ui", GLib.UriFlags.NONE),
InternalChildren: [
"left-box-order-list-box",
"center-box-order-list-box",
"right-box-order-list-box"
]
}, class PrefsPage extends Adw.PreferencesPage { }, class PrefsPage extends Adw.PreferencesPage {
constructor(params = {}) { constructor(params = {}) {
super(params); super(params);
@ -81,6 +87,91 @@ const PrefsPage = GObject.registerClass({
this.add_controller(controller); this.add_controller(controller);
} }
onRowMove(listBox, row, direction) {
const rowPosition = row.get_index();
if (direction === "up") { // If the direction of the move is up.
// Handle the case, where the row is the topmost row in the list box.
if (rowPosition === 0) {
switch (listBox.boxOrder) {
// If the row is also in the topmost list box, then do
// nothing and return.
case "left-box-order":
log("The row is already the topmost row in the topmost box order.");
return;
// If the row is in the center list box, then move it up to
// the left one.
case "center-box-order":
listBox.removeRow(row);
this._left_box_order_list_box.insertRow(row, -1);
// First save the box order of the destination, then do
// "a save for clean up".
this._left_box_order_list_box.saveBoxOrderToSettings();
this._left_box_order_list_box.determineRowMoveActionEnable();
listBox.saveBoxOrderToSettings();
listBox.determineRowMoveActionEnable();
return;
// If the row is in the right list box, then move it up to
// the center one.
case "right-box-order":
listBox.removeRow(row);
this._center_box_order_list_box.insertRow(row, -1);
this._center_box_order_list_box.saveBoxOrderToSettings();
this._center_box_order_list_box.determineRowMoveActionEnable();
listBox.saveBoxOrderToSettings();
listBox.determineRowMoveActionEnable();
return;
}
}
// Else just move the row up in the box.
listBox.removeRow(row);
listBox.insertRow(row, rowPosition - 1);
listBox.saveBoxOrderToSettings();
listBox.determineRowMoveActionEnable();
return;
} else { // Else the direction of the move must be down.
// Handle the case, where the row is the bottommost row in the list box.
const rowNextSibling = row.get_next_sibling();
if (rowNextSibling instanceof PrefsBoxOrderListEmptyPlaceholder || rowNextSibling === null) {
switch (listBox.boxOrder) {
// If the row is also in the bottommost list box, then do
// nothing and return.
case "right-box-order":
log("The row is already the bottommost row in the bottommost box order.");
return;
// If the row is in the center list box, then move it down
// to the right one.
case "center-box-order":
listBox.removeRow(row);
this._right_box_order_list_box.insertRow(row, 0);
this._right_box_order_list_box.saveBoxOrderToSettings();
this._right_box_order_list_box.determineRowMoveActionEnable();
listBox.saveBoxOrderToSettings();
listBox.determineRowMoveActionEnable();
return;
// If the row is in the left list box, then move it down to
// the center one.
case "left-box-order":
listBox.removeRow(row);
this._center_box_order_list_box.insertRow(row, 0);
this._center_box_order_list_box.saveBoxOrderToSettings();
this._center_box_order_list_box.determineRowMoveActionEnable();
listBox.saveBoxOrderToSettings();
listBox.determineRowMoveActionEnable();
return;
}
}
// Else just move the row down in the box.
listBox.removeRow(row);
listBox.insertRow(row, rowPosition + 1);
listBox.saveBoxOrderToSettings();
listBox.determineRowMoveActionEnable();
return;
}
}
}); });
export default PrefsPage; export default PrefsPage;