Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
83bf4bf
[wip] gtk4-treeview
stsdc Feb 11, 2026
35e7641
[wip] add signal list factory
stsdc Feb 20, 2026
236bc00
ProcessTreeView: Show process name and add sorting
stsdc Feb 21, 2026
21f5432
ProcessTreeView: Reintroduce cpu and memory columns
stsdc Feb 21, 2026
98b801f
ProcessTreeView: Reintroduce PID column
stsdc Feb 23, 2026
780a157
ProcessTreeView: reintroduce process_selected signal
stsdc Feb 23, 2026
1bd04dd
ProcessTreeView: use selected-item property to watch
stsdc Feb 24, 2026
a05b0bd
ProcessTreeView: update selection model connection and expand Proces …
stsdc Feb 25, 2026
ade573e
TreeViewModel: update values in the store
stsdc Feb 26, 2026
ee8b5e1
ProcessTreeView: introduce initial filtering by name
stsdc Feb 26, 2026
8e6d601
ProcessTreeView: process filtering works
stsdc Feb 28, 2026
9907a24
ProcessTreeView: reverse sorting for numeric values; fix process reor…
stsdc Mar 4, 2026
b6c2933
ProcessTreeView: show process icons
stsdc Mar 4, 2026
d37b1e1
TreeViewModel: refactor
stsdc Mar 4, 2026
ead83cd
TreeViewModel: refactor
stsdc Mar 4, 2026
a0927f2
CPUProcessTreeView: adios!
stsdc Mar 4, 2026
6b66db3
ProcessView: remove obsolete commented out filtering function
stsdc Mar 4, 2026
952481b
cleanup
stsdc Mar 5, 2026
d5b2492
Remove unused functions
stsdc Mar 6, 2026
336bde8
Remove unneeded GTK namespaces
stsdc Mar 6, 2026
af1686b
Use correct casting syntax
stsdc Mar 6, 2026
63e94ba
Remove old treeview functionality
stsdc Mar 6, 2026
6424b20
Fix code style
stsdc Mar 6, 2026
92d3a60
Use format_size; Move some setup factories to a separate reusable method
stsdc Mar 6, 2026
0c921f1
Convert lambdas to methods
stsdc Mar 6, 2026
932954a
Remove a leftover comment
stsdc Mar 6, 2026
131fe82
Fix memory size
stsdc Mar 6, 2026
74615a0
Localize column_view object
stsdc Mar 6, 2026
d3265cd
Merge branch 'main' into stsdc/gtk4-treeview
stsdc Mar 19, 2026
c42c3bb
TreeVIewModel: Move classes to separate files
stsdc Mar 19, 2026
3922627
TreeViewFilter: make chain filters private
stsdc Mar 19, 2026
9833f56
Fix lint
stsdc Mar 19, 2026
ab9bee1
Merge branch 'main' into stsdc/gtk4-treeview
stsdc Mar 26, 2026
c8b3014
Merge branch 'main' into stsdc/gtk4-treeview
stsdc Apr 10, 2026
c07348f
ProcessTreeView: Use bindings to fix list jumps
stsdc May 10, 2026
111f4db
Use correct casting approach
stsdc May 10, 2026
df026ba
Cleanup and formatting
stsdc May 10, 2026
6b80f21
TreeViewModel: make it singleton
stsdc Mar 11, 2026
cf3aeef
Add license header to ProcessRowData
stsdc May 21, 2026
f06383b
Update POTFILES to include ProcessTreeView
stsdc May 21, 2026
3f8b934
Prevent initializing a new object and substituting it to child prope…
stsdc May 21, 2026
2cce9e9
Limit scope for some properties; prevent null errors on process select
stsdc May 21, 2026
c8eb319
Use binding needle to search property of filters
stsdc May 21, 2026
3cda387
Convert filters to local variables in constructor
stsdc May 21, 2026
95a54c4
Remove unnecessary OR in pid search
stsdc May 21, 2026
c08d704
Rename: name_item_factory_setup → name_item_setup_factory
stsdc May 21, 2026
38647df
Remove whitespace
stsdc May 21, 2026
5bb6099
Introduce ProcessTreeViewNameCell to improve readability
stsdc May 21, 2026
76f5401
Apply suggestions from code review
stsdc May 24, 2026
8f0359f
Remove unnecessary newlines
stsdc May 24, 2026
946de36
Undo: TreeViewModel: make it singleton
stsdc May 24, 2026
f70dd45
Use ProcessTreeViewNameCell in the unbind callback
stsdc May 24, 2026
222a225
Do not init and assign to other's object property
stsdc May 24, 2026
cc25a41
Unbind also icon in the factory unbind callback
stsdc May 24, 2026
1716039
Remove extra newlines; move scrolled window creation at the end of th…
stsdc May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ src/Indicator/Widgets/PopoverWidget.vala
src/Views/ProcessView/ProcessInfoView/ProcessInfoHeader.vala
src/Views/ProcessView/ProcessInfoView/ProcessInfoIOStats.vala
src/Views/ProcessView/ProcessInfoView/ProcessInfoView.vala
src/Views/ProcessView/ProcessTreeView/CPUProcessTreeView.vala
src/Views/ProcessView/ProcessInfoView/ProcessInfoCPURAM.vala
src/Views/ProcessView/ProcessTreeView/ProcessTreeView.vala
src/Views/ProcessView/ProcessTreeView/ProcessTreeViewNameCell.vala
src/Views/PreferencesView.vala
src/Views/SystemView/SystemCPUView.vala
src/Views/SystemView/SystemCPUInfoPopover.vala
Expand Down
14 changes: 1 addition & 13 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,10 @@ public class Monitor.MainWindow : Gtk.ApplicationWindow {
MonitorApp.settings.bind ("opened-view", stack, "visible-child-name", DEFAULT);

search_entry.search_changed.connect (() => {
// collapse tree only when search is focused and changed
if (search_entry.is_focus ()) {
process_view.process_tree_view.collapse_all ();
}

process_view.needle = search_entry.text;

// focus on child row to avoid the app crashes by clicking "Kill/End Process" buttons in headerbar
process_view.process_tree_view.focus_on_child_row ();
process_view.treeview_model.filtered.needle = search_entry.text;
search_entry.grab_focus ();
});

search_entry.activate.connect (() => {
process_view.process_tree_view.focus_on_first_row ();
});

var search_action = new GLib.SimpleAction ("search", null);
search_action.activate.connect (() => {
search_entry.text = "";
Expand Down
15 changes: 15 additions & 0 deletions src/Models/ProcessRowData.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
*/

Comment thread
stsdc marked this conversation as resolved.
/* This class holds data from Process class to use in the ColumnView */
public class Monitor.ProcessRowData : GLib.Object {
public Icon icon { get; set; }
public string name { get; set; }
public int cpu { get; set; }
public uint64 memory { get; set; }
public int pid { get; set; }
public string cmd { get; set; }
public Gee.HashMap<string, Binding> bindings = new Gee.HashMap<string, Binding> ();
}
41 changes: 41 additions & 0 deletions src/Models/TreeViewFilter.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
*/

public class Monitor.TreeViewFilter : GLib.Object {
Comment thread
stsdc marked this conversation as resolved.
public string needle { get; set; }
public Gtk.FilterListModel model_out;
Comment thread
stsdc marked this conversation as resolved.

public TreeViewFilter (GLib.ListModel? model) {
var name_filter = build_str_filter ("name");
var cmd_filter = build_str_filter ("cmd");

// since the pid property is an int, we need to use a custom filter to convert it to a string
var pid_filter = new Gtk.CustomFilter ((obj) => {
var item = (ProcessRowData) obj;
bool pid_found = item.pid.to_string ().contains (needle.casefold ());
return pid_found;
});

var any_filter = new Gtk.AnyFilter ();
any_filter.append (name_filter);
any_filter.append (cmd_filter);
any_filter.append (pid_filter);

model_out = new Gtk.FilterListModel (model, any_filter);

Comment thread
stsdc marked this conversation as resolved.
bind_property ("needle", name_filter, "search", SYNC_CREATE);
bind_property ("needle", cmd_filter, "search", SYNC_CREATE);
}

private Gtk.StringFilter build_str_filter (string column_name) {
var expression = new Gtk.PropertyExpression (typeof (ProcessRowData), null, column_name);
return new Gtk.StringFilter (expression) {
ignore_case = true,
match_mode = SUBSTRING,
search = needle
};
}

}
116 changes: 76 additions & 40 deletions src/Models/TreeViewModel.vala
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,49 @@
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
*/

public enum Monitor.Column {
ICON,
NAME,
CPU,
MEMORY,
PID,
CMD
}
public class Monitor.TreeViewModel : GLib.Object {

public class Monitor.TreeViewModel : Gtk.TreeStore {
public ProcessManager process_manager;
private Gee.Map<int, Gtk.TreeIter ? > process_rows;

public TreeViewFilter filtered { get; private set; }
public Gtk.SingleSelection selection_model { get; private set; }

public signal void added_first_row ();
public signal void process_selected (Process process);

public Gtk.Sorter sorter {
get {
return sorted.sorter;
}
set {
sorted.sorter = value;
}
}

private GLib.ListStore store;
private Gtk.SortListModel sorted;

private Gee.Map<int, ProcessRowData ?> process_rows;

construct {
process_rows = new Gee.HashMap<int, Gtk.TreeIter ? > ();

set_column_types (new Type[] {
typeof (string),
typeof (string),
typeof (double),
typeof (int64),
typeof (int),
typeof (string),
process_rows = new Gee.HashMap<int, ProcessRowData ?> ();
store = new GLib.ListStore (typeof (ProcessRowData));
sorted = new Gtk.SortListModel (store, null);

filtered = new TreeViewFilter (sorted);

selection_model = new Gtk.SingleSelection (filtered.model_out) {
autoselect = true
};

selection_model.notify["selected-item"].connect ((sender, property) => {
var row_data = (ProcessRowData) selection_model.get_selected_item ();
// prevent passing null when there is no more processes left after filtering
if (row_data == null) {
return;
}
Process process = process_manager.get_process (row_data.pid);
process_selected (process);
});

process_manager = ProcessManager.get_default ();
Expand All @@ -37,6 +56,16 @@ public class Monitor.TreeViewModel : Gtk.TreeStore {
Idle.add (() => { add_running_processes (); return false; });
}

public Gtk.StringSorter str_sorter (string column_name) {
return new Gtk.StringSorter (new Gtk.PropertyExpression (typeof (ProcessRowData), null, column_name));
}

public Gtk.NumericSorter num_sorter (string column_name) {
return new Gtk.NumericSorter (new Gtk.PropertyExpression (typeof (ProcessRowData), null, column_name)) {
sort_order = Gtk.SortType.DESCENDING
};
}

private void add_running_processes () {
debug ("add_running_processes");
var running_processes = process_manager.get_process_list ();
Expand All @@ -49,24 +78,22 @@ public class Monitor.TreeViewModel : Gtk.TreeStore {
if (process != null && !process_rows.has_key (process.stat.pid)) {
debug ("Add process %d Parent PID: %d", process.stat.pid, process.stat.ppid);
// add the process to the model
Gtk.TreeIter iter;
append (out iter, null); // null means top-level

// donno what is going on, but maybe just use a string instead of Icon ??
// coz it lagz
// string icon_name = process.icon.to_string ();

set (iter,
Column.NAME, process.application_name,
Column.ICON, process.icon.to_string (),
Column.PID, process.stat.pid,
Column.CMD, process.command,
-1);
var row = new ProcessRowData () {
icon = process.icon,
name = process.application_name,
cpu = (int) process.cpu_percentage,
memory = process.mem_usage,
pid = process.stat.pid,
cmd = process.command
};

store.append (row);

if (process_rows.size < 1) {
added_first_row ();
}
// add the process to our cache of process_rows
process_rows.set (process.stat.pid, iter);
process_rows.set (process.stat.pid, row);
return true;
}
return false;
Expand All @@ -75,20 +102,29 @@ public class Monitor.TreeViewModel : Gtk.TreeStore {
private void update_model () {
foreach (int pid in process_rows.keys) {
Process process = process_manager.get_process (pid);
Gtk.TreeIter iter = process_rows[pid];
set (iter,
Column.CPU, process.cpu_percentage,
Column.MEMORY, process.mem_usage,
-1);
var process_row = process_rows.get (pid);

uint pos;
if (!store.find (process_row, out pos)) {
return;
}

var item = (ProcessRowData) store.get_item (pos);
item.cpu = (int) process.cpu_percentage;
item.memory = process.mem_usage;
sorter.changed (DIFFERENT);
}
}

private void remove_process (int pid) {
debug ("remove process %d from model".printf (pid));
// if process rows has pid
if (process_rows.has_key (pid)) {
var cached_iter = process_rows.get (pid);
remove (ref cached_iter);
uint pos;
var process_row = process_rows.get (pid);
if (store.find (process_row, out pos)) {
store.remove (pos);
}
process_rows.unset (pid);
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/Monitor.vala
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ namespace Monitor {
}

settings.bind ("is-maximized", window, "maximized", SettingsBindFlags.SET);

window.process_view.process_tree_view.focus_on_first_row ();
}

public static int main (string[] args) {
Expand Down
Loading
Loading