From c577bc12e4652d226a39c7b0b3ccb53e62e31280 Mon Sep 17 00:00:00 2001 From: tkashkin Date: Sun, 19 Jul 2020 20:20:44 +0300 Subject: [PATCH] New iteration of Tweak options UI (#355) --- res/styles/common/_style.css | 2 + res/styles/common/dialogs/SettingsDialog.css | 16 +- .../common/widgets/settings/SettingsGroup.css | 20 +- .../widgets/tweaks/TweakOptionsPopover.css | 28 + res/styles/styles.gresource.xml | 1 + src/meson.build | 5 +- .../GamePropertiesDialog/tabs/Overlays.vala | 4 +- src/ui/dialogs/GameTweaksDialog.vala | 1 + .../SettingsDialog/SettingsDialog.vala | 80 +-- .../pages/general/Collection.vala | 30 +- .../pages/general/Controller.vala | 56 +- .../SettingsDialog/pages/general/Tweaks.vala | 49 +- .../pages/providers/Providers.vala | 8 +- .../SettingsDialog/pages/ui/Appearance.vala | 2 +- src/ui/widgets/FileChooserEntry.vala | 4 +- src/ui/widgets/TweaksList.vala | 320 ----------- src/ui/widgets/settings/Settings.vala | 530 +++++++++--------- src/ui/widgets/settings/SettingsGroup.vala | 170 +++--- src/ui/widgets/settings/SettingsSidebar.vala | 20 +- .../widgets/tweaks/TweakOptionsPopover.vala | 343 ++++++++++++ src/ui/widgets/tweaks/TweakRow.vala | 177 ++++++ src/ui/widgets/tweaks/TweaksList.vala | 109 ++++ src/utils/Gamepad.vala | 14 +- src/utils/Utils.vala | 26 +- 24 files changed, 1179 insertions(+), 836 deletions(-) create mode 100644 res/styles/common/widgets/tweaks/TweakOptionsPopover.css delete mode 100644 src/ui/widgets/TweaksList.vala create mode 100644 src/ui/widgets/tweaks/TweakOptionsPopover.vala create mode 100644 src/ui/widgets/tweaks/TweakRow.vala create mode 100644 src/ui/widgets/tweaks/TweaksList.vala diff --git a/res/styles/common/_style.css b/res/styles/common/_style.css index edc6db3b..f4b4e832 100644 --- a/res/styles/common/_style.css +++ b/res/styles/common/_style.css @@ -6,3 +6,5 @@ @import url("views/GamesView.css"); @import url("widgets/settings/SettingsGroup.css"); + +@import url("widgets/tweaks/TweakOptionsPopover.css"); diff --git a/res/styles/common/dialogs/SettingsDialog.css b/res/styles/common/dialogs/SettingsDialog.css index a4e0338b..2443015f 100644 --- a/res/styles/common/dialogs/SettingsDialog.css +++ b/res/styles/common/dialogs/SettingsDialog.css @@ -10,50 +10,50 @@ .settings-dialog:dir(ltr) { background-image: linear-gradient(to right, - shade(@theme_bg_color, 0.95) 202px, + shade(@theme_bg_color, 0.95) 202px, @settings_separator 202px, @settings_separator 203px, transparent 203px, transparent); } .settings-dialog > headerbar:dir(ltr) { background-image: linear-gradient(to right, - alpha(shade(@theme_bg_color, 0.1), 0.05) 202px, + alpha(shade(@theme_bg_color, 0.1), 0.05) 202px, @settings_separator 202px, @settings_separator 203px, transparent 203px, transparent); } .settings-dialog:dir(rtl) { background-image: linear-gradient(to left, - shade(@theme_bg_color, 0.95) 202px, + shade(@theme_bg_color, 0.95) 202px, @settings_separator 202px, @settings_separator 203px, transparent 203px, transparent); } .settings-dialog > headerbar:dir(rtl) { background-image: linear-gradient(to left, - alpha(shade(@theme_bg_color, 0.1), 0.05) 202px, + alpha(shade(@theme_bg_color, 0.1), 0.05) 202px, @settings_separator 202px, @settings_separator 203px, transparent 203px, transparent); } .settings-dialog > headerbar > .vertical:dir(ltr) { - padding-left: 200px; + padding-left: 200px; } .settings-dialog > headerbar > .vertical:dir(rtl) { - padding-right: 200px; + padding-right: 200px; } .settings-dialog > headerbar switch { - margin: 0px; + margin: 0px; } .settings-dialog .settings-sidebar-row { - padding: 8px; + padding: 8px; } .settings-dialog .settings-pages-sidebar list diff --git a/res/styles/common/widgets/settings/SettingsGroup.css b/res/styles/common/widgets/settings/SettingsGroup.css index ff15cf87..27240056 100644 --- a/res/styles/common/widgets/settings/SettingsGroup.css +++ b/res/styles/common/widgets/settings/SettingsGroup.css @@ -1,6 +1,6 @@ .settings-group { - padding: 12px 18px; + padding: 12px 18px; } .settings-group > .title, @@ -11,22 +11,22 @@ } .settings-group-standalone-title { - margin-top: 12px; - margin-left: 18px; - margin-right: 18px; + margin-top: 12px; + margin-left: 18px; + margin-right: 18px; } .settings-group .setting:not(.custom-widget-setting) > box, .settings-group .setting:not(.custom-widget-setting) > grid, .settings-group .setting.label-setting > label { - padding: 6px 12px; - min-height: 36px; + padding: 6px 12px; + min-height: 36px; } .settings-group .setting .description { - font-size: 0.9em; + font-size: 0.9em; } .settings-group .setting.list-row, @@ -38,16 +38,16 @@ .settings-group .setting.list-row:last-child, .settings-group row.setting:last-child { - margin-bottom: -1px; + margin-bottom: -1px; } .settings-group .setting.provider-setting > box { - padding: 0; + padding: 0; } .settings-group .setting.provider-setting > box > grid { - padding: 6px 12px; + padding: 6px 12px; } .settings-group .setting.provider-setting .provider-settings { diff --git a/res/styles/common/widgets/tweaks/TweakOptionsPopover.css b/res/styles/common/widgets/tweaks/TweakOptionsPopover.css new file mode 100644 index 00000000..fc7e1f1b --- /dev/null +++ b/res/styles/common/widgets/tweaks/TweakOptionsPopover.css @@ -0,0 +1,28 @@ +.tweak-options-popover +{ + padding: 4px; +} + +.tweak-options-popover .list-title +{ + font-weight: bold; + padding: 4px 8px; + margin-bottom: 4px; +} + +.tweak-options-popover list +{ + background: transparent; +} + +.tweak-options-popover list row > box, +.tweak-options-popover list row > grid +{ + padding: 4px 8px; + min-height: 36px; +} + +.tweak-options-popover list row .description +{ + font-size: 0.9em; +} diff --git a/res/styles/styles.gresource.xml b/res/styles/styles.gresource.xml index 612c7c46..2aa9efac 100644 --- a/res/styles/styles.gresource.xml +++ b/res/styles/styles.gresource.xml @@ -7,6 +7,7 @@ common/dialogs/SettingsDialog.css common/views/GamesView.css common/widgets/settings/SettingsGroup.css + common/widgets/tweaks/TweakOptionsPopover.css diff --git a/src/meson.build b/src/meson.build index 1f5cd26b..27b8724c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -190,13 +190,16 @@ sources = [ 'ui/widgets/ModeButton.vala', 'ui/widgets/OverlayBar.vala', 'ui/widgets/Welcome.vala', - 'ui/widgets/TweaksList.vala', 'ui/widgets/DirectoriesList.vala', 'ui/widgets/settings/SettingsSidebar.vala', 'ui/widgets/settings/SettingsGroup.vala', 'ui/widgets/settings/Settings.vala', + 'ui/widgets/tweaks/TweaksList.vala', + 'ui/widgets/tweaks/TweakRow.vala', + 'ui/widgets/tweaks/TweakOptionsPopover.vala', + 'utils/Utils.vala', 'utils/fs/FS.vala', diff --git a/src/ui/dialogs/GamePropertiesDialog/tabs/Overlays.vala b/src/ui/dialogs/GamePropertiesDialog/tabs/Overlays.vala index 7e735ab3..d9f62e29 100644 --- a/src/ui/dialogs/GamePropertiesDialog/tabs/Overlays.vala +++ b/src/ui/dialogs/GamePropertiesDialog/tabs/Overlays.vala @@ -31,7 +31,7 @@ namespace GameHub.UI.Dialogs.GamePropertiesDialog.Tabs { public Traits.Game.SupportsOverlays game { get; construct; } - private Stack stack; + private Stack stack; private Box disabled_box; private AlertView disabled_alert; @@ -56,7 +56,7 @@ namespace GameHub.UI.Dialogs.GamePropertiesDialog.Tabs construct { - stack = new Stack(); + stack = new Stack(); stack.transition_type = StackTransitionType.CROSSFADE; content = new Box(Orientation.VERTICAL, 0); diff --git a/src/ui/dialogs/GameTweaksDialog.vala b/src/ui/dialogs/GameTweaksDialog.vala index 481260bd..41ec7db8 100644 --- a/src/ui/dialogs/GameTweaksDialog.vala +++ b/src/ui/dialogs/GameTweaksDialog.vala @@ -25,6 +25,7 @@ using GameHub.Data.DB; using GameHub.Data.Runnables; using GameHub.Utils; using GameHub.UI.Widgets; +using GameHub.UI.Widgets.Tweaks; namespace GameHub.UI.Dialogs { diff --git a/src/ui/dialogs/SettingsDialog/SettingsDialog.vala b/src/ui/dialogs/SettingsDialog/SettingsDialog.vala index 3cd13d06..0b36cbe1 100644 --- a/src/ui/dialogs/SettingsDialog/SettingsDialog.vala +++ b/src/ui/dialogs/SettingsDialog/SettingsDialog.vala @@ -93,7 +93,7 @@ namespace GameHub.UI.Dialogs.SettingsDialog pages.interpolate_size = true; pages.notify["visible-child"].connect(() => { - update_current_page((SettingsSidebar.SettingsPage) pages.visible_child); + update_current_page((SettingsSidebar.SettingsPage) pages.visible_child); }); add_page("ui/appearance", new Pages.UI.Appearance(this)); @@ -159,53 +159,53 @@ namespace GameHub.UI.Dialogs.SettingsDialog private void update_current_page(SettingsSidebar.SettingsPage page) { - if(current_page != null) - { - current_page.disconnect(current_page_title_handler); - current_page.disconnect(current_page_desc_handler); - current_page.disconnect(current_page_has_active_switch_handler); - if(current_page_active_binding != null) - { - current_page_active_binding.unbind(); - current_page_active_binding = null; - } - } - current_page = page; - current_page_title_handler = current_page.notify["title"].connect(update_current_page_title); - current_page_desc_handler = current_page.notify["description"].connect(update_current_page_title); - current_page_has_active_switch_handler = current_page.notify["has-active-switch"].connect(update_current_page_active_switch); - update_current_page_title(); - update_current_page_active_switch(); + if(current_page != null) + { + current_page.disconnect(current_page_title_handler); + current_page.disconnect(current_page_desc_handler); + current_page.disconnect(current_page_has_active_switch_handler); + if(current_page_active_binding != null) + { + current_page_active_binding.unbind(); + current_page_active_binding = null; + } + } + current_page = page; + current_page_title_handler = current_page.notify["title"].connect(update_current_page_title); + current_page_desc_handler = current_page.notify["description"].connect(update_current_page_title); + current_page_has_active_switch_handler = current_page.notify["has-active-switch"].connect(update_current_page_active_switch); + update_current_page_title(); + update_current_page_active_switch(); } private void update_current_page_title() { - if(headerbar == null || current_page == null) return; - var title = current_page.title; - var desc = current_page.description; - if(desc != null) - { - headerbar.subtitle = "%s • %s".printf(title, desc); - } - else - { - headerbar.subtitle = title; - } + if(headerbar == null || current_page == null) return; + var title = current_page.title; + var desc = current_page.description; + if(desc != null) + { + headerbar.subtitle = "%s • %s".printf(title, desc); + } + else + { + headerbar.subtitle = title; + } } private void update_current_page_active_switch() { - if(headerbar_page_active_switch == null || current_page == null) return; - headerbar_page_active_switch.visible = current_page.has_active_switch; - if(current_page_active_binding != null) - { - current_page_active_binding.unbind(); - current_page_active_binding = null; - } - if(current_page.has_active_switch) - { - current_page_active_binding = current_page.bind_property("active", headerbar_page_active_switch, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); - } + if(headerbar_page_active_switch == null || current_page == null) return; + headerbar_page_active_switch.visible = current_page.has_active_switch; + if(current_page_active_binding != null) + { + current_page_active_binding.unbind(); + current_page_active_binding = null; + } + if(current_page.has_active_switch) + { + current_page_active_binding = current_page.bind_property("active", headerbar_page_active_switch, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + } } private void add_page(string id, SettingsSidebar.SettingsPage page) diff --git a/src/ui/dialogs/SettingsDialog/pages/general/Collection.vala b/src/ui/dialogs/SettingsDialog/pages/general/Collection.vala index b993541a..aee04d8d 100644 --- a/src/ui/dialogs/SettingsDialog/pages/general/Collection.vala +++ b/src/ui/dialogs/SettingsDialog/pages/general/Collection.vala @@ -28,7 +28,7 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General { public class Collection: SettingsDialogPage { - private const string EXAMPLE_GAME_NAME = "Factorio"; + private const string EXAMPLE_GAME_NAME = "Factorio"; private Paths.Collection collection; private Paths.Collection.GOG gog; @@ -64,26 +64,26 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General gog = Paths.Collection.GOG.instance; humble = Paths.Collection.Humble.instance; - var sgrp_collection = new SettingsGroup(); + var sgrp_collection = new SettingsGroup(); collection_root = sgrp_collection.add_setting( - new FileSetting.bind( - _("Collection directory") + """ • $root""", _("Installers and bonus content will be downloaded in the collection directory"), - file_chooser(_("Select collection root directory"), FileChooserAction.SELECT_FOLDER), - collection, "root" - ) + new FileSetting.bind( + _("Collection directory") + """ • $root""", _("Installers and bonus content will be downloaded in the collection directory"), + file_chooser(_("Select collection root directory"), FileChooserAction.SELECT_FOLDER), + collection, "root" + ) ); add_widget(sgrp_collection); - var sgrp_gog = new SettingsGroup("GOG"); - gog_game_dir = sgrp_gog.add_setting(new EntrySetting.bind(_("Game directory") + """ • $game_dir""", null, entry("source-gog-symbolic"), gog, "game-dir")); - gog_installers = sgrp_gog.add_setting(new EntrySetting.bind(_("Installers directory"), null, entry("source-gog-symbolic"), gog, "installers")); - gog_dlc = sgrp_gog.add_setting(new EntrySetting.bind(_("DLC directory"), null, entry("folder-download-symbolic"), gog, "dlc")); - gog_bonus = sgrp_gog.add_setting(new EntrySetting.bind(_("Bonus content directory"), null, entry("folder-music-symbolic"), gog, "bonus")); + var sgrp_gog = new SettingsGroup("GOG"); + gog_game_dir = sgrp_gog.add_setting(new EntrySetting.bind(_("Game directory") + """ • $game_dir""", null, entry("source-gog-symbolic"), gog, "game-dir")); + gog_installers = sgrp_gog.add_setting(new EntrySetting.bind(_("Installers directory"), null, entry("source-gog-symbolic"), gog, "installers")); + gog_dlc = sgrp_gog.add_setting(new EntrySetting.bind(_("DLC directory"), null, entry("folder-download-symbolic"), gog, "dlc")); + gog_bonus = sgrp_gog.add_setting(new EntrySetting.bind(_("Bonus content directory"), null, entry("folder-music-symbolic"), gog, "bonus")); add_widget(sgrp_gog); - var sgrp_humble = new SettingsGroup("Humble Bundle"); - humble_game_dir = sgrp_humble.add_setting(new EntrySetting.bind(_("Game directory") + """ • $game_dir""", null, entry("source-humble-symbolic"), humble, "game-dir")); - humble_installers = sgrp_humble.add_setting(new EntrySetting.bind(_("Installers directory"), null, entry("source-humble-symbolic"), humble, "installers")); + var sgrp_humble = new SettingsGroup("Humble Bundle"); + humble_game_dir = sgrp_humble.add_setting(new EntrySetting.bind(_("Game directory") + """ • $game_dir""", null, entry("source-humble-symbolic"), humble, "game-dir")); + humble_installers = sgrp_humble.add_setting(new EntrySetting.bind(_("Installers directory"), null, entry("source-humble-symbolic"), humble, "installers")); add_widget(sgrp_humble); gog_game_dir.ellipsize_description = gog_installers.ellipsize_description = gog_dlc.ellipsize_description = gog_bonus.ellipsize_description = Pango.EllipsizeMode.START; diff --git a/src/ui/dialogs/SettingsDialog/pages/general/Controller.vala b/src/ui/dialogs/SettingsDialog/pages/general/Controller.vala index a1b72d93..6873f563 100644 --- a/src/ui/dialogs/SettingsDialog/pages/general/Controller.vala +++ b/src/ui/dialogs/SettingsDialog/pages/general/Controller.vala @@ -46,35 +46,35 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General if(has_active_switch) { - settings.bind_property("enabled", this, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + settings.bind_property("enabled", this, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); - var sgrp_controller_options = new SettingsGroup(); - sgrp_controller_options.add_setting(new SwitchSetting.bind(_("Focus GameHub window with Guide button"), null, settings, "focus-window")); - add_widget(sgrp_controller_options); + var sgrp_controller_options = new SettingsGroup(); + sgrp_controller_options.add_setting(new SwitchSetting.bind(_("Focus GameHub window with Guide button"), null, settings, "focus-window")); + add_widget(sgrp_controller_options); - sgrp_controllers = new SettingsGroup(_("Controllers")); - add_widget(sgrp_controllers); + sgrp_controllers = new SettingsGroup(_("Controllers")); + add_widget(sgrp_controllers); - shortcuts_grid = add_widget(new Grid()); - shortcuts_grid.valign = Align.END; - shortcuts_grid.column_spacing = 12; - shortcuts_grid.margin_top = 12; - shortcuts_grid.margin_start = 18; - shortcuts_grid.margin_end = 18; - shortcuts_grid.margin_bottom = 6; - shortcuts_grid.expand = true; + shortcuts_grid = add_widget(new Grid()); + shortcuts_grid.valign = Align.END; + shortcuts_grid.column_spacing = 12; + shortcuts_grid.margin_top = 12; + shortcuts_grid.margin_start = 18; + shortcuts_grid.margin_end = 18; + shortcuts_grid.margin_bottom = 6; + shortcuts_grid.expand = true; - add_shortcut(0, 0, _("Move focus"), "trigger-left", "/", "trigger-right"); - shortcuts_grid.add(new Separator(Orientation.VERTICAL)); - add_shortcut(2, 0, _("Quit GameHub"), "guide", "+", "b"); + add_shortcut(0, 0, _("Move focus"), "trigger-left", "/", "trigger-right"); + shortcuts_grid.add(new Separator(Orientation.VERTICAL)); + add_shortcut(2, 0, _("Quit GameHub"), "guide", "+", "b"); - update(); + update(); } else { - var xorg_warning = new AlertView(_("Controllers are not supported"), _("GameHub currently only supports controllers when running under X.Org display server"), "dialog-warning-symbolic"); - xorg_warning.get_style_context().remove_class(Gtk.STYLE_CLASS_VIEW); - add_widget(xorg_warning); + var xorg_warning = new AlertView(_("Controllers are not supported"), _("GameHub currently only supports controllers when running under X.Org display server"), "dialog-warning-symbolic"); + xorg_warning.get_style_context().remove_class(Gtk.STYLE_CLASS_VIEW); + add_widget(xorg_warning); } } @@ -86,14 +86,14 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General if(settings.known_controllers.length == 0) { - sgrp_controllers.add_setting(new LabelSetting(_("No controllers detected. Connected controllers will appear here"))); + sgrp_controllers.add_setting(new LabelSetting(_("No controllers detected. Connected controllers will appear here"))); } else { - foreach(var controller in settings.known_controllers) - { - sgrp_controllers.add_setting(new ControllerRow(controller, !(controller in settings.ignored_controllers), this)); - } + foreach(var controller in settings.known_controllers) + { + sgrp_controllers.add_setting(new ControllerRow(controller, !(controller in settings.ignored_controllers), this)); + } } } @@ -163,8 +163,8 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General }); setting_activated.connect(() => { - enabled_switch.activate(); - }); + enabled_switch.activate(); + }); } } diff --git a/src/ui/dialogs/SettingsDialog/pages/general/Tweaks.vala b/src/ui/dialogs/SettingsDialog/pages/general/Tweaks.vala index 968ac91d..615edfbf 100644 --- a/src/ui/dialogs/SettingsDialog/pages/general/Tweaks.vala +++ b/src/ui/dialogs/SettingsDialog/pages/general/Tweaks.vala @@ -19,6 +19,7 @@ along with GameHub. If not, see . using Gtk; using GameHub.UI.Widgets; using GameHub.UI.Widgets.Settings; +using GameHub.UI.Widgets.Tweaks; using GameHub.Data; using GameHub.Utils; @@ -38,22 +39,22 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General construct { - var dirs = FS.get_data_dirs("tweaks", true); + var dirs = FS.get_data_dirs("tweaks", true); var last_dir = dirs.last(); - var sgrp_dirs = new SettingsGroup(); - var dirs_btn = new MenuButton(); + var sgrp_dirs = new SettingsGroup(); + var dirs_btn = new MenuButton(); dirs_btn.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); - dirs_btn.tooltip_text = _("Tweak directories"); - dirs_btn.can_focus = false; + dirs_btn.tooltip_text = _("Tweak directories"); + dirs_btn.can_focus = false; var dirs_setting = sgrp_dirs.add_setting(new BaseSetting( - _("Tweak directories"), - _("Tweaks are loaded from these directories in order\nLast tweak overrides previous tweaks with same identifiers"), - dirs_btn - )); + _("Tweak directories"), + _("Tweaks are loaded from these directories in order\nLast tweak overrides previous tweaks with same identifiers"), + dirs_btn + )); - var dirs_menu = new Gtk.Menu(); + var dirs_menu = new Gtk.Menu(); dirs_menu.halign = Align.END; foreach(var dir in dirs) @@ -61,13 +62,13 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General var dir_item = new Gtk.MenuItem.with_label(dir.get_path()); if(dir.query_exists()) { - dir_item.activate.connect(() => { - Utils.open_uri(dir.get_uri()); - }); + dir_item.activate.connect(() => { + Utils.open_uri(dir.get_uri()); + }); } else { - dir_item.sensitive = false; + dir_item.sensitive = false; } dirs_menu.add(dir_item); } @@ -75,19 +76,19 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.General dirs_menu.show_all(); dirs_btn.popup = dirs_menu; - dirs_setting.activatable = true; - dirs_setting.setting_activated.connect(() => { - #if GTK_3_22 + dirs_setting.activatable = true; + dirs_setting.setting_activated.connect(() => { + #if GTK_3_22 dirs_menu.popup_at_widget(dirs_btn, Gdk.Gravity.SOUTH_EAST, Gdk.Gravity.NORTH_EAST); - #else - dirs_menu.popup(null, null, null, 0, get_current_event_time()); - #endif - }); + #else + dirs_menu.popup(null, null, null, 0, get_current_event_time()); + #endif + }); add_widget(sgrp_dirs); - var sgrp_tweaks = new SettingsGroupBox(); - sgrp_tweaks.container.get_style_context().remove_class(Gtk.STYLE_CLASS_VIEW); - sgrp_tweaks.add_widget(new TweaksList()); + var sgrp_tweaks = new SettingsGroupBox(); + sgrp_tweaks.container.get_style_context().remove_class(Gtk.STYLE_CLASS_VIEW); + sgrp_tweaks.add_widget(new TweaksList()); add_widget(sgrp_tweaks); } } diff --git a/src/ui/dialogs/SettingsDialog/pages/providers/Providers.vala b/src/ui/dialogs/SettingsDialog/pages/providers/Providers.vala index 40a16170..3d6dd2e6 100644 --- a/src/ui/dialogs/SettingsDialog/pages/providers/Providers.vala +++ b/src/ui/dialogs/SettingsDialog/pages/providers/Providers.vala @@ -88,8 +88,8 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.Providers construct { - get_style_context().add_class("setting"); - get_style_context().add_class("provider-setting"); + get_style_context().add_class("setting"); + get_style_context().add_class("provider-setting"); var root_vbox = new Box(Orientation.VERTICAL, 0); @@ -166,8 +166,8 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.Providers }); setting_activated.connect(() => { - enabled_switch.activate(); - }); + enabled_switch.activate(); + }); } } } diff --git a/src/ui/dialogs/SettingsDialog/pages/ui/Appearance.vala b/src/ui/dialogs/SettingsDialog/pages/ui/Appearance.vala index dac1ea97..23946b45 100644 --- a/src/ui/dialogs/SettingsDialog/pages/ui/Appearance.vala +++ b/src/ui/dialogs/SettingsDialog/pages/ui/Appearance.vala @@ -87,7 +87,7 @@ namespace GameHub.UI.Dialogs.SettingsDialog.Pages.UI var sgrp_appearance = new SettingsGroup(); sgrp_appearance.add_setting(new SwitchSetting.bind(_("Prefer dark theme"), null, settings, "dark-theme")); - sgrp_appearance.add_setting(new ModeButtonSetting.bind(_("Icon style"), _("Colored icons may look better for some themes"), { C_("icon_style", "Automatic"), C_("icon_style", "Symbolic"), C_("icon_style", "Colored") }, settings, "icon-style")); + sgrp_appearance.add_setting(new ModeButtonSetting.bind(_("Icon style"), _("Colored icons may look better for some themes"), { C_("icon_style", "Automatic"), C_("icon_style", "Symbolic"), C_("icon_style", "Colored") }, settings, "icon-style")); add_widget(sgrp_appearance); var sgrp_grid = new SettingsGroup(_("Grid")); diff --git a/src/ui/widgets/FileChooserEntry.vala b/src/ui/widgets/FileChooserEntry.vala index abff553a..3938ddba 100644 --- a/src/ui/widgets/FileChooserEntry.vala +++ b/src/ui/widgets/FileChooserEntry.vala @@ -40,8 +40,8 @@ namespace GameHub.UI.Widgets public string? file_path { - get { return text; } - set { select_file_path(value); } + get { return text; } + set { select_file_path(value); } } public FileChooserEntry(string? title, FileChooserAction action, string? icon=null, string? hint=null, bool allow_url=false, bool allow_executable=false) diff --git a/src/ui/widgets/TweaksList.vala b/src/ui/widgets/TweaksList.vala deleted file mode 100644 index 381548f7..00000000 --- a/src/ui/widgets/TweaksList.vala +++ /dev/null @@ -1,320 +0,0 @@ -/* -This file is part of GameHub. -Copyright (C) 2018-2019 Anatoliy Kashkin - -GameHub is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -GameHub is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GameHub. If not, see . -*/ - -using Gtk; -using Gdk; -using Gee; - -using GameHub.UI.Widgets; -using GameHub.UI.Widgets.Settings; - -using GameHub.Data; -using GameHub.Data.Tweaks; -using GameHub.Data.Runnables; - -namespace GameHub.UI.Widgets -{ - public class TweaksList: Notebook - { - public Traits.Game.SupportsTweaks? game { get; construct; default = null; } - - public TweaksList(Traits.Game.SupportsTweaks? game = null) - { - Object(game: game, show_border: false, expand: true); - } - - construct - { - update(); - } - - public void update(CompatTool? compat_tool=null) - { - this.foreach(w => w.destroy()); - - var tweaks = Tweak.load_tweaks_grouped(game == null); - - if(tweaks != null && tweaks.size > 0) - { - foreach(var group in tweaks.entries) - { - var tab = new TweakGroupTab(game, compat_tool, group.key ?? _("Ungrouped"), group.value); - append_page(tab, new Label(tab.group)); - } - show_tabs = tweaks.size > 1; - } - else - { - append_page(new AlertView(_("No tweaks"), _("No tweaks were found\nAdd your tweaks into one of the tweak directories"), "dialog-warning-symbolic")); - show_tabs = false; - } - } - - private class TweakGroupTab: ScrolledWindow - { - public Traits.Game.SupportsTweaks? game { get; construct; default = null; } - public CompatTool? compat_tool { get; construct; default = null; } - public string? group { get; construct; default = null; } - public HashMap? tweaks { get; construct; default = null; } - - public TweakGroupTab(Traits.Game.SupportsTweaks? game = null, CompatTool? compat_tool = null, string? group = null, HashMap? tweaks = null) - { - Object(game: game, compat_tool: compat_tool, group: group, tweaks: tweaks, hscrollbar_policy: PolicyType.NEVER, expand: true); - } - - construct - { - var tweaks_list = new ListBox(); - tweaks_list.selection_mode = SelectionMode.NONE; - child = tweaks_list; - - if(tweaks != null) - { - foreach(var tweak in tweaks.values) - { - if(game == null || tweak.is_applicable_to(game, compat_tool)) - { - tweaks_list.add(new TweakRow(tweak, game)); - } - } - } - - tweaks_list.row_activated.connect(row => { - var setting = row as ActivatableSetting; - if(setting != null) - { - setting.setting_activated(); - } - }); - - show_all(); - } - } - - private class TweakRow: ListBoxRow, ActivatableSetting - { - public Tweak tweak { get; construct; } - public Traits.Game.SupportsTweaks? game { get; construct; default = null; } - - public TweakRow(Tweak tweak, Traits.Game.SupportsTweaks? game=null) - { - Object(tweak: tweak, game: game, activatable: true, selectable: false); - } - - construct - { - get_style_context().add_class("setting"); - get_style_context().add_class("tweak-setting"); - - var grid = new Grid(); - grid.column_spacing = 12; - - var icon = new Image.from_icon_name(tweak.icon, IconSize.LARGE_TOOLBAR); - icon.valign = Align.CENTER; - - var name = new Label(tweak.name ?? tweak.id); - name.get_style_context().add_class("title"); - name.set_size_request(96, -1); - name.hexpand = true; - name.ellipsize = Pango.EllipsizeMode.END; - name.max_width_chars = 60; - name.xalign = 0; - name.valign = Align.CENTER; - - var description = new Label(tweak.description ?? _("No description")); - description.get_style_context().add_class("description"); - description.tooltip_text = tweak.description; - description.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); - description.hexpand = true; - description.ellipsize = Pango.EllipsizeMode.END; - description.max_width_chars = 60; - description.xalign = 0; - description.valign = Align.CENTER; - - var install = new Button.with_label(_("Install")); - install.valign = Align.CENTER; - install.sensitive = false; - - var enabled = new Switch(); - enabled.active = tweak.is_enabled(game); - enabled.valign = Align.CENTER; - - var buttons_hbox = new Box(Orientation.HORIZONTAL, 0); - buttons_hbox.valign = Align.CENTER; - - grid.attach(icon, 0, 0, 1, 2); - grid.attach(name, 1, 0); - grid.attach(description, 1, 1); - grid.attach(buttons_hbox, 2, 0, 1, 2); - - if(tweak.url != null) - { - var url = new Button.from_icon_name("web-symbolic", IconSize.BUTTON); - url.tooltip_markup = """%s%s%s""".printf(_("Open URL"), "\n", tweak.url); - url.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); - - url.clicked.connect(() => { - Utils.open_uri(tweak.url); - }); - - buttons_hbox.add(url); - } - - if(tweak.file != null && tweak.file.query_exists()) - { - var edit = new Button.from_icon_name("accessories-text-editor-symbolic", IconSize.BUTTON); - edit.tooltip_markup = """%s%s%s""".printf(_("Edit file"), "\n", tweak.file.get_path()); - edit.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); - - edit.clicked.connect(() => { - Utils.open_uri(tweak.file.get_uri()); - }); - - buttons_hbox.add(edit); - } - - MenuButton? options = null; - if(tweak.options != null && tweak.options.size > 0) - { - var options_menu = new Gtk.Menu(); - - foreach(var option in tweak.options) - { - var option_menu = new Gtk.Menu(); - - if(option.presets != null && option.presets.size > 0) - { - RadioMenuItem? preset_item = null; - - var presets_header = new Gtk.MenuItem.with_label("""%s""".printf(_("Presets"))); - ((Label) presets_header.get_child()).use_markup = true; - presets_header.sensitive = false; - option_menu.append(presets_header); - - foreach(var preset in option.presets) - { - var preset_item_label = preset.name ?? preset.id; - if(preset.description != null) - { - preset_item_label = """%s%s%s""".printf(preset_item_label, "\n", preset.description); - } - - preset_item = new RadioMenuItem.with_label_from_widget(preset_item, preset_item_label); - ((Label) preset_item.get_child()).use_markup = true; - option_menu.append(preset_item); - } - } - - if(option.values != null && option.values.size > 0) - { - if(option.presets != null && option.presets.size > 0) - { - option_menu.append(new SeparatorMenuItem()); - } - - var values_header = new Gtk.MenuItem.with_label("""%s""".printf(_("Options"))); - ((Label) values_header.get_child()).use_markup = true; - values_header.sensitive = false; - option_menu.append(values_header); - - foreach(var value in option.values.entries) - { - var value_item = new CheckMenuItem.with_label("""%s%s%s""".printf(value.key, "\n", value.value)); - ((Label) value_item.get_child()).use_markup = true; - option_menu.append(value_item); - } - } - - var option_item_label = option.name ?? option.id; - if(option.description != null) - { - option_item_label = """%s%s%s""".printf(option_item_label, "\n", option.description); - } - - var option_item = new Gtk.MenuItem.with_label(option_item_label); - ((Label) option_item.get_child()).use_markup = true; - option_item.submenu = option_menu; - options_menu.append(option_item); - } - - options_menu.show_all(); - - options = new MenuButton(); - options.image = new Image.from_icon_name("gh-settings-cog-symbolic", IconSize.BUTTON); - options.popup = options_menu; - options.tooltip_text = _("Options"); - options.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); - - buttons_hbox.add(options); - } - - grid.attach(enabled, 4, 0, 1, 2); - - var unavailable_reqs = tweak.get_unavailable_requirements(); - if(unavailable_reqs != null) - { - string[] reqs = {}; - - if(unavailable_reqs.executables != null && unavailable_reqs.executables.size != 0) - { - reqs += """%s%s""".printf("\n", unavailable_reqs.executables.size == 1 - ? _("Requires an executable") - : _("Requires one of the executables")); - foreach(var executable in unavailable_reqs.executables) - { - reqs += "• %s".printf(executable); - } - } - - if(unavailable_reqs.kernel_modules != null) - { - reqs += """%s%s""".printf("\n", unavailable_reqs.kernel_modules.size == 1 - ? _("Requires a kernel module") - : _("Requires one of the kernel modules")); - - foreach(var kmod in unavailable_reqs.kernel_modules) - { - reqs += "• %s".printf(kmod); - } - } - - if(options != null) - { - options.sensitive = false; - } - - enabled.sensitive = false; - activatable = false; - icon.icon_name = "action-unavailable-symbolic"; - icon.tooltip_markup = enabled.tooltip_markup = string.joinv("\n", reqs).strip(); - } - else - { - enabled.notify["active"].connect(() => { - tweak.set_enabled(enabled.active, game); - }); - setting_activated.connect(() => { - enabled.activate(); - }); - } - - child = grid; - } - } - } -} diff --git a/src/ui/widgets/settings/Settings.vala b/src/ui/widgets/settings/Settings.vala index bf7ea107..38f0c955 100644 --- a/src/ui/widgets/settings/Settings.vala +++ b/src/ui/widgets/settings/Settings.vala @@ -22,270 +22,268 @@ using GameHub.UI.Widgets; namespace GameHub.UI.Widgets.Settings { - public interface ActivatableSetting: ListBoxRow - { - public signal void setting_activated(); - } - - public class BaseSetting: ListBoxRow, ActivatableSetting - { - public string title { get; construct set; } - public string? description { get; construct set; } - public Widget? widget { get; construct set; } - - public Pango.EllipsizeMode ellipsize_title { get; set; default = Pango.EllipsizeMode.NONE; } - public Pango.EllipsizeMode ellipsize_description { get; set; default = Pango.EllipsizeMode.NONE; } - - private Box hbox; - private Label title_label; - private Label description_label; - - public BaseSetting(string title, string? description = null, Widget? widget = null) - { - Object(title: title, description: description, widget: widget, activatable: false, selectable: false); - } - - construct - { - get_style_context().add_class("setting"); - - hbox = new Box(Orientation.HORIZONTAL, 12); - - var text_vbox = new Box(Orientation.VERTICAL, 0); - text_vbox.hexpand = true; - text_vbox.valign = Align.CENTER; - - title_label = new Label(null); - title_label.use_markup = true; - title_label.get_style_context().add_class("title"); - title_label.hexpand = true; - title_label.wrap = true; - title_label.xalign = 0; - - description_label = new Label(null); - description_label.use_markup = true; - description_label.get_style_context().add_class("description"); - description_label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); - description_label.hexpand = true; - description_label.wrap = true; - description_label.xalign = 0; - description_label.no_show_all = true; - - text_vbox.add(title_label); - text_vbox.add(description_label); - - hbox.add(text_vbox); - - child = hbox; - - show_all(); - - description_label.notify["label"].connect(() => { - description_label.visible = description_label.label != null && description_label.label.length > 0; - }); - - bind_property("title", title_label, "label", BindingFlags.SYNC_CREATE); - - bind_property("description", description_label, "label", BindingFlags.SYNC_CREATE); - bind_property("description", description_label, "tooltip-text", BindingFlags.SYNC_CREATE); - - notify["ellipsize-title"].connect(() => { - title_label.ellipsize = ellipsize_title; - title_label.wrap = ellipsize_title == Pango.EllipsizeMode.NONE; - }); - - notify["ellipsize-description"].connect(() => { - description_label.ellipsize = ellipsize_description; - description_label.wrap = ellipsize_description == Pango.EllipsizeMode.NONE; - }); - - notify["widget"].connect(() => replace_widget(widget)); - replace_widget(widget); - } - - protected void replace_widget(Widget? w) - { - if(widget != null && widget.parent == hbox) - { - hbox.remove(widget); - } - widget = w; - if(widget != null && widget.parent != hbox) - { - widget.valign = Align.CENTER; - widget.halign = Align.END; - hbox.add(widget); - init_widget(widget); - } - } - - protected virtual void init_widget(Widget w){} - } - - public class CustomWidgetSetting: ListBoxRow - { - public Widget widget { get; construct; } - - public CustomWidgetSetting(Widget widget = null) - { - Object(widget: widget, activatable: false, selectable: false); - } - - construct - { - get_style_context().add_class("setting"); - get_style_context().add_class("custom-widget-setting"); - child = widget; - show_all(); - } - } - - public class LabelSetting: ListBoxRow - { - public Label label { get; construct; } - - public LabelSetting(string label) - { - Object(label: new Label(label), activatable: false, selectable: false, can_focus: false); - } - - construct - { - get_style_context().add_class("setting"); - get_style_context().add_class("label-setting"); - label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); - child = label; - show_all(); - } - } - - public class SwitchSetting: BaseSetting - { - public Switch? @switch { get { return widget as Switch; } } - - public SwitchSetting(string title, string? description = null, bool active = false) - { - Object(title: title, description: description, widget: new Switch(), activatable: true, selectable: false); - @switch.active = active; - @switch.can_focus = false; - } - - public SwitchSetting.bind(string title, string? description = null, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) - { - Object(title: title, description: description, widget: new Switch(), activatable: true, selectable: false); - target.bind_property(prop, @switch, "active", flags); - @switch.can_focus = false; - } - - construct - { - get_style_context().add_class("switch-setting"); - setting_activated.connect(() => { - @switch.activate(); - }); - } - } - - public class EntrySetting: BaseSetting - { - public Entry? entry { get { return widget as Entry; } } - - public EntrySetting(string title, string? description = null, Entry text_entry, string? value = null) - { - Object(title: title, description: description, widget: text_entry, activatable: false, selectable: false); - entry.text = value; - } - - public EntrySetting.bind(string title, string? description = null, Entry text_entry, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) - { - Object(title: title, description: description, widget: text_entry, activatable: false, selectable: false); - target.bind_property(prop, entry, "text", flags); - } - - construct - { - get_style_context().add_class("entry-setting"); - } - } - - public class ModeButtonSetting: BaseSetting - { - public ModeButton? button { get { return widget as ModeButton; } } - - public ModeButtonSetting(string title, string? description = null, string[]? options = null, int selected_option = -1) - { - Object(title: title, description: description, widget: new ModeButton(), options: options, selected_option: selected_option, activatable: false, selectable: false); - } - - public ModeButtonSetting.bind(string title, string? description = null, string[]? options = null, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) - { - Object(title: title, description: description, widget: new ModeButton(), activatable: false, selectable: false); - target.bind_property(prop, this, "selected-option", flags); - this.options = options; - } - - private string[]? _options = null; - private int _selected_option = -1; - - public string[]? options - { - get { return _options; } - set - { - _options = value; - if(button != null && _options != null) - { - button.clear_children(); - for(var i = 0; i < _options.length; i++) - { - button.append_text(_options[i]); - if(i == selected_option) - { - button.selected = i; - } - } - } - } - } - public int selected_option - { - get { return _selected_option; } - set - { - _selected_option = value; - button.selected = _selected_option; - } - } - - construct - { - get_style_context().add_class("mode-button-setting"); - button.homogeneous = false; - button.mode_changed.connect(() => { - selected_option = button.selected; - }); - } - } - - public class FileSetting: BaseSetting - { - public FileChooserEntry? chooser { get { return widget as FileChooserEntry; } } - - public FileSetting(string title, string? description = null, FileChooserEntry file_chooser, string? value) - { - Object(title: title, description: description, widget: file_chooser, activatable: false, selectable: false); - chooser.select_file_path(value); - } - - public FileSetting.bind(string title, string? description = null, FileChooserEntry file_chooser, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) - { - Object(title: title, description: description, widget: file_chooser, activatable: false, selectable: false); - target.bind_property(prop, chooser, "file-path", flags); - } - - construct - { - get_style_context().add_class("file-setting"); - } - } + public interface ActivatableSetting: ListBoxRow + { + public signal void setting_activated(); + } + + public class BaseSetting: ListBoxRow, ActivatableSetting + { + public string title { get; construct set; } + public string? description { get; construct set; } + public Widget? widget { get; construct set; } + + public Pango.EllipsizeMode ellipsize_title { get; set; default = Pango.EllipsizeMode.NONE; } + public Pango.EllipsizeMode ellipsize_description { get; set; default = Pango.EllipsizeMode.NONE; } + + private Box hbox; + private Label title_label; + private Label description_label; + + public BaseSetting(string title, string? description = null, Widget? widget = null) + { + Object(title: title, description: description, widget: widget, activatable: false, selectable: false); + } + + construct + { + get_style_context().add_class("setting"); + + hbox = new Box(Orientation.HORIZONTAL, 12); + + var text_vbox = new Box(Orientation.VERTICAL, 0); + text_vbox.hexpand = true; + text_vbox.valign = Align.CENTER; + + title_label = new Label(null); + title_label.get_style_context().add_class("title"); + title_label.use_markup = true; + title_label.hexpand = true; + title_label.wrap = true; + title_label.xalign = 0; + + description_label = new Label(null); + description_label.get_style_context().add_class("description"); + description_label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + description_label.use_markup = true; + description_label.hexpand = true; + description_label.wrap = true; + description_label.xalign = 0; + description_label.no_show_all = true; + + text_vbox.add(title_label); + text_vbox.add(description_label); + + hbox.add(text_vbox); + child = hbox; + show_all(); + + description_label.notify["label"].connect(() => { + description_label.visible = description_label.label != null && description_label.label.length > 0; + }); + + bind_property("title", title_label, "label", BindingFlags.SYNC_CREATE); + + bind_property("description", description_label, "label", BindingFlags.SYNC_CREATE); + bind_property("description", description_label, "tooltip-text", BindingFlags.SYNC_CREATE); + + notify["ellipsize-title"].connect(() => { + title_label.ellipsize = ellipsize_title; + title_label.wrap = ellipsize_title == Pango.EllipsizeMode.NONE; + }); + + notify["ellipsize-description"].connect(() => { + description_label.ellipsize = ellipsize_description; + description_label.wrap = ellipsize_description == Pango.EllipsizeMode.NONE; + }); + + notify["widget"].connect(() => replace_widget(widget)); + replace_widget(widget); + } + + protected void replace_widget(Widget? w) + { + if(widget != null && widget.parent == hbox) + { + hbox.remove(widget); + } + widget = w; + if(widget != null && widget.parent != hbox) + { + widget.valign = Align.CENTER; + widget.halign = Align.END; + hbox.add(widget); + init_widget(widget); + } + } + + protected virtual void init_widget(Widget w){} + } + + public class CustomWidgetSetting: ListBoxRow + { + public Widget widget { get; construct; } + + public CustomWidgetSetting(Widget widget = null) + { + Object(widget: widget, activatable: false, selectable: false); + } + + construct + { + get_style_context().add_class("setting"); + get_style_context().add_class("custom-widget-setting"); + child = widget; + show_all(); + } + } + + public class LabelSetting: ListBoxRow + { + public Label label { get; construct; } + + public LabelSetting(string label) + { + Object(label: new Label(label), activatable: false, selectable: false, can_focus: false); + } + + construct + { + get_style_context().add_class("setting"); + get_style_context().add_class("label-setting"); + label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + child = label; + show_all(); + } + } + + public class SwitchSetting: BaseSetting + { + public Switch? @switch { get { return widget as Switch; } } + + public SwitchSetting(string title, string? description = null, bool active = false) + { + Object(title: title, description: description, widget: new Switch(), activatable: true, selectable: false); + @switch.active = active; + @switch.can_focus = false; + } + + public SwitchSetting.bind(string title, string? description = null, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) + { + Object(title: title, description: description, widget: new Switch(), activatable: true, selectable: false); + target.bind_property(prop, @switch, "active", flags); + @switch.can_focus = false; + } + + construct + { + get_style_context().add_class("switch-setting"); + setting_activated.connect(() => { + @switch.activate(); + }); + } + } + + public class EntrySetting: BaseSetting + { + public Entry? entry { get { return widget as Entry; } } + + public EntrySetting(string title, string? description = null, Entry text_entry, string? value = null) + { + Object(title: title, description: description, widget: text_entry, activatable: false, selectable: false); + entry.text = value; + } + + public EntrySetting.bind(string title, string? description = null, Entry text_entry, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) + { + Object(title: title, description: description, widget: text_entry, activatable: false, selectable: false); + target.bind_property(prop, entry, "text", flags); + } + + construct + { + get_style_context().add_class("entry-setting"); + } + } + + public class ModeButtonSetting: BaseSetting + { + public ModeButton? button { get { return widget as ModeButton; } } + + public ModeButtonSetting(string title, string? description = null, string[]? options = null, int selected_option = -1) + { + Object(title: title, description: description, widget: new ModeButton(), options: options, selected_option: selected_option, activatable: false, selectable: false); + } + + public ModeButtonSetting.bind(string title, string? description = null, string[]? options = null, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) + { + Object(title: title, description: description, widget: new ModeButton(), activatable: false, selectable: false); + target.bind_property(prop, this, "selected-option", flags); + this.options = options; + } + + private string[]? _options = null; + private int _selected_option = -1; + + public string[]? options + { + get { return _options; } + set + { + _options = value; + if(button != null && _options != null) + { + button.clear_children(); + for(var i = 0; i < _options.length; i++) + { + button.append_text(_options[i]); + if(i == selected_option) + { + button.selected = i; + } + } + } + } + } + public int selected_option + { + get { return _selected_option; } + set + { + _selected_option = value; + button.selected = _selected_option; + } + } + + construct + { + get_style_context().add_class("mode-button-setting"); + button.homogeneous = false; + button.mode_changed.connect(() => { + selected_option = button.selected; + }); + } + } + + public class FileSetting: BaseSetting + { + public FileChooserEntry? chooser { get { return widget as FileChooserEntry; } } + + public FileSetting(string title, string? description = null, FileChooserEntry file_chooser, string? value) + { + Object(title: title, description: description, widget: file_chooser, activatable: false, selectable: false); + chooser.select_file_path(value); + } + + public FileSetting.bind(string title, string? description = null, FileChooserEntry file_chooser, Object target, string prop, BindingFlags flags = BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) + { + Object(title: title, description: description, widget: file_chooser, activatable: false, selectable: false); + target.bind_property(prop, chooser, "file-path", flags); + } + + construct + { + get_style_context().add_class("file-setting"); + } + } } diff --git a/src/ui/widgets/settings/SettingsGroup.vala b/src/ui/widgets/settings/SettingsGroup.vala index 8789e91f..95f37200 100644 --- a/src/ui/widgets/settings/SettingsGroup.vala +++ b/src/ui/widgets/settings/SettingsGroup.vala @@ -20,89 +20,89 @@ using Gtk; namespace GameHub.UI.Widgets.Settings { - public class SettingsGroup: Box - { - public string? title { get; construct; } - - private Label? title_label; - public ListBox settings; - - public SettingsGroup(string? title = null) - { - Object(title: title, orientation: Orientation.VERTICAL); - } - - construct - { - get_style_context().add_class("settings-group"); - - if(title != null) - { - title_label = new Label(title); - title_label.get_style_context().add_class("title"); - title_label.ellipsize = Pango.EllipsizeMode.END; - title_label.xalign = 0; - add(title_label); - } - - settings = new ListBox(); - settings.get_style_context().add_class("settings-group-frame"); - settings.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); - settings.get_style_context().add_class(Gtk.STYLE_CLASS_VIEW); - add(settings); - - settings.row_activated.connect(row => { - var setting = row as ActivatableSetting; - if(setting != null) - { - setting.setting_activated(); - } - }); - } - - public T add_setting(T setting) - { - settings.add((Widget) setting); - return setting; - } - } - - public class SettingsGroupBox: Box - { - public string? title { get; construct; } - - private Label? title_label; - public Box container; - - public SettingsGroupBox(string? title = null) - { - Object(title: title, orientation: Orientation.VERTICAL); - } - - construct - { - get_style_context().add_class("settings-group"); - - if(title != null) - { - title_label = new Label(title); - title_label.get_style_context().add_class("title"); - title_label.ellipsize = Pango.EllipsizeMode.END; - title_label.xalign = 0; - add(title_label); - } - - container = new Box(Orientation.VERTICAL, 0); - container.get_style_context().add_class("settings-group-frame"); - container.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); - container.get_style_context().add_class(Gtk.STYLE_CLASS_VIEW); - add(container); - } - - public T add_widget(T widget) - { - container.add((Widget) widget); - return widget; - } - } + public class SettingsGroup: Box + { + public string? title { get; construct; } + + private Label? title_label; + public ListBox settings; + + public SettingsGroup(string? title = null) + { + Object(title: title, orientation: Orientation.VERTICAL); + } + + construct + { + get_style_context().add_class("settings-group"); + + if(title != null) + { + title_label = new Label(title); + title_label.get_style_context().add_class("title"); + title_label.ellipsize = Pango.EllipsizeMode.END; + title_label.xalign = 0; + add(title_label); + } + + settings = new ListBox(); + settings.get_style_context().add_class("settings-group-frame"); + settings.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); + settings.get_style_context().add_class(Gtk.STYLE_CLASS_VIEW); + add(settings); + + settings.row_activated.connect(row => { + var setting = row as ActivatableSetting; + if(setting != null) + { + setting.setting_activated(); + } + }); + } + + public T add_setting(T setting) + { + settings.add((Widget) setting); + return setting; + } + } + + public class SettingsGroupBox: Box + { + public string? title { get; construct; } + + private Label? title_label; + public Box container; + + public SettingsGroupBox(string? title = null) + { + Object(title: title, orientation: Orientation.VERTICAL); + } + + construct + { + get_style_context().add_class("settings-group"); + + if(title != null) + { + title_label = new Label(title); + title_label.get_style_context().add_class("title"); + title_label.ellipsize = Pango.EllipsizeMode.END; + title_label.xalign = 0; + add(title_label); + } + + container = new Box(Orientation.VERTICAL, 0); + container.get_style_context().add_class("settings-group-frame"); + container.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); + container.get_style_context().add_class(Gtk.STYLE_CLASS_VIEW); + add(container); + } + + public T add_widget(T widget) + { + container.add((Widget) widget); + return widget; + } + } } diff --git a/src/ui/widgets/settings/SettingsSidebar.vala b/src/ui/widgets/settings/SettingsSidebar.vala index d18b3906..1611be48 100644 --- a/src/ui/widgets/settings/SettingsSidebar.vala +++ b/src/ui/widgets/settings/SettingsSidebar.vala @@ -145,7 +145,7 @@ namespace GameHub.UI.Widgets.Settings construct { - get_style_context().add_class("settings-page"); + get_style_context().add_class("settings-page"); } } @@ -155,7 +155,7 @@ namespace GameHub.UI.Widgets.Settings construct { - get_style_context().add_class("simple-settings-page"); + get_style_context().add_class("simple-settings-page"); content = new Box(Orientation.VERTICAL, 0); content.vexpand = true; add(content); @@ -166,13 +166,13 @@ namespace GameHub.UI.Widgets.Settings private void update_sensitivity() { - content.sensitive = active || !has_active_switch; + content.sensitive = active || !has_active_switch; } protected T add_widget(T widget) { - content.add((Widget) widget); - return widget; + content.add((Widget) widget); + return widget; } } @@ -185,16 +185,16 @@ namespace GameHub.UI.Widgets.Settings switch(value) { case SettingsPage.StatusType.ERROR: - status_icon.visible = true; + status_icon.visible = true; status_icon.icon_name = "dialog-error-symbolic"; break; case SettingsPage.StatusType.WARNING: - status_icon.visible = true; + status_icon.visible = true; status_icon.icon_name = "dialog-warning-symbolic"; break; default: - status_icon.visible = false; - break; + status_icon.visible = false; + break; } } } @@ -238,7 +238,7 @@ namespace GameHub.UI.Widgets.Settings construct { - get_style_context().add_class("settings-sidebar-row"); + get_style_context().add_class("settings-sidebar-row"); icon = new Image(); icon.halign = Align.START; diff --git a/src/ui/widgets/tweaks/TweakOptionsPopover.vala b/src/ui/widgets/tweaks/TweakOptionsPopover.vala new file mode 100644 index 00000000..9415282c --- /dev/null +++ b/src/ui/widgets/tweaks/TweakOptionsPopover.vala @@ -0,0 +1,343 @@ +/* +This file is part of GameHub. +Copyright (C) 2018-2019 Anatoliy Kashkin + +GameHub is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GameHub is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GameHub. If not, see . +*/ + +using Gtk; +using Gdk; +using Gee; + +using GameHub.UI.Widgets; +using GameHub.UI.Widgets.Settings; + +using GameHub.Data; +using GameHub.Data.Tweaks; +using GameHub.Data.Runnables; + +namespace GameHub.UI.Widgets.Tweaks +{ + public class TweakOptionsPopover: Popover + { + public Tweak tweak { get; construct; } + + private Box option_details_hbox; + + public TweakOptionsPopover(Tweak tweak) + { + Object(tweak: tweak); + } + + construct + { + get_style_context().add_class("tweak-options-popover"); + + if(tweak.options == null || tweak.options.size == 0) + { + var options_warning = new AlertView(_("%s: no configurable options").printf(tweak.name), _("This tweak does not have any configurable options"), "dialog-warning-symbolic"); + options_warning.get_style_context().remove_class(Gtk.STYLE_CLASS_VIEW); + options_warning.set_size_request(640, 240); + options_warning.show_all(); + child = options_warning; + return; + } + + var hbox = new Box(Orientation.HORIZONTAL, 0); + hbox.expand = true; + hbox.set_size_request(640, 480); + + if(tweak.options.size > 1) + { + var options_vbox = new Box(Orientation.VERTICAL, 0); + options_vbox.hexpand = false; + options_vbox.vexpand = true; + + var options_title = new Label(_("Options")); + options_title.get_style_context().add_class("list-title"); + options_title.xalign = 0; + + var options_scrolled = new ScrolledWindow(null, null); + options_scrolled.expand = true; + options_scrolled.hscrollbar_policy = PolicyType.NEVER; + + var options_list = new ListBox(); + options_list.set_size_request(200, -1); + options_list.get_style_context().add_class("options-list"); + options_list.selection_mode = SelectionMode.BROWSE; + + options_scrolled.add(options_list); + options_vbox.add(options_title); + options_vbox.add(options_scrolled); + + hbox.add(options_vbox); + hbox.add(new Separator(Orientation.VERTICAL)); + + options_list.row_selected.connect(row => { + select_option(((OptionRow) row).option); + }); + + foreach(var option in tweak.options) + { + options_list.add(new OptionRow(option)); + } + } + + option_details_hbox = new Box(Orientation.HORIZONTAL, 0); + option_details_hbox.expand = true; + + hbox.add(option_details_hbox); + + hbox.show_all(); + child = hbox; + + select_option(tweak.options.first()); + } + + private void select_option(Tweak.Option? option) + { + option_details_hbox.foreach(w => w.destroy()); + + if(option == null) return; + + var has_presets = option.presets != null && option.presets.size > 0; + var has_values = option.values != null && option.values.size > 0; + + if(has_presets) + { + var presets_vbox = new Box(Orientation.VERTICAL, 0); + presets_vbox.expand = true; + + var presets_title = new Label(_("Presets")); + presets_title.get_style_context().add_class("list-title"); + presets_title.xalign = 0; + + var presets_scrolled = new ScrolledWindow(null, null); + presets_scrolled.expand = true; + presets_scrolled.hscrollbar_policy = PolicyType.NEVER; + + var presets_list = new ListBox(); + presets_list.set_size_request(200, -1); + presets_list.get_style_context().add_class("presets-list"); + presets_list.selection_mode = SelectionMode.NONE; + + presets_scrolled.add(presets_list); + presets_vbox.add(presets_title); + presets_vbox.add(presets_scrolled); + + presets_list.row_activated.connect(row => { + ((PresetRow) row).selected = true; + }); + + RadioButton? prev_radio = null; + foreach(var preset in option.presets) + { + var row = new PresetRow(preset, prev_radio); + presets_list.add(row); + prev_radio = row.radio; + } + + option_details_hbox.add(presets_vbox); + } + + if(has_presets && has_values) + { + option_details_hbox.add(new Separator(Orientation.VERTICAL)); + } + + if(has_values) + { + var values_vbox = new Box(Orientation.VERTICAL, 0); + values_vbox.expand = true; + + var values_title = new Label(_("Values")); + values_title.get_style_context().add_class("list-title"); + values_title.xalign = 0; + + var values_scrolled = new ScrolledWindow(null, null); + values_scrolled.expand = true; + values_scrolled.hscrollbar_policy = PolicyType.NEVER; + + var values_list = new ListBox(); + values_list.set_size_request(200, -1); + values_list.get_style_context().add_class("values-list"); + values_list.selection_mode = SelectionMode.NONE; + + values_scrolled.add(values_list); + values_vbox.add(values_title); + values_vbox.add(values_scrolled); + + values_list.row_activated.connect(r => { + var row = (ValueRow) r; + row.selected = !row.selected; + }); + + foreach(var value in option.values.entries) + { + values_list.add(new ValueRow(value.key, value.value)); + } + + option_details_hbox.add(values_vbox); + } + + option_details_hbox.show_all(); + } + + private class OptionRow: ListBoxRow + { + public Tweak.Option option { get; construct; } + + public OptionRow(Tweak.Option option) + { + Object(option: option); + } + + construct + { + get_style_context().add_class("option"); + + var vbox = new Box(Orientation.VERTICAL, 0); + vbox.hexpand = true; + vbox.valign = Align.CENTER; + + var title = new Label(option.name ?? option.id); + title.get_style_context().add_class("title"); + title.hexpand = true; + title.ellipsize = Pango.EllipsizeMode.END; + title.xalign = 0; + vbox.add(title); + + if(option.description != null) + { + var description = new Label(option.description); + description.get_style_context().add_class("description"); + description.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + description.tooltip_text = description.label; + description.hexpand = true; + description.ellipsize = Pango.EllipsizeMode.END; + description.xalign = 0; + vbox.add(description); + } + + child = vbox; + } + } + + private class PresetRow: ListBoxRow + { + public Tweak.Option.Preset preset { get; construct; } + + public bool selected { get; set; } + public RadioButton? radio { get; construct; } + + public PresetRow(Tweak.Option.Preset preset, RadioButton? prev_radio = null) + { + Object(preset: preset, radio: new RadioButton.from_widget(prev_radio), selectable: false, activatable: true); + } + + construct + { + get_style_context().add_class("preset"); + + var grid = new Grid(); + grid.hexpand = true; + grid.valign = Align.CENTER; + + radio.set_events(0); + radio.can_focus = false; + radio.valign = Align.CENTER; + + var title = new Label(preset.name ?? preset.id); + title.get_style_context().add_class("title"); + title.hexpand = true; + title.ellipsize = Pango.EllipsizeMode.END; + title.xalign = 0; + + grid.attach(title, 0, 0); + + if(preset.description != null) + { + var description = new Label(preset.description); + description.get_style_context().add_class("description"); + description.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + description.tooltip_text = description.label; + description.hexpand = true; + description.ellipsize = Pango.EllipsizeMode.END; + description.xalign = 0; + + grid.attach(description, 0, 1); + grid.attach(radio, 1, 0, 1, 2); + } + else + { + grid.attach(radio, 1, 0); + } + + bind_property("selected", radio, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + + child = grid; + } + } + + private class ValueRow: ListBoxRow + { + public string value { get; construct; } + public string description { get; construct; } + + public bool selected { get; set; } + + public ValueRow(string value, string description) + { + Object(value: value, description: description, selectable: false, activatable: true); + } + + construct + { + get_style_context().add_class("value"); + + var grid = new Grid(); + grid.hexpand = true; + grid.valign = Align.CENTER; + + var selected = new CheckButton(); + selected.set_events(0); + selected.can_focus = false; + selected.valign = Align.CENTER; + + var title = new Label(this.value); + title.get_style_context().add_class("title"); + title.hexpand = true; + title.ellipsize = Pango.EllipsizeMode.END; + title.xalign = 0; + + grid.attach(title, 0, 0); + + var description = new Label(this.description); + description.get_style_context().add_class("description"); + description.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + description.tooltip_text = description.label; + description.hexpand = true; + description.ellipsize = Pango.EllipsizeMode.END; + description.xalign = 0; + + grid.attach(description, 0, 1); + grid.attach(selected, 1, 0, 1, 2); + + bind_property("selected", selected, "active", BindingFlags.SYNC_CREATE); + + child = grid; + } + } + } +} diff --git a/src/ui/widgets/tweaks/TweakRow.vala b/src/ui/widgets/tweaks/TweakRow.vala new file mode 100644 index 00000000..59afac84 --- /dev/null +++ b/src/ui/widgets/tweaks/TweakRow.vala @@ -0,0 +1,177 @@ +/* +This file is part of GameHub. +Copyright (C) 2018-2019 Anatoliy Kashkin + +GameHub is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GameHub is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GameHub. If not, see . +*/ + +using Gtk; +using Gdk; +using Gee; + +using GameHub.UI.Widgets; +using GameHub.UI.Widgets.Settings; + +using GameHub.Data; +using GameHub.Data.Tweaks; +using GameHub.Data.Runnables; + +namespace GameHub.UI.Widgets.Tweaks +{ + public class TweakRow: ListBoxRow, ActivatableSetting + { + public Tweak tweak { get; construct; } + public Traits.Game.SupportsTweaks? game { get; construct; default = null; } + + public TweakRow(Tweak tweak, Traits.Game.SupportsTweaks? game=null) + { + Object(tweak: tweak, game: game, activatable: true, selectable: false); + } + + construct + { + get_style_context().add_class("setting"); + get_style_context().add_class("tweak-setting"); + + var grid = new Grid(); + grid.column_spacing = 12; + + var icon = new Image.from_icon_name(tweak.icon, IconSize.LARGE_TOOLBAR); + icon.valign = Align.CENTER; + + var name = new Label(tweak.name ?? tweak.id); + name.get_style_context().add_class("title"); + name.hexpand = true; + name.ellipsize = Pango.EllipsizeMode.END; + name.xalign = 0; + name.valign = Align.CENTER; + + var description = new Label(tweak.description ?? _("No description")); + description.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + description.get_style_context().add_class("description"); + description.tooltip_text = tweak.description; + description.hexpand = true; + description.ellipsize = Pango.EllipsizeMode.END; + description.xalign = 0; + description.valign = Align.CENTER; + + var install = new Button.with_label(_("Install")); + install.valign = Align.CENTER; + install.sensitive = false; + + var enabled = new Switch(); + enabled.active = tweak.is_enabled(game); + enabled.valign = Align.CENTER; + + var buttons_hbox = new Box(Orientation.HORIZONTAL, 0); + buttons_hbox.valign = Align.CENTER; + + grid.attach(icon, 0, 0, 1, 2); + grid.attach(name, 1, 0); + grid.attach(description, 1, 1); + grid.attach(buttons_hbox, 2, 0, 1, 2); + grid.attach(enabled, 4, 0, 1, 2); + + if(tweak.url != null) + { + var url = new Button.from_icon_name("web-symbolic", IconSize.BUTTON); + url.tooltip_markup = """%s%s%s""".printf(_("Open URL"), "\n", tweak.url); + url.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); + + url.clicked.connect(() => { + Utils.open_uri(tweak.url); + }); + + buttons_hbox.add(url); + } + + if(tweak.file != null && tweak.file.query_exists()) + { + var edit = new Button.from_icon_name("accessories-text-editor-symbolic", IconSize.BUTTON); + edit.tooltip_markup = """%s%s%s""".printf(_("Edit file"), "\n", tweak.file.get_path()); + edit.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); + + edit.clicked.connect(() => { + Utils.open_uri(tweak.file.get_uri()); + }); + + buttons_hbox.add(edit); + } + + MenuButton? options = null; + if(tweak.options != null && tweak.options.size > 0) + { + var options_popover = new TweakOptionsPopover(tweak); + + options = new MenuButton(); + options.tooltip_text = _("Options"); + options.get_style_context().add_class(Gtk.STYLE_CLASS_FLAT); + options.image = new Image.from_icon_name("gh-settings-cog-symbolic", IconSize.BUTTON); + options.popover = options_popover; + options_popover.position = PositionType.LEFT; + buttons_hbox.add(options); + } + + var unavailable_reqs = tweak.get_unavailable_requirements(); + if(unavailable_reqs != null) + { + string[] reqs = {}; + + if(unavailable_reqs.executables != null && unavailable_reqs.executables.size != 0) + { + reqs += """%s%s""".printf("\n", unavailable_reqs.executables.size == 1 + ? _("Requires an executable") + : _("Requires one of the executables")); + foreach(var executable in unavailable_reqs.executables) + { + reqs += "• %s".printf(executable); + } + } + + if(unavailable_reqs.kernel_modules != null) + { + reqs += """%s%s""".printf("\n", unavailable_reqs.kernel_modules.size == 1 + ? _("Requires a kernel module") + : _("Requires one of the kernel modules")); + + foreach(var kmod in unavailable_reqs.kernel_modules) + { + reqs += "• %s".printf(kmod); + } + } + + if(options != null) + { + options.sensitive = false; + } + + enabled.sensitive = false; + activatable = false; + icon.icon_name = "action-unavailable-symbolic"; + icon.tooltip_markup = enabled.tooltip_markup = string.joinv("\n", reqs).strip(); + } + else + { + enabled.notify["active"].connect(() => { + tweak.set_enabled(enabled.active, game); + }); + setting_activated.connect(() => { + enabled.activate(); + }); + } + + child = grid; + } + } +} diff --git a/src/ui/widgets/tweaks/TweaksList.vala b/src/ui/widgets/tweaks/TweaksList.vala new file mode 100644 index 00000000..ef6b03a6 --- /dev/null +++ b/src/ui/widgets/tweaks/TweaksList.vala @@ -0,0 +1,109 @@ +/* +This file is part of GameHub. +Copyright (C) 2018-2019 Anatoliy Kashkin + +GameHub is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GameHub is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GameHub. If not, see . +*/ + +using Gtk; +using Gdk; +using Gee; + +using GameHub.UI.Widgets; +using GameHub.UI.Widgets.Settings; + +using GameHub.Data; +using GameHub.Data.Tweaks; +using GameHub.Data.Runnables; + +namespace GameHub.UI.Widgets.Tweaks +{ + public class TweaksList: Notebook + { + public Traits.Game.SupportsTweaks? game { get; construct; default = null; } + + public TweaksList(Traits.Game.SupportsTweaks? game = null) + { + Object(game: game, show_border: false, expand: true); + } + + construct + { + update(); + } + + public void update(CompatTool? compat_tool=null) + { + this.foreach(w => w.destroy()); + + var tweaks = Tweak.load_tweaks_grouped(game == null); + + if(tweaks != null && tweaks.size > 0) + { + foreach(var group in tweaks.entries) + { + var tab = new TweakGroupTab(game, compat_tool, group.key ?? _("Ungrouped"), group.value); + append_page(tab, new Label(tab.group)); + } + show_tabs = tweaks.size > 1; + } + else + { + append_page(new AlertView(_("No tweaks"), _("No tweaks were found\nAdd your tweaks into one of the tweak directories"), "dialog-warning-symbolic")); + show_tabs = false; + } + } + + private class TweakGroupTab: ScrolledWindow + { + public Traits.Game.SupportsTweaks? game { get; construct; default = null; } + public CompatTool? compat_tool { get; construct; default = null; } + public string? group { get; construct; default = null; } + public HashMap? tweaks { get; construct; default = null; } + + public TweakGroupTab(Traits.Game.SupportsTweaks? game = null, CompatTool? compat_tool = null, string? group = null, HashMap? tweaks = null) + { + Object(game: game, compat_tool: compat_tool, group: group, tweaks: tweaks, hscrollbar_policy: PolicyType.NEVER, expand: true); + } + + construct + { + var tweaks_list = new ListBox(); + tweaks_list.selection_mode = SelectionMode.NONE; + child = tweaks_list; + + if(tweaks != null) + { + foreach(var tweak in tweaks.values) + { + if(game == null || tweak.is_applicable_to(game, compat_tool)) + { + tweaks_list.add(new TweakRow(tweak, game)); + } + } + } + + tweaks_list.row_activated.connect(row => { + var setting = row as ActivatableSetting; + if(setting != null) + { + setting.setting_activated(); + } + }); + + show_all(); + } + } + } +} diff --git a/src/utils/Gamepad.vala b/src/utils/Gamepad.vala index a49bc830..b2d914b1 100644 --- a/src/utils/Gamepad.vala +++ b/src/utils/Gamepad.vala @@ -253,14 +253,14 @@ namespace GameHub.Utils.Gamepad public static unowned X.Display? get_xdisplay(bool wnd_active = true) { - foreach(var wnd in Gtk.Window.list_toplevels()) + foreach(var wnd in Gtk.Window.list_toplevels()) { if(!wnd_active || wnd.is_active) { - var display = wnd.screen.get_display(); - if(display is Gdk.X11.Display) - { - return (wnd.screen.get_display() as Gdk.X11.Display).get_xdisplay(); + var display = wnd.screen.get_display(); + if(display is Gdk.X11.Display) + { + return (wnd.screen.get_display() as Gdk.X11.Display).get_xdisplay(); } } } @@ -269,7 +269,7 @@ namespace GameHub.Utils.Gamepad public static bool is_supported() { - return get_xdisplay(false) != null; + return get_xdisplay(false) != null; } private static void emit_key_event(uint key_code, bool press) @@ -278,7 +278,7 @@ namespace GameHub.Utils.Gamepad unowned var xdisplay = get_xdisplay(true); if(xdisplay != null) { - XTest.fake_key_event(xdisplay, xdisplay.keysym_to_keycode((ulong) key_code), press, X.CURRENT_TIME); + XTest.fake_key_event(xdisplay, xdisplay.keysym_to_keycode((ulong) key_code), press, X.CURRENT_TIME); Gamepad.ButtonPressed = true; } } diff --git a/src/utils/Utils.vala b/src/utils/Utils.vala index f136f0a5..3fdba1f3 100644 --- a/src/utils/Utils.vala +++ b/src/utils/Utils.vala @@ -353,7 +353,7 @@ namespace GameHub.Utils } } - private static string? distro = null; + private static string? distro = null; public static string get_distro() { if(distro != null) return distro; @@ -428,22 +428,22 @@ namespace GameHub.Utils #endif } - private static string[]? kmods = null; + private static string[]? kmods = null; public static bool is_kernel_module_loaded(string? name) { - if(name == null || name.length == 0) return true; - if(kmods == null) - { - kmods = {}; - string proc_modules; + if(name == null || name.length == 0) return true; + if(kmods == null) + { + kmods = {}; + string proc_modules; FileUtils.get_contents("/proc/modules", out proc_modules); var module_lines = proc_modules.split("\n"); - foreach(var line in module_lines) - { - kmods += line.split(" ")[0]; - } - } - return name in kmods; + foreach(var line in module_lines) + { + kmods += line.split(" ")[0]; + } + } + return name in kmods; } public static async void sleep_async(uint interval, int priority=GLib.Priority.DEFAULT)