Saving The Content To A File
In this lesson you will learn how to add a menu entry with a key shortcut, ask the user to select a file for saving the Gtk::TextBuffer
contents, and save a file.
Add the “Save As” menu item
- Open the UI definition file for your window and find the
primary_menu
menu definition at the bottom of the file - Remove the “Preferences” menu item, as we are not going to need it
- In place of the removed menu item, add the definition of the Save As menu item:
xml
<menu id="primary_menu">
<section>
<item>
<attribute name="label" translatable="yes">_Save as...</attribute>
<attribute name="action">win.save-as</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">win.show-help-overlay</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About {{name}}</attribute>
<attribute name="action">app.about</attribute>
</item>
</section>
</menu>
The “Save as” menu item is bound to the win.save-as
action; this means that activating the menu item will activate the save-as
action registered on the Text::Viewer::Window
window.
Add the “Save As” action
- Open the
window.cr
file, and find the class constructor of theText::Viewer::Window
widget - Create the
save-as
action, connect a callback to itsactivate
signal, and add the action to the window
crystal
def initialize
super()
@header_bar = Adw::HeaderBar.cast(template_child("header_bar"))
@main_text_view = Gtk::TextView.cast(template_child("main_text_view"))
@open_button = Gtk::Button.cast(template_child("open_button"))
@cursor_pos = Gtk::Label.cast(template_child("cursor_pos"))
open_action = Gio::SimpleAction.new("open", nil)
open_action.activate_signal.connect do
self.open_file_dialog
end
self.add_action open_action
save_action = Gio::SimpleAction.new("save-as", nil)
save_action.activate_signal.connect do
self.save_file_dialog
end
self.add_action save_action
@main_text_view.buffer.notify_signal["cursor-position"].connect do
buffer = @main_text_view.buffer
cursor_position = buffer.cursor_position
iter = buffer.iter_at_offset(cursor_position)
@cursor_pos.label = "Ln #{iter.line}, Col #{iter.line_offset}"
end
end
Select a file
- In the
activate
callback method for thesave-as
action, create a file selection dialog using theGtk::FileChooserAction::Save
action, and connect to itsresponse
signal
crystal
private def save_file_dialog
filechooser = Gtk::FileChooserNative.new("Save File As", nil, Gtk::FileChooserAction::Save, "_Save", "_Cancel")
filechooser.transient_for = self
filechooser.response_signal.connect do |response|
end
filechooser.show
end
- In the method, retrieve the
Gio::File
for the location selected by the user, and call the save_file method
crystal
private def save_file_dialog
filechooser = Gtk::FileChooserNative.new("Save File As", nil, Gtk::FileChooserAction::Save, "_Save", "_Cancel")
filechooser.transient_for = self
filechooser.response_signal.connect do |response|
if Gtk::ResponseType.from_value(response) == Gtk::ResponseType::Accept
self.save_file(filechooser.file)
end
end
filechooser.show
end
Save the contents of the text buffer
In the save_file
method, retrieve the contents of the Gtk::TextBuffer
using the start and end Gtk::TextIter
as the bounds of the buffer, then save it to the Gio::File
's Path
using Crystal's File IO
crystal
private def save_file(file : Gio::File?)
return if file.nil?
file_path = file.not_nil!.path.not_nil!
File.open(file_path, "w") do |file_io|
buffer = @main_text_view.buffer
# Retrieve the iterator at the start of the buffer
start_iter = buffer.start_iter
# Retrieve the iterator at the end of the buffer
end_iter = buffer.end_iter
# Retrieve all the visible text between the two bounds
text = buffer.text(start_iter, end_iter, false)
return if text.size == 0
file_io.print(text)
end
end
Add a key shortcut for the “Save As” action
- Open the
text-viewer.cr
source file and find the constructor for theText::Viewer::App
class - Add Ctrl + Shift + S as the accelerator shortcut for the
win.save-as
action
crystal
#...
class App < Adw::Application
def initialize
#...
self.set_accels_for_action("app.quit", {"<primary>q"})
self.set_accels_for_action("win.open", {"<Ctrl>o"})
self.set_accels_for_action("win.save-as", {"<Ctrl><Shift>s"})
#...
end
#...
Add the “Save As” shortcut to the Keyboard Shortcuts help
- Find the
help-overlay.ui
file in the sources directory - Find the
GtkShortcutsGroup
definition - Add a new
GtkShortcutsShortcut
definition for thewin.save
action in the shortcuts group
xml
<object class="GtkShortcutsGroup">
<property name="title" translatable="yes" context="shortcut window">General</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes" context="shortcut window">Open</property>
<property name="action-name">win.open</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes" context="shortcut window">Save As</property>
<property name="action-name">win.save-as</property>
</object>
</child>
At the end of this lesson, you should be able to:
- select the “Save As” menu item from the primary menu
- select a file from a dialog
- save the contents of the text viewer in the selected file