Skip to content
2 changes: 1 addition & 1 deletion src/Services/Document.vala
Original file line number Diff line number Diff line change
Expand Up @@ -1315,7 +1315,7 @@ namespace Scratch.Services {
/* Pull the buffer into an array and then work out which parts are to be deleted.
* Do not strip line currently being edited unless forced */
private void strip_trailing_spaces () {
if (!loaded || source_view.language == null) {
if (!loaded || source_view.language == null || source_view.buffer.has_selection) {
return;
}

Expand Down
3 changes: 2 additions & 1 deletion src/Widgets/NavMarkGutterRenderer.vala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class Scratch.Widgets.NavMarkGutterRenderer : Gtk.SourceGutterRendererPix
public Gtk.TextBuffer buffer { get; construct; }
public bool has_marks {
get {
purge_and_sort_mark_list ();
return mark_list.size > 0;
}
}
Expand Down Expand Up @@ -101,6 +100,8 @@ public class Scratch.Widgets.NavMarkGutterRenderer : Gtk.SourceGutterRendererPix
}

sorted_line_list.sort ();

notify_property ("has-marks");
}

public void delete_mark_at_line (int line) {
Expand Down
113 changes: 63 additions & 50 deletions src/Widgets/SourceView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace Scratch.Widgets {

public GLib.File location { get; set; }
public FolderManager.ProjectFolderItem project { get; set; default = null; }
public SimpleActionGroup actions { get; construct; }

private string font;
private uint selection_changed_timer = 0;
Expand All @@ -55,6 +56,7 @@ namespace Scratch.Widgets {
set {
((Gtk.SourceBuffer) buffer).language = value;
}

get {
return ((Gtk.SourceBuffer) buffer).language;
}
Expand Down Expand Up @@ -192,7 +194,67 @@ namespace Scratch.Widgets {
}
});

populate_popup.connect_after (on_context_menu);
// Actions and menumodel for additional context menu items
var sort_action = new SimpleAction ("sort-lines", null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like we already have actions for these that we were using previously. Is there a reason to create new actions instead of reusing the existing ones?

@jeremypw jeremypw Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I am in the process of removing those actions and associated functions from MainWindow. It makes more sense to handle them locally, especially in Gtk4.

Note that this pattern has already been used for the Terminal pane and Sidebar for example.

var mark_action = new SimpleAction ("mark", null);
var next_mark_action = new SimpleAction ("next-mark", null);
var prev_mark_action = new SimpleAction ("prev-mark", null);
var toggle_comment_action = new SimpleAction ("toggle-comment", null);

actions = new SimpleActionGroup ();
actions.add_action (sort_action);
actions.add_action (mark_action);
actions.add_action (next_mark_action);
actions.add_action (prev_mark_action);
actions.add_action (toggle_comment_action);

insert_action_group ("sourceview", actions);
sort_action.activate.connect (sort_selected_lines);
mark_action.activate.connect (add_mark_at_cursor);
next_mark_action.activate.connect (goto_next_mark);
prev_mark_action.activate.connect (goto_previous_mark);
toggle_comment_action.activate.connect (() => {
CommentToggler.toggle_comment (buffer as Gtk.SourceBuffer);
});

var extra_menu = new Menu ();
extra_menu.append (_("Sort Lines"), "sort-lines");
extra_menu.append (_("Mark Line"), "mark");
extra_menu.append (_("Previous Mark"), "next-mark");
extra_menu.append (_("Next Mark"), "prev-mark");
extra_menu.append (_("Toggle Comment"), "toggle-comment");

// enable/disable action depending on changes to language, selection, marks in document
buffer.notify["has-selection"].connect (() => {
sort_action.set_enabled (buffer.has_selection);
});
buffer.notify["language"].connect (() => {
toggle_comment_action.set_enabled (CommentToggler.language_has_comments (((Gtk.SourceBuffer)buffer).language));
});
buffer.notify_property ("has-selection");
buffer.notify_property ("language");

navmark_gutter_renderer.notify["has-marks"].connect (() => {
next_mark_action.set_enabled (navmark_gutter_renderer.has_marks);
prev_mark_action.set_enabled (next_mark_action.get_enabled ());
});
navmark_gutter_renderer.notify_property ("has-marks");

// For Gtk3 we need to convert extra_menu to additional Gtk.MenuItems. This is omitted in Gtk4
populate_popup.connect_after ((menu) => {
scroll_mark_onscreen (buffer.get_mark ("insert")); //TODO Check if still needed in Gtk4
for (int i = 0; i < extra_menu.get_n_items (); i++) {
var name = extra_menu.get_item_attribute_value (i, "label", VariantType.STRING).get_string ();
var action = extra_menu.get_item_attribute_value (i, "action", VariantType.STRING).get_string ();
// warning ("adding menuitem name %s, action_name %s", name, action);
menu.add (
new Gtk.MenuItem.with_label (name) {
action_name = "sourceview." + action
}
);
}
menu.show_all ();
});

size_allocate.connect ((allocation) => {
// Throttle for performance
Expand Down Expand Up @@ -588,55 +650,6 @@ namespace Scratch.Widgets {
}
}

private void on_context_menu (Gtk.Menu menu) {
scroll_mark_onscreen (buffer.get_mark ("insert"));

var sort_item = new Gtk.MenuItem.with_label (_("Sort Lines")) {
action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_SORT_LINES
};

var add_edit_item = new Gtk.MenuItem.with_label (_("Mark Line")) {
action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_ADD_MARK
};

var previous_edit_item = new Gtk.MenuItem.with_label (_("Previous Mark")) {
action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_PREVIOUS_MARK
};

var next_edit_item = new Gtk.MenuItem.with_label (_("Next Mark")) {
action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_NEXT_MARK
};

menu.add (sort_item);
menu.add (add_edit_item);
menu.add (previous_edit_item);
menu.add (next_edit_item);

if (buffer is Gtk.SourceBuffer) {
var comment_item = new Gtk.MenuItem.with_label (_("Toggle Comment")) {
action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_TOGGLE_COMMENT
};

var can_comment = CommentToggler.language_has_comments (((Gtk.SourceBuffer) buffer).get_language ());
if (!can_comment) {
comment_item.action_name = "";
}

menu.add (comment_item);
}

menu.show_all ();

if (!(get_selected_line_count () > 1)) {
sort_item.action_name = "";
}

if (!navmark_gutter_renderer.has_marks) {
previous_edit_item.action_name = "";
next_edit_item.action_name = "";
}
}

private static int calculate_bottom_margin (int height_in_px) {
const int LINES_TO_KEEP = 3;
const double PT_TO_PX = 1.6667; // Normally 1.3333, but this accounts for line-height
Expand Down