diff --git a/data/Application.css b/data/Application.css
index 749802e..ff4d984 100644
--- a/data/Application.css
+++ b/data/Application.css
@@ -13,14 +13,14 @@
}
/* Window and titlebar inactive */
-.lyrics:backdrop,
-.lyrics:backdrop .titlebar {
+.lyrics:backdrop.translucid-backdrop,
+.lyrics:backdrop.translucid-backdrop .titlebar {
color: #0c0c0c;
background: rgba(255, 255, 255, 0.25);
}
-.lyrics.dark:backdrop,
-.lyrics.dark:backdrop .titlebar {
+.lyrics.dark:backdrop.translucid-backdrop,
+.lyrics.dark:backdrop.translucid-backdrop .titlebar {
color: #fff;
background: rgba(0, 0, 0, 0.5);
}
diff --git a/data/com.github.naaando.lyrics.gschema.xml b/data/com.github.naaando.lyrics.gschema.xml
index c7a45ec..7ac0a11 100644
--- a/data/com.github.naaando.lyrics.gschema.xml
+++ b/data/com.github.naaando.lyrics.gschema.xml
@@ -16,5 +16,22 @@
Most recent y position
Most recent y position
+
+
+
+
+
+
+ 'When playing'
+ Keep the window on the top
+
+
+ true
+ Set window's opacity low when it goes out of focus
+
+
+ ''
+ Location to download and search for lyrics
+
diff --git a/meson.build b/meson.build
index bb26a18..61d81bf 100644
--- a/meson.build
+++ b/meson.build
@@ -17,7 +17,6 @@ executable(
meson.project_name (),
asresources,
'src/Controller/DisplayController.vala',
- 'src/Controller/StackController.vala',
'src/Core/Factory/MetasongFactory.vala',
'src/Core/Lyric.vala',
'src/Core/Metasong.vala',
@@ -35,14 +34,18 @@ executable(
'src/Mpris/MprisPlayer.vala',
'src/Mpris/MprisService.vala',
'src/Parser/LRC-Parser.vala',
+ 'src/View/HeaderBar.vala',
+ 'src/View/MainStack.vala',
+ 'src/View/MainWindow.vala',
+ 'src/View/SettingsPopover.vala',
'src/Widgets/Displays/IDisplay.vala',
'src/Widgets/Displays/SimpleDisplay.vala',
'src/Widgets/Displays/ScrolledDisplay.vala',
'src/Widgets/Download.vala',
+ 'src/Widgets/FileChooserButton.vala',
'src/Widgets/ModeSwitch.vala',
'src/Widgets/SearchLyric.vala',
'src/Application.vala',
- 'src/MainWindow.vala',
'src/SimplePlayer.vala',
dependencies: [
dependency ('glib-2.0'),
diff --git a/src/Application.vala b/src/Application.vala
index 8b93eeb..66c4bf7 100644
--- a/src/Application.vala
+++ b/src/Application.vala
@@ -1,14 +1,14 @@
public class Lyrics.Application : Gtk.Application {
- public static GLib.Settings settings;
+ public static string DEFAULT_LYRICS_DIR = Environment.get_home_dir ()+"/.lyrics/";
+ public static GLib.Settings settings = new Settings ("com.github.naaando.lyrics");
public Application () {
Object (application_id: "com.github.naaando.lyrics",
flags: ApplicationFlags.FLAGS_NONE);
- }
-
- static construct {
- settings = new Settings ("com.github.naaando.lyrics");
+ if (settings.get_string ("download-location") == "") {
+ settings.set_string ("download-location", DEFAULT_LYRICS_DIR);
+ }
}
protected override void activate () {
@@ -19,15 +19,12 @@ public class Lyrics.Application : Gtk.Application {
var players = new Players ();
- var stack_controller = new Controller.StackController (players);
-
var scanner = new Mpris.Service ();
scanner.found.connect ((player) => players.add (player));
scanner.lost.connect (players.remove_by_busname);
scanner.setup_dbus ();
- var main_window = new MainWindow (this, stack_controller.get_stack ());
- main_window.players = players;
+ var main_window = new MainWindow (this, players, new MainStack (players));
var window_x = settings.get_int ("window-x");
var window_y = settings.get_int ("window-y");
diff --git a/src/Controller/StackController.vala b/src/Controller/StackController.vala
deleted file mode 100644
index 2312da3..0000000
--- a/src/Controller/StackController.vala
+++ /dev/null
@@ -1,102 +0,0 @@
-
-public class Lyrics.Controller.StackController : Object {
- Players players;
- Gtk.Stack stack;
- DisplayController display_controller = new DisplayController ();
-
- public StackController (Players _players) {
- var download = new Download ();
- stack = factory_gtk_stack (display_controller.display, download);
-
- players = _players;
-
- // FIXME: Signal not working outside thread
- new Thread ("players-signals",() => {
- var loop = new MainLoop ();
- players.notify["active-player"].connect (() => {
- if (players.active_player != null) {
- players.active_player.notify.connect (() => {
- update_stack ();
- });
- }
- update_stack ();
- });
-
- loop.run ();
-
- return 0;
- });
- }
-
- public void update_stack () {
- if (players.active_player == null) {
- stack.visible_child_name = "NO_PLAYER";
- return;
- }
-
- if (players.active_player.state.to_string () == "PLAYING") {
- display_controller.start (players.active_player) ? stack.visible_child_name = players.active_player.state.to_string () : stack.visible_child_name = "NO_LYRICS";
- } else {
- display_controller.stop ();
- stack.visible_child_name = "STOPPED";
- }
- }
-
- public Gtk.Stack get_stack () {
- return stack;
- }
-
- private Gtk.Stack factory_gtk_stack (IDisplay display, Download download) {
- var stack = new Gtk.Stack ();
- stack.transition_type = Gtk.StackTransitionType.CROSSFADE;
- stack.border_width = 24;
- stack.margin = 6;
- stack.expand = true;
-
- var context = stack.get_style_context ();
- context.add_class ("stack");
-
- stack.add_named (build_no_player (), "NO_PLAYER");
- stack.add_named (build_not_playing (), "STOPPED");
- stack.add_named (display_controller.display, "PLAYING");
- stack.add_named (download, "DOWNLOADING");
- stack.add_named (build_no_lyrics (), "NO_LYRICS");
- return stack;
- }
-
- private Gtk.Box build_no_player () {
- var label = new Gtk.Label ("Couldn't find any player, check your player's MPRIS configuration");
- label.wrap = true;
- label.get_style_context ().add_class ("description");
-
- var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
- box.valign = Gtk.Align.CENTER;
- box.add (label);
-
- return box;
- }
-
- private Gtk.Box build_not_playing () {
- var label = new Gtk.Label ("Currently not playing");
- label.wrap = true;
- label.get_style_context ().add_class ("description");
-
- var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
- box.valign = Gtk.Align.CENTER;
- box.add (label);
-
- return box;
- }
-
- private Gtk.Box build_no_lyrics () {
- var label = new Gtk.Label ("Couldn't find lyrics for song");
- label.wrap = true;
- label.get_style_context ().add_class ("description");
-
- var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
- box.valign = Gtk.Align.CENTER;
- box.add (label);
-
- return box;
- }
-}
diff --git a/src/MainWindow.vala b/src/MainWindow.vala
deleted file mode 100644
index 0a0b074..0000000
--- a/src/MainWindow.vala
+++ /dev/null
@@ -1,156 +0,0 @@
-
-public class Lyrics.MainWindow : Gtk.ApplicationWindow {
- public Players players {
- get {
- return _players;
- }
- set {
- _players = value;
- connect_to_playpause (players);
- }
- }
-
- Player active_player {
- get {
- return (players == null) ? null : players.active_player;
- }
- }
-
- Players _players;
- Gtk.Button playpause;
-
- public MainWindow (Gtk.Application application, Gtk.Stack stack) {
- Object (
- application: application,
- icon_name: "com.github.naaando.lyrics",
- resizable: true,
- title: _("Lyrics"),
- window_position: Gtk.WindowPosition.CENTER
- );
-
- var context = get_style_context ();
- context.add_class ("rounded");
- context.add_class ("lyrics");
-
- var provider = new Gtk.CssProvider ();
- provider.load_from_resource ("/com/github/naaando/lyrics/Application.css");
- Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
-
- set_titlebar (generate_header ());
- set_keep_above (true);
- stick ();
-
- add (stack);
- }
-
- Gtk.HeaderBar generate_header () {
- var header = new Gtk.HeaderBar ();
- header.decoration_layout = "close:menu";
- header.show_close_button = true;
- var header_context = header.get_style_context ();
- header_context.add_class ("titlebar");
- header_context.add_class ("default-decoration");
-
- var mode_switch = new ModeSwitch (
- "display-brightness-symbolic",
- "weather-clear-night-symbolic"
- );
- mode_switch.margin_end = 6;
- mode_switch.primary_icon_tooltip_text = _("Light background");
- mode_switch.secondary_icon_tooltip_text = _("Dark background");
- mode_switch.valign = Gtk.Align.CENTER;
-
- var gtk_settings = Gtk.Settings.get_default ();
- mode_switch.bind_property ("active", gtk_settings, "gtk_application_prefer_dark_theme");
- mode_switch.notify["active"].connect (() => {
- var context = get_style_context ();
- if (gtk_settings.gtk_application_prefer_dark_theme) {
- context.add_class ("dark");
- } else {
- context.remove_class ("dark");
- }
- });
-
- Application.settings.bind ("dark", mode_switch, "active", GLib.SettingsBindFlags.DEFAULT);
-
- /**
- * Left side of headerbar
- */
-
- /* Player controls */
- // Previous music button
- header.pack_start (build_button_from_icon ("media-skip-backward-symbolic", null, (btn) => {
- if (active_player != null) {
- active_player.previous ();
- }
- }));
-
- // Play/Pause button
- playpause = build_button_from_icon ("media-playback-start-symbolic", null, (btn) => {
- if (active_player != null) {
- active_player.toggle_play_pause ();
- }
- });
- header.pack_start (playpause);
-
- // Next music button
- header.pack_start (build_button_from_icon ("media-skip-forward-symbolic", null, (btn) => {
- if (active_player != null) {
- active_player.next ();
- }
- }));
-
-
- /**
- * Right side of headerbar
- */
-
- // Switch color button
- header.pack_end (mode_switch);
- return header;
- }
-
- Gtk.Button build_button_from_icon (string icon_name, string? tooltip = null, Func? clicked_cb = null) {
- var button = new Gtk.Button.from_icon_name (icon_name);
-
- if (tooltip != null) {
- button.tooltip_text = tooltip;
- }
-
- if (clicked_cb != null) {
- button.clicked.connect (() => clicked_cb (button));
- }
-
- return button;
- }
-
- void connect_to_playpause (Players plrs) {
- update_playpause_icon (plrs);
-
- // Update play/pause button
- plrs.notify["active-player"].connect (() => {
- if (plrs.active_player == null) {
- return;
- }
-
- update_playpause_icon (plrs);
- plrs.active_player.notify.connect (() => {
- update_playpause_icon (plrs);
- });
- });
- }
-
- void update_playpause_icon (Players plrs) {
- var path = (plrs.active_player != null && plrs.active_player.state != PAUSED) ? "media-playback-pause-symbolic" : "media-playback-start-symbolic";
- playpause.image = new Gtk.Image.from_icon_name (path, Gtk.IconSize.SMALL_TOOLBAR);
- }
-
- public override bool configure_event (Gdk.EventConfigure event) {
- int root_x, root_y;
- get_position (out root_x, out root_y);
- Application.settings.set_int ("window-x", root_x);
- Application.settings.set_int ("window-y", root_y);
-
- return base.configure_event (event);
- }
-}
diff --git a/src/Repository/Local/LocalRepository.vala b/src/Repository/Local/LocalRepository.vala
index eafcd58..5b24615 100644
--- a/src/Repository/Local/LocalRepository.vala
+++ b/src/Repository/Local/LocalRepository.vala
@@ -1,6 +1,6 @@
public class Lyrics.LocalRepository : IRepository, Object {
- string local_storage = Environment.get_home_dir ()+"/.lyrics/";
+ public string local_storage { get; set; default = Environment.get_home_dir ()+"/.lyrics/"; }
public bool save (Metasong song, ILyricFile lyric_file) {
var file = File.new_for_path (local_storage+get_filename_for_song (song));
diff --git a/src/Repository/Repository.vala b/src/Repository/Repository.vala
index 0b55790..cbe3a83 100644
--- a/src/Repository/Repository.vala
+++ b/src/Repository/Repository.vala
@@ -8,6 +8,9 @@ public class Lyrics.Repository : IRepository, Object {
public Repository () {
lyricsources["viewlyrics"] = new LyricSources.Repository (viewlyrics_dbus[0], viewlyrics_dbus[1]);
+
+ Application.settings.changed["download-location"].connect (configure_download_local);
+ configure_download_local ();
}
public ILyricFile? find_first (Metasong song) {
@@ -31,4 +34,10 @@ public class Lyrics.Repository : IRepository, Object {
collection.add_all (lyricsources["viewlyrics"].find (song));
return collection;
}
+
+ void configure_download_local () {
+ var settings_location = Application.settings.get_string ("download-location");
+ local_repository.local_storage = settings_location == "" ? Environment.get_home_dir ()+"/.lyrics/" : settings_location+"/";
+ debug ("Setting local storage to " + local_repository.local_storage);
+ }
}
diff --git a/src/View/HeaderBar.vala b/src/View/HeaderBar.vala
new file mode 100644
index 0000000..6e214e8
--- /dev/null
+++ b/src/View/HeaderBar.vala
@@ -0,0 +1,83 @@
+public class Lyrics.HeaderBar : Gtk.HeaderBar {
+ Gtk.Settings settings = Gtk.Settings.get_default ();
+ Gtk.Button play_n_pause_btn;
+ Players players;
+
+ public HeaderBar (Players players) {
+ decoration_layout = "close:menu";
+ show_close_button = true;
+ get_style_context ().add_class ("titlebar");
+ get_style_context ().add_class ("default-decoration");
+ players.notify["active-player"].connect (on_active_player_changes);
+
+ var previous_btn = new Gtk.Button.from_icon_name ("media-skip-backward-symbolic");
+ previous_btn.clicked.connect (on_previous_btn_clicked);
+
+ play_n_pause_btn = new Gtk.Button.from_icon_name ("media-playback-start-symbolic");
+ play_n_pause_btn.clicked.connect (on_play_n_pause_btn_clicked);
+
+ var next_btn = new Gtk.Button.from_icon_name ("media-skip-forward-symbolic");
+ next_btn.clicked.connect (on_next_btn_clicked);
+
+ var settings = new Gtk.MenuButton ();
+ settings.image = new Gtk.Image.from_icon_name ("open-menu-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
+ settings.popover = new SettingsPopover ();
+
+ pack_start (previous_btn);
+ pack_start (play_n_pause_btn);
+ pack_start (next_btn);
+
+ pack_end (settings);
+ pack_end (create_mode_switch ());
+ }
+
+ void update_play_n_pause_icon () {
+ var icon_name = (players.active_player == null || players.active_player.state != PAUSED) ? "media-playback-start-symbolic" : "media-playback-pause-symbolic";
+ if (play_n_pause_btn != null) {
+ play_n_pause_btn.image = new Gtk.Image.from_icon_name (icon_name, Gtk.IconSize.SMALL_TOOLBAR);
+ }
+ }
+
+ void on_active_player_changes () {
+ if (players == null | players.active_player == null) {
+ return;
+ }
+
+ update_play_n_pause_icon ();
+ players.active_player.notify.connect (update_play_n_pause_icon);
+ }
+
+ void on_previous_btn_clicked () {
+ if (players.active_player != null) {
+ players.active_player.previous ();
+ }
+ }
+
+ void on_play_n_pause_btn_clicked () {
+ if (players.active_player != null) {
+ players.active_player.toggle_play_pause ();
+ }
+ }
+
+ void on_next_btn_clicked () {
+ if (players.active_player != null) {
+ players.active_player.next ();
+ }
+ }
+
+ Gtk.Widget create_mode_switch () {
+ var mode_switch = new ModeSwitch (
+ "display-brightness-symbolic",
+ "weather-clear-night-symbolic"
+ );
+
+ mode_switch.margin_end = 6;
+ mode_switch.primary_icon_tooltip_text = _("Light background");
+ mode_switch.secondary_icon_tooltip_text = _("Dark background");
+ mode_switch.valign = Gtk.Align.CENTER;
+ mode_switch.bind_property ("active", settings, "gtk_application_prefer_dark_theme");
+ Application.settings.bind ("dark", mode_switch, "active", GLib.SettingsBindFlags.DEFAULT);
+
+ return mode_switch;
+ }
+}
diff --git a/src/View/MainStack.vala b/src/View/MainStack.vala
new file mode 100644
index 0000000..c1596ae
--- /dev/null
+++ b/src/View/MainStack.vala
@@ -0,0 +1,92 @@
+public class Lyrics.MainStack : Gtk.Stack {
+ public Gtk.Widget no_player_view { get;set; default = MainStack.build_no_player (); }
+ public Gtk.Widget not_playing_view { get; set; default = MainStack.build_not_playing (); }
+ public Gtk.Widget lyrics_not_found_view { get; set; default = MainStack.build_no_lyrics (); }
+
+ public Gtk.Widget? lyrics_display { get; set; }
+ public Gtk.Widget? downloading_view { get; set; }
+ Controller.DisplayController display_controller = new Controller.DisplayController ();
+ Players players;
+
+ public MainStack (Players _players) {
+ transition_type = Gtk.StackTransitionType.CROSSFADE;
+ border_width = 24;
+ margin = 6;
+ expand = true;
+
+ get_style_context ().add_class ("stack");
+
+ lyrics_display = display_controller.display;
+ downloading_view = new Download ();
+
+ add_named (no_player_view, "NO_PLAYER");
+ add_named (not_playing_view, "STOPPED");
+ add_named (lyrics_display, "PLAYING");
+ add_named (downloading_view, "DOWNLOADING");
+ add_named (lyrics_not_found_view, "NO_LYRICS");
+
+ players = _players;
+
+ players.notify["active-player"].connect (on_active_player_change);
+ }
+
+ void on_active_player_change () {
+ if (players.active_player != null) {
+ players.active_player.notify.connect (() => {
+ update_stack ();
+ });
+ }
+
+ update_stack ();
+ }
+
+ public void update_stack () {
+ if (players.active_player == null) {
+ visible_child_name = "NO_PLAYER";
+ return;
+ }
+
+ if (players.active_player.state.to_string () == "PLAYING") {
+ display_controller.start (players.active_player) ? visible_child_name = players.active_player.state.to_string () : visible_child_name = "NO_LYRICS";
+ } else {
+ display_controller.stop ();
+ visible_child_name = "STOPPED";
+ }
+ }
+
+ static Gtk.Box build_no_player () {
+ var label = new Gtk.Label ("Couldn't find any player, check your player's MPRIS configuration");
+ label.wrap = true;
+ label.get_style_context ().add_class ("description");
+
+ var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ box.valign = Gtk.Align.CENTER;
+ box.add (label);
+
+ return box;
+ }
+
+ static Gtk.Box build_not_playing () {
+ var label = new Gtk.Label ("Currently not playing");
+ label.wrap = true;
+ label.get_style_context ().add_class ("description");
+
+ var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ box.valign = Gtk.Align.CENTER;
+ box.add (label);
+
+ return box;
+ }
+
+ static Gtk.Box build_no_lyrics () {
+ var label = new Gtk.Label ("Couldn't find lyrics for song");
+ label.wrap = true;
+ label.get_style_context ().add_class ("description");
+
+ var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ box.valign = Gtk.Align.CENTER;
+ box.add (label);
+
+ return box;
+ }
+}
diff --git a/src/View/MainWindow.vala b/src/View/MainWindow.vala
new file mode 100644
index 0000000..cce2949
--- /dev/null
+++ b/src/View/MainWindow.vala
@@ -0,0 +1,99 @@
+
+public class Lyrics.MainWindow : Gtk.ApplicationWindow {
+ Gtk.Stack main_stack;
+ Players players;
+ bool keep_above_when_playing;
+
+ public MainWindow (Gtk.Application application, Players _players, Gtk.Stack stack) {
+ Object (
+ application: application,
+ icon_name: "com.github.naaando.lyrics",
+ resizable: true,
+ title: _("Lyrics"),
+ window_position: Gtk.WindowPosition.CENTER
+ );
+
+ // Add css classes to main window
+ get_style_context ().add_class ("rounded");
+ get_style_context ().add_class ("lyrics");
+
+ main_stack = stack;
+ players = _players;
+
+ set_titlebar (new Lyrics.HeaderBar (players));
+
+ Application.settings.changed["window-keep-above"].connect (configure_window_keep_above_settings);
+ Application.settings.changed["window-out-of-focus-translucid"].connect (configure_window_opacity_on_focus_loss);
+ main_stack.notify["visible-child-name"].connect (on_stack_visible_child_changed);
+
+ add (main_stack);
+ setup ();
+ }
+
+ public override bool configure_event (Gdk.EventConfigure event) {
+ int root_x, root_y;
+ get_position (out root_x, out root_y);
+ Application.settings.set_int ("window-x", root_x);
+ Application.settings.set_int ("window-y", root_y);
+
+ return base.configure_event (event);
+ }
+
+ void setup () {
+ configure_dark_theme ();
+ configure_css_provider ();
+ configure_window_keep_above_settings ();
+ configure_window_opacity_on_focus_loss ();
+ stick ();
+ }
+
+ void configure_css_provider () {
+ var provider = new Gtk.CssProvider ();
+ provider.load_from_resource ("/com/github/naaando/lyrics/Application.css");
+ Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+
+ void configure_dark_theme () {
+ update_dark_mode ();
+ Application.settings.changed["dark"].connect (update_dark_mode);
+ }
+
+ void update_dark_mode () {
+ if (Application.settings.get_boolean ("dark")) {
+ get_style_context ().add_class ("dark");
+ } else {
+ get_style_context ().remove_class ("dark");
+ }
+ }
+
+ void configure_window_keep_above_settings () {
+ switch (Application.settings.get_string ("window-keep-above")) {
+ case "Always":
+ set_keep_above (true);
+ keep_above_when_playing = false;
+ break;
+ case "When playing":
+ keep_above_when_playing = true;
+ set_keep_above (main_stack.visible_child_name == "PLAYING" ? true : false);
+ break;
+ case "Never keep above":
+ set_keep_above (false);
+ keep_above_when_playing = false;
+ break;
+ }
+ }
+
+ void configure_window_opacity_on_focus_loss () {
+ if (Application.settings.get_boolean ("window-out-of-focus-translucid")) {
+ get_style_context ().add_class ("translucid-backdrop");
+ } else {
+ get_style_context ().remove_class ("translucid-backdrop");
+ }
+ }
+
+ void on_stack_visible_child_changed () {
+ if (keep_above_when_playing) {
+ set_keep_above (main_stack.visible_child_name == "PLAYING" ? true : false);
+ }
+ }
+}
diff --git a/src/View/SettingsPopover.vala b/src/View/SettingsPopover.vala
new file mode 100644
index 0000000..6d422ac
--- /dev/null
+++ b/src/View/SettingsPopover.vala
@@ -0,0 +1,70 @@
+public class Lyrics.SettingsPopover : Gtk.Popover {
+ FileChooserButton folder_chooser_button;
+ Gtk.ComboBox combobox;
+ Gtk.Switch opacity_switch;
+
+ public SettingsPopover () {
+ var grid = new Gtk.Grid ();
+ grid.margin = 12;
+ grid.row_spacing = grid.column_spacing = 12;
+
+ var lyrics_folder_label = new Gtk.Label ("Lyrics download folder:");
+ folder_chooser_button = new FileChooserButton (_("Select folder to download lyrics"), Gtk.FileChooserAction.SELECT_FOLDER);
+ try {
+ folder_chooser_button.add_shortcut_folder (Application.DEFAULT_LYRICS_DIR);
+ } catch (Error e) {
+ info ("Couldn't add default lyrics folder to FileChooserButton " + e.message);
+ }
+
+ var window_behavior_label = new Gtk.Label ("Keep window above:");
+ window_behavior_label.halign = Gtk.Align.START;
+
+ combobox = create_combobox ();
+ var reset_default_button = create_reset_button ();
+
+ grid.attach (lyrics_folder_label, 0, 0);
+ grid.attach (folder_chooser_button, 1, 0);
+ grid.attach (window_behavior_label, 0, 1);
+ grid.attach (combobox, 1, 1);
+ grid.attach (create_translucid_switch (), 0, 2, 2);
+ grid.attach (reset_default_button, 0, 3, 2);
+ grid.show_all ();
+
+ add (grid);
+
+ Application.settings.bind ("download-location", folder_chooser_button, "filename", GLib.SettingsBindFlags.DEFAULT);
+ Application.settings.bind ("window-keep-above", combobox, "active-id", GLib.SettingsBindFlags.DEFAULT);
+ Application.settings.bind ("window-out-of-focus-translucid", opacity_switch, "active", GLib.SettingsBindFlags.DEFAULT);
+ }
+
+ Gtk.ComboBox create_combobox () {
+ var combobox = new Gtk.ComboBoxText ();
+ combobox.append ("Always", "Always");
+ combobox.append ("When playing", "When playing");
+ combobox.append ("Never keep above", "Never keep above");
+
+ return combobox;
+ }
+
+ Gtk.Container create_translucid_switch () {
+ var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+ box.add (opacity_switch = new Gtk.Switch ());
+ box.add (new Gtk.Label ("Make translucid on focus loss"));
+ box.halign = Gtk.Align.START;
+
+ return box;
+ }
+
+ Gtk.Button create_reset_button () {
+ var btn = new Gtk.Button.with_label (_("Restore default settings"));
+ btn.halign = Gtk.Align.CENTER;
+ btn.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION);
+ btn.clicked.connect (() => {
+ Application.settings.set_string ("download-location", Application.DEFAULT_LYRICS_DIR);
+ Application.settings.reset ("window-keep-above");
+ Application.settings.reset ("window-out-of-focus-translucid");
+ });
+
+ return btn;
+ }
+}
diff --git a/src/Widgets/FileChooserButton.vala b/src/Widgets/FileChooserButton.vala
new file mode 100644
index 0000000..ff4ba5f
--- /dev/null
+++ b/src/Widgets/FileChooserButton.vala
@@ -0,0 +1,27 @@
+public class Lyrics.FileChooserButton : Gtk.FileChooserButton {
+ string? _filename;
+ public string filename {
+ get {
+ return _filename;
+ }
+ set {
+ if (value == null) {
+ return;
+ }
+
+ _filename = value;
+ set_filename (_filename);
+ }
+ }
+
+ public FileChooserButton (string _title, Gtk.FileChooserAction _action) {
+ title = _title;
+ action = _action;
+
+ selection_changed.connect (() => {
+ if (filename != get_filename()) {
+ filename = get_filename();
+ }
+ });
+ }
+}