Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 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
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
13 changes: 1 addition & 12 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,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 (() => {
Expand Down
162 changes: 130 additions & 32 deletions src/Models/TreeViewModel.vala
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,102 @@ public enum Monitor.Column {
CMD
}

public class Monitor.TreeViewModel : Gtk.TreeStore {
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 class Monitor.TreeViewFilter : GLib.Object {
private string _needle;
public string needle {
get {
return _needle;
}
set {
name_filter.search = value;
cmd_filter.search = value;
_needle = value;
}
}
public Gtk.FilterListModel model_out;
private Gtk.AnyFilter any_filter;
public Gtk.StringFilter name_filter;
public Gtk.StringFilter cmd_filter;
public Gtk.CustomFilter pid_filter;

public TreeViewFilter (GLib.ListModel? model) {
name_filter = build_str_filter ("name");
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
pid_filter = new Gtk.CustomFilter ((obj) => {
var item = (ProcessRowData) obj;
bool pid_found = item.pid.to_string ().contains (needle.casefold ()) || false;
return pid_found;
});

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);

}

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
};
}

}

public class Monitor.TreeViewModel : GLib.Object {

public ProcessManager process_manager;
private Gee.Map<int, Gtk.TreeIter ? > process_rows;

public TreeViewFilter filtered;
public Gtk.SingleSelection selection_model;

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 = selection_model.get_selected_item () as ProcessRowData;
Process process = process_manager.get_process (row_data.pid);
process_selected (process);
});

process_manager = ProcessManager.get_default ();
Expand All @@ -37,6 +118,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 +140,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 +164,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)) {
var item = store.get_item (pos) as ProcessRowData;
item.cpu = (int) process.cpu_percentage;
item.memory = process.mem_usage;
store.items_changed (pos, 1, 1);
} else {
debug ("Failed to find process row for pid %d", pid);
}
}
}

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
1 change: 0 additions & 1 deletion src/Monitor.vala
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,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