Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

library: Add ListModel Entry #361

Merged
merged 20 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/Library/demos/List Model/main.blp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Gtk 4.0;
using Adw 1;

Adw.StatusPage {
title: "List Model";
description: _("List models are a simple interface for ordered lists of GObject instances");
andyholmes marked this conversation as resolved.
Show resolved Hide resolved

Box {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so let's do this:

Except for the StackSwitcher and FlowBox items, go through and remove all the halign: center and width-request properties. Then put this Box inside an Adw.Clamp with maximum-size: 640; so you have something like:

Adw.Clamp {
  maximum-size: 640;
  child:
    Box current_box {
      // ...
    };
}

Then set hexpand: true and valign: start on list_box, flow_box and list_box_editable. That should even up the whole demo.

orientation: vertical;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can let the containing box do the child spacing.

Suggested change
orientation: vertical;
orientation: vertical;
spacing: 24;

Box {
orientation: horizontal;
halign: center;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one can stay, so the switch is nicely centered about the lists.

margin-bottom: 24;
styles ["linked"]

ToggleButton toggle_list_box {
active: true;
label: _("ListBox");
}

ToggleButton toggle_flow_box {
active: false;
label: _("FlowBox");
group: toggle_list_box;
}
}

Revealer reveal_list_box {
transition-duration: 300;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
transition-duration: 300;

Not needed.

transition-type: slide_up;
margin-bottom: 24;
reveal-child: true;

Box {
halign: center;

ListBox list_box {
activate-on-single-click: true;
selection-mode: none;
width-request: 360;

styles ["boxed-list"]
}
}
}

Revealer reveal_flow_box {
transition-duration: 300;
transition-type: slide_up;
margin-bottom: 24;

Box {
halign: center;

FlowBox flow_box {
width-request: 360;
activate-on-single-click: true;
orientation: horizontal;
selection-mode: none;
styles ["card"]
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's clean this all up with some higher-level widget.

Suggested change
Box {
orientation: horizontal;
halign: center;
margin-bottom: 24;
styles ["linked"]
ToggleButton toggle_list_box {
active: true;
label: _("ListBox");
}
ToggleButton toggle_flow_box {
active: false;
label: _("FlowBox");
group: toggle_list_box;
}
}
Revealer reveal_list_box {
transition-duration: 300;
transition-type: slide_up;
margin-bottom: 24;
reveal-child: true;
Box {
halign: center;
ListBox list_box {
activate-on-single-click: true;
selection-mode: none;
width-request: 360;
styles ["boxed-list"]
}
}
}
Revealer reveal_flow_box {
transition-duration: 300;
transition-type: slide_up;
margin-bottom: 24;
Box {
halign: center;
FlowBox flow_box {
width-request: 360;
activate-on-single-click: true;
orientation: horizontal;
selection-mode: none;
styles ["card"]
}
}
}
Gtk.StackSwitcher {
stack: stack;
halign: center;
}
Gtk.Stack stack {
transition-duration: 300;
transition-type: crossfade;
vexpand: true;
Gtk.StackPage {
name: "listbox";
title: _("List Box");
child:
Box {
halign: center;
ListBox list_box {
activate-on-single-click: true;
selection-mode: none;
width-request: 360;
styles ["boxed-list"]
}
};
}
Gtk.StackPage {
name: "flowbox";
title: _("Flow Box");
child:
Box {
halign: center;
FlowBox flow_box {
width-request: 360;
activate-on-single-click: true;
orientation: horizontal;
selection-mode: none;
styles ["card"]
}
};
}
}


Box {
styles ["linked"]
halign: center;
margin-bottom: 18;

Button add {
label: _("Add Items");
}
Button remove {
label: _("Remove Items");
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this above the "Edit" page's list, and connect the whole thing so it looks something like this:

Box {
  orientation: vertical;
  spacing: 18;

  Box {
    styles ["linked"]

    SearchEntry search_entry {
      hexpand: true;
      placeholder-text: _("Start searching");
    }
    Button add {
      icon-name: "list-add-symbolic";
      tooltip-text: _("Add Item");
    }
    Button remove {
      icon-name: "list-remove-symbolic";
      tooltip-text: _("Remove Item");
    }
  }

  ListBox list_box_editable {
    activate-on-single-click: true;
    selection-mode: single;
    styles ["boxed-list"]
  }
}


LinkButton{
label: "Documentation";
uri: "https://gjs.guide/guides/gio/list-models.html";
}
}
}




86 changes: 86 additions & 0 deletions src/Library/demos/List Model/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import Gio from "gi://Gio";
import Gdk from "gi://Gdk";
import Gtk from "gi://Gtk";
import Adw from "gi://Adw";

const toggle_list_box = workbench.builder.get_object("toggle_list_box");
const toggle_flow_box = workbench.builder.get_object("toggle_flow_box");
const reveal_list_box = workbench.builder.get_object("reveal_list_box");
const reveal_flow_box = workbench.builder.get_object("reveal_flow_box");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much simpler 🙂

Suggested change
const toggle_list_box = workbench.builder.get_object("toggle_list_box");
const toggle_flow_box = workbench.builder.get_object("toggle_flow_box");
const reveal_list_box = workbench.builder.get_object("reveal_list_box");
const reveal_flow_box = workbench.builder.get_object("reveal_flow_box");
const stack = workbench.builder.get_object("stack");

const list_box = workbench.builder.get_object("list_box");
const flow_box = workbench.builder.get_object("flow_box");
const add = workbench.builder.get_object("add");
const remove = workbench.builder.get_object("remove");

//ListModel initialization and binding
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//ListModel initialization and binding
// Model

const model = new Gtk.StringList();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const model = new Gtk.StringList();
const model = new Gtk.StringList({
strings: [
"Default Item 1",
"Default Item 2",
"Default Item 3",
],
});

I think this is the easiest way to avoid dealing with empty states in the demo. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's obviously better than a void space

let item = 1;

andyholmes marked this conversation as resolved.
Show resolved Hide resolved
function createItemForListBox(listItem) {
const listRow = new Adw.ActionRow({
title: listItem.string,
});
return listRow;
}

function createItemForFlowBox(listItem) {
const listRow = new Adw.ActionRow({
title: listItem.string,
});
return listRow;
}
andyholmes marked this conversation as resolved.
Show resolved Hide resolved

list_box.bind_model(model, createItemForListBox);
flow_box.bind_model(model, createItemForFlowBox);

//Handling for ListBox
const isListBoxActive = toggle_list_box.get_active();
reveal_list_box.reveal_child = isListBoxActive;
if (isListBoxActive) {
add.connect("clicked", () => {
const newItem = `Item ${item}`;
model.append(newItem); // Append the new item as an array to the model
item++;
});
remove.connect("clicked", () => {
const length = model.get_n_items();
model.remove(length - 1);
});
}

//Handling for FlowBox
const isFlowBoxActive = toggle_flow_box.get_active();
reveal_flow_box.reveal_child = isFlowBoxActive;
if (isFlowBoxActive) {
add.connect("clicked", () => {
const newItem = `Item ${item}`;
model.append(newItem); // Append the new item as an array to the model
item++;
});
remove.connect("clicked", () => {
const length = model.get_n_items();
model.remove(length - 1);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we only need one set of these, since we're "controlling" the "model" in a model-view-controller pattern. Stripping this part down makes that a little bit clearer.

Suggested change
//Handling for ListBox
const isListBoxActive = toggle_list_box.get_active();
reveal_list_box.reveal_child = isListBoxActive;
if (isListBoxActive) {
add.connect("clicked", () => {
const newItem = `Item ${item}`;
model.append(newItem); // Append the new item as an array to the model
item++;
});
remove.connect("clicked", () => {
const length = model.get_n_items();
model.remove(length - 1);
});
}
//Handling for FlowBox
const isFlowBoxActive = toggle_flow_box.get_active();
reveal_flow_box.reveal_child = isFlowBoxActive;
if (isFlowBoxActive) {
add.connect("clicked", () => {
const newItem = `Item ${item}`;
model.append(newItem); // Append the new item as an array to the model
item++;
});
remove.connect("clicked", () => {
const length = model.get_n_items();
model.remove(length - 1);
});
}
// Controller
add.connect("clicked", () => {
const new_item = `Item ${item}`;
model.append(new_item);
item++;
});
remove.connect("clicked", () => {
const n_items = model.get_n_items();
model.remove(n_items - 1);
});


// Check if ListBox is Active
toggle_list_box.connect("toggled", () => {
const isActive = toggle_list_box.get_active();
reveal_list_box.reveal_child = isActive;
if (isActive) {
console.log("ListBox toggled on");
} else {
console.log("ListBox toggled off");
}
});

// Check if FlowBox is Active
toggle_flow_box.connect("toggled", () => {
const isActive = toggle_flow_box.get_active();
reveal_flow_box.reveal_child = isActive;
if (isActive) {
console.log("FlowBox toggled on");
} else {
console.log("FlowBox toggled off");
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea to identify when the view has changed, but let's keep it simple so it doesn't distract from the rest of the demo.

Suggested change
// Check if ListBox is Active
toggle_list_box.connect("toggled", () => {
const isActive = toggle_list_box.get_active();
reveal_list_box.reveal_child = isActive;
if (isActive) {
console.log("ListBox toggled on");
} else {
console.log("ListBox toggled off");
}
});
// Check if FlowBox is Active
toggle_flow_box.connect("toggled", () => {
const isActive = toggle_flow_box.get_active();
reveal_flow_box.reveal_child = isActive;
if (isActive) {
console.log("FlowBox toggled on");
} else {
console.log("FlowBox toggled off");
}
});
// View
stack.connect("notify::visible-child", () => {
if (stack.visible_child === list_box) {
console.log("View: List Box");
} else {
console.log("View: Flow Box");
}
});

7 changes: 7 additions & 0 deletions src/Library/demos/List Model/main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "List Model",
"category": "layout",
"description": "List models are a simple interface for ordered lists of GObject instances",
"panels": ["ui", "preview"],
"autorun": true
}