From 9354042379a34afb9f03f6f503e46382e7f0bdc1 Mon Sep 17 00:00:00 2001 From: Rafostar <40623528+Rafostar@users.noreply.github.com> Date: Sun, 25 Oct 2020 10:14:14 +0100 Subject: [PATCH] Add preferences dialog Allows customizing various settings. For now it includes player seeking times and mode customization. More options will be added in the future. --- build-aux/meson/postinstall.py | 5 +- clapper_src/controls.js | 19 ++- clapper_src/menu.js | 19 ++- clapper_src/player.js | 133 ++++++------------- clapper_src/playerBase.js | 130 ++++++++++++++++++ clapper_src/prefs.js | 89 +++++++++++++ clapper_src/prefsBase.js | 118 ++++++++++++++++ clapper_src/widget.js | 13 +- css/styles.css | 9 ++ data/com.github.rafostar.Clapper.gschema.xml | 21 +++ data/meson.build | 3 + pkgs/rpm/clapper.spec | 6 +- ui/clapper.ui | 6 + 13 files changed, 460 insertions(+), 111 deletions(-) create mode 100644 clapper_src/playerBase.js create mode 100644 clapper_src/prefs.js create mode 100644 clapper_src/prefsBase.js create mode 100644 data/com.github.rafostar.Clapper.gschema.xml diff --git a/build-aux/meson/postinstall.py b/build-aux/meson/postinstall.py index 21f2a925..213a4a61 100755 --- a/build-aux/meson/postinstall.py +++ b/build-aux/meson/postinstall.py @@ -18,6 +18,5 @@ if not destdir: print('Updating desktop database...') call(['update-desktop-database', '-q', path.join(sharedir, 'applications')]) - # NO CLAPPER SCHEMAS YET - #print('Compiling GSettings schemas...') - #call(['glib-compile-schemas', path.join(sharedir, 'glib-2.0', 'schemas')]) + print('Compiling GSettings schemas...') + call(['glib-compile-schemas', path.join(sharedir, 'glib-2.0', 'schemas')]) diff --git a/clapper_src/controls.js b/clapper_src/controls.js index a757ccd4..6db6e16b 100644 --- a/clapper_src/controls.js +++ b/clapper_src/controls.js @@ -25,7 +25,7 @@ class ClapperControls extends Gtk.Box this.currentVolume = 0; this.currentPosition = 0; this.currentDuration = 0; - this.isPositionSeeking = false; + this.isPositionDragging = false; this.durationFormated = '00:00:00'; this.elapsedInitial = '00:00:00/00:00:00'; @@ -263,6 +263,11 @@ class ClapperControls extends Gtk.Box can_focus: false, visible: false, }); + let scrollController = new Gtk.EventControllerScroll(); + scrollController.set_flags(Gtk.EventControllerScrollFlags.BOTH_AXES); + scrollController.connect('scroll', this._onPositionScaleScroll.bind(this)); + this.positionScale.add_controller(scrollController); + this.positionScale.add_css_class('positionscale'); this.positionScale.connect( 'value-changed', this._onPositionScaleValueChanged.bind(this) @@ -276,6 +281,8 @@ class ClapperControls extends Gtk.Box ); this.positionAdjustment = this.positionScale.get_adjustment(); + this.positionAdjustment.set_page_increment(0); + this.positionAdjustment.set_step_increment(8); let box = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, @@ -369,6 +376,12 @@ class ClapperControls extends Gtk.Box player.toggle_play(); } + _onPositionScaleScroll(controller, dx, dy) + { + let { player } = this.get_ancestor(Gtk.Grid); + player._onScroll(controller, dx || dy, 0); + } + _onPositionScaleValueChanged(scale) { let positionSeconds = Math.round(scale.get_value()); @@ -406,9 +419,9 @@ class ClapperControls extends Gtk.Box _onPositionScaleDragging(scale) { - let isPositionSeeking = scale.has_css_class('dragging'); + let isPositionDragging = scale.has_css_class('dragging'); - if((this.isPositionSeeking = isPositionSeeking)) + if((this.isPositionDragging = isPositionDragging)) return; let clapperWidget = this.get_ancestor(Gtk.Grid); diff --git a/clapper_src/menu.js b/clapper_src/menu.js index ba1d6110..f52fbb67 100644 --- a/clapper_src/menu.js +++ b/clapper_src/menu.js @@ -1,7 +1,9 @@ const { GObject, Gst, Gtk } = imports.gi; const Misc = imports.clapper_src.misc; +const { Prefs } = imports.clapper_src.prefs; var actions = [ + prefs, about ]; @@ -9,6 +11,21 @@ var accels = [ ['app.quit', ['q']], ]; +function prefs(window, appName) +{ + let prefs = new Prefs(); + let prefsDialog = new Gtk.Dialog({ + title: 'Preferences', + modal: true, + transient_for: window, + child: prefs, + default_width: 400, + default_height: 320, + }); + prefsDialog.connect('close-request', () => prefsDialog.run_dispose()); + prefsDialog.present(); +} + function about(window, appName) { let gstVer = [ @@ -37,6 +54,6 @@ function about(window, appName) system_information: osInfo, transient_for: window }); - + aboutDialog.connect('close-request', () => aboutDialog.run_dispose()); aboutDialog.present(); } diff --git a/clapper_src/player.js b/clapper_src/player.js index 6eb357b4..b6cf22b8 100644 --- a/clapper_src/player.js +++ b/clapper_src/player.js @@ -1,49 +1,19 @@ const { Gdk, Gio, GLib, GObject, Gst, GstPlayer, Gtk } = imports.gi; const ByteArray = imports.byteArray; +const { PlayerBase } = imports.clapper_src.playerBase; const Debug = imports.clapper_src.debug; -const GSTPLAYER_DEFAULTS = { - position_update_interval: 1000, - seek_accurate: false, - user_agent: 'clapper', -}; - let { debug } = Debug; var Player = GObject.registerClass( -class ClapperPlayer extends GstPlayer.Player +class ClapperPlayer extends PlayerBase { - _init(opts) + _init() { - opts = opts || {}; - opts = Object.assign({}, GSTPLAYER_DEFAULTS, opts); - - if(!Gst.is_initialized()) - Gst.init(null); - - let plugin = 'gtk4glsink'; - let gtkglsink = Gst.ElementFactory.make(plugin, null); - - if(!gtkglsink) { - return debug(new Error( - `Could not load "${plugin}".` - + ' Do you have gstreamer-plugins-good-gtk4 installed?' - )); - } - - let glsinkbin = Gst.ElementFactory.make('glsinkbin', null); - glsinkbin.sink = gtkglsink; - - let dispatcher = new GstPlayer.PlayerGMainContextSignalDispatcher(); - let renderer = new GstPlayer.PlayerVideoOverlayVideoRenderer({ - video_sink: glsinkbin - }); - - super._init({ - signal_dispatcher: dispatcher, - video_renderer: renderer - }); + super._init(); + this.state = GstPlayer.PlayerState.STOPPED; + this.cursorInPlayer = false; this.is_local_file = false; this.seek_done = true; this.dragAllowed = false; @@ -55,29 +25,6 @@ class ClapperPlayer extends GstPlayer.Player this._playerSignals = []; this._widgetSignals = []; - let config = this.get_config(); - - for(let setting of Object.keys(GSTPLAYER_DEFAULTS)) { - let setOption = GstPlayer.Player[`config_set_${setting}`]; - if(!setOption) { - debug(`unsupported option: ${setting}`, 'LEVEL_WARNING'); - continue; - } - setOption(config, opts[setting]); - } - - this.set_config(config); - this.set_mute(false); - this.set_plugin_rank('vah264dec', 300); - - this.widget = gtkglsink.widget; - this.widget.vexpand = true; - this.widget.hexpand = true; - - this.state = GstPlayer.PlayerState.STOPPED; - this.visualization_enabled = false; - this.fast_seeking = opts.fast_seeking || false; - this._playlist = []; this._trackId = 0; @@ -85,8 +32,6 @@ class ClapperPlayer extends GstPlayer.Player this._hideControlsTimeout = null; this._updateTimeTimeout = null; - this.cursorInPlayer = false; - let clickGesture = new Gtk.GestureClick(); clickGesture.set_button(0); clickGesture.connect('pressed', this._onWidgetPressed.bind(this)); @@ -215,16 +160,17 @@ class ClapperPlayer extends GstPlayer.Player seek(position) { + this.seek_done = false; + if(this.state === GstPlayer.PlayerState.STOPPED) this.pause(); if(position < 0) position = 0; - this.seek_done = false; - debug(`player is seeking to position: ${position}`); + debug(`${this.seekingMode} seeking to position: ${position}`); - if(!this.fast_seeking) + if(this.seekingMode !== 'fast') return super.seek(position); let pipeline = this.get_pipeline(); @@ -252,10 +198,32 @@ class ClapperPlayer extends GstPlayer.Player adjust_position(isIncrease) { - let { controls } = this.widget.get_ancestor(Gtk.Grid); + this.seek_done = false; + + let { controls } = this.widget.get_ancestor(Gtk.Grid); + let max = controls.positionAdjustment.get_upper(); + let seekingValue = this.settings.get_int('seeking-value'); + let seekingUnit = this.settings.get_string('seeking-unit'); + + switch(seekingUnit) { + case 'minute': + seekingValue *= 60; + break; + case 'percentage': + seekingValue = max * seekingValue / 100; + break; + default: + break; + } + + if(!isIncrease) + seekingValue *= -1; + + let positionSeconds = controls.positionScale.get_value() + seekingValue; + + if(positionSeconds > max) + positionSeconds = max; - let value = (isIncrease) ? 10 : -10; - let positionSeconds = controls.positionScale.get_value() + value; controls.positionScale.set_value(positionSeconds); } @@ -277,27 +245,6 @@ class ClapperPlayer extends GstPlayer.Player this[action](); } - set_subtitle_font_desc(desc) - { - let pipeline = this.get_pipeline(); - pipeline.subtitle_font_desc = desc; - } - - set_plugin_rank(name, rank) - { - debug(`changing rank of plugin: ${name}`); - - let gstRegistry = Gst.Registry.get(); - let feature = gstRegistry.lookup_feature(name); - if(!feature) - return debug(`plugin unavailable: ${name}`); - - let oldRank = feature.get_rank(); - feature.set_rank(rank); - - debug(`changed rank: ${oldRank} -> ${rank} for ${name}`); - } - selfConnect(signal, fn) { this._playerSignals.push( @@ -439,13 +386,12 @@ class ClapperPlayer extends GstPlayer.Player case Gdk.KEY_Right: bool = true; case Gdk.KEY_Left: - clapperWidget.controls.isPositionSeeking = true; + this.adjust_position(bool); this._clearTimeout('hideControls'); if(this.keyPressCount > 1) { clapperWidget.revealerBottom.set_can_focus(false); clapperWidget.revealerBottom.revealChild(true); } - this.adjust_position(bool); break; default: break; @@ -477,7 +423,6 @@ class ClapperPlayer extends GstPlayer.Player ); this.seek_seconds(value); this._setHideControlsTimeout(); - clapperWidget.controls.isPositionSeeking = false; break; case Gdk.KEY_F11: clapperWidget.toggleFullscreen(); @@ -619,8 +564,12 @@ class ClapperPlayer extends GstPlayer.Player let isHorizontal = Math.abs(dx) >= Math.abs(dy); let isIncrease = (isHorizontal) ? dx < 0 : dy < 0; - if(isHorizontal) + if(isHorizontal) { this.adjust_position(isIncrease); + let { controls } = this.widget.get_ancestor(Gtk.Grid); + let value = Math.round(controls.positionScale.get_value()); + this.seek_seconds(value); + } else this.adjust_volume(isIncrease); diff --git a/clapper_src/playerBase.js b/clapper_src/playerBase.js new file mode 100644 index 00000000..9f327d11 --- /dev/null +++ b/clapper_src/playerBase.js @@ -0,0 +1,130 @@ +const { Gio, GObject, Gst, GstPlayer, Gtk } = imports.gi; +const Debug = imports.clapper_src.debug; + +let { debug } = Debug; + +var PlayerBase = GObject.registerClass( +class ClapperPlayerBase extends GstPlayer.Player +{ + _init() + { + if(!Gst.is_initialized()) + Gst.init(null); + + let plugin = 'gtk4glsink'; + let gtkglsink = Gst.ElementFactory.make(plugin, null); + + if(!gtkglsink) { + return debug(new Error( + `Could not load "${plugin}".` + + ' Do you have gstreamer-plugins-good-gtk4 installed?' + )); + } + + let glsinkbin = Gst.ElementFactory.make('glsinkbin', null); + glsinkbin.sink = gtkglsink; + + let dispatcher = new GstPlayer.PlayerGMainContextSignalDispatcher(); + let renderer = new GstPlayer.PlayerVideoOverlayVideoRenderer({ + video_sink: glsinkbin + }); + + super._init({ + signal_dispatcher: dispatcher, + video_renderer: renderer + }); + + this.widget = gtkglsink.widget; + this.widget.vexpand = true; + this.widget.hexpand = true; + + this.settings = new Gio.Settings({ + schema_id: 'com.github.rafostar.Clapper' + }); + + this.visualization_enabled = false; + this.set_plugin_rank('vah264dec', 300); + + this.set_initial_config(); + this.set_and_bind_settings(); + + this.settings.connect('changed', this._onSettingsKeyChanged.bind(this)); + } + + set_and_bind_settings() + { + let settingsToSet = [ + 'seeking-mode', + ]; + + for(let key of settingsToSet) + this._onSettingsKeyChanged(this.settings, key); + + //let flag = Gio.SettingsBindFlags.GET; + } + + set_initial_config() + { + let gstPlayerConfig = { + position_update_interval: 1000, + user_agent: 'clapper', + }; + + for(let option of Object.keys(gstPlayerConfig)) + this.set_config_option(option, gstPlayerConfig[option]); + + this.set_mute(false); + } + + set_config_option(option, value) + { + let setOption = GstPlayer.Player[`config_set_${option}`]; + if(!setOption) + return debug(`unsupported option: ${option}`, 'LEVEL_WARNING'); + + let config = this.get_config(); + setOption(config, value); + this.set_config(config); + } + + /* FIXME: add in prefs and move to bind_settings() */ + set_subtitle_font_desc(desc) + { + let pipeline = this.get_pipeline(); + pipeline.subtitle_font_desc = desc; + } + + set_plugin_rank(name, rank) + { + debug(`changing rank of plugin: ${name}`); + + let gstRegistry = Gst.Registry.get(); + let feature = gstRegistry.lookup_feature(name); + if(!feature) + return debug(`plugin unavailable: ${name}`); + + let oldRank = feature.get_rank(); + feature.set_rank(rank); + + debug(`changed rank: ${oldRank} -> ${rank} for ${name}`); + } + + _onSettingsKeyChanged(settings, key) + { + switch(key) { + case 'seeking-mode': + this.seekingMode = settings.get_string('seeking-mode'); + switch(this.seekingMode) { + case 'accurate': + this.set_config_option('seek_accurate', true); + break; + default: + this.set_config_option('seek_accurate', false); + break; + } + break; + default: + break; + } + } +}); diff --git a/clapper_src/prefs.js b/clapper_src/prefs.js new file mode 100644 index 00000000..367a3eb1 --- /dev/null +++ b/clapper_src/prefs.js @@ -0,0 +1,89 @@ +const { GObject, Gtk } = imports.gi; +const PrefsBase = imports.clapper_src.prefsBase; + +let GeneralPage = GObject.registerClass( +class ClapperGeneralPage extends PrefsBase.Grid +{ + _init() + { + super._init(); + + let label; + let widget; + + label = this.getLabel('Seeking', true); + this.addToGrid(label); + + label = this.getLabel('Mode:'); + widget = this.getComboBoxText([ + ['normal', "Normal"], + ['accurate', "Accurate"], + /* Needs gstplayer pipeline ref count fix */ + //['fast', "Fast"], + ], 'seeking-mode'); + this.addToGrid(label, widget); + + label = this.getLabel('Value:'); + widget = this.getSpinButton(1, 99, 'seeking-value'); + this.addToGrid(label, widget); + + label = this.getLabel('Unit:'); + widget = this.getComboBoxText([ + ['second', "Second"], + ['minute', "Minute"], + ['percentage', "Percentage"], + ], 'seeking-unit'); + this.addToGrid(label, widget); + } +}); + +let GStreamerPage = GObject.registerClass( +class ClapperGStreamerPage extends PrefsBase.Grid +{ + _init() + { + super._init(); + + let label; + let widget; + + label = this.getLabel('Plugin Ranking', true); + this.addToGrid(label); + } +}); + +var Prefs = GObject.registerClass( +class ClapperPrefs extends Gtk.Box +{ + _init() + { + super._init({ + orientation: Gtk.Orientation.VERTICAL, + }); + + this.add_css_class('prefsbox'); + + let pages = [ + { + title: 'General', + widget: GeneralPage, + }, +/* + { + title: 'Advanced', + pages: [ + { + title: 'GStreamer', + widget: GStreamerPage, + } + ] + } +*/ + ]; + + let prefsNotebook = new PrefsBase.Notebook(pages); + prefsNotebook.add_css_class('prefsnotebook'); + + this.append(prefsNotebook); + } +}); diff --git a/clapper_src/prefsBase.js b/clapper_src/prefsBase.js new file mode 100644 index 00000000..7e289501 --- /dev/null +++ b/clapper_src/prefsBase.js @@ -0,0 +1,118 @@ +const { Gio, GObject, Gtk } = imports.gi; + +var Notebook = GObject.registerClass( +class ClapperPrefsNotebook extends Gtk.Notebook +{ + _init(pages) + { + super._init({ + vexpand: true, + hexpand: true, + }); + + this.addArrayPages(pages); + } + + addArrayPages(array) + { + for(let obj of array) + this.addObjectPages(obj); + } + + addObjectPages(item) + { + let widget = (item.pages) + ? new Notebook(item.pages) + : new item.widget(); + + this.addToNotebook(widget, item.title); + } + + addToNotebook(widget, title) + { + let label = new Gtk.Label({ + label: title, + }); + this.append_page(widget, label); + } +}); + +var Grid = GObject.registerClass( +class ClapperPrefsGrid extends Gtk.Grid +{ + _init() + { + super._init({ + row_spacing: 6, + column_spacing: 20, + }); + + this.settings = new Gio.Settings({ + schema_id: 'com.github.rafostar.Clapper' + }); + this.flag = Gio.SettingsBindFlags.DEFAULT; + + this.gridIndex = 0; + this.widgetDefaults = { + width_request: 160, + halign: Gtk.Align.END, + valign: Gtk.Align.CENTER, + }; + } + + addToGrid(leftWidget, rightWidget) + { + let spanWidth = 2; + + if(rightWidget) { + spanWidth = 1; + this.attach(rightWidget, 1, this.gridIndex, 1, 1); + } + + this.attach(leftWidget, 0, this.gridIndex, spanWidth, 1); + this.gridIndex++; + } + + getLabel(text, isTitle) + { + let marginLR = 0; + let marginBottom = (isTitle) ? 2 : 0; + + if(isTitle) + text = '' + text + ''; + else + marginLR = 12; + + return new Gtk.Label({ + label: text, + use_markup: true, + hexpand: true, + halign: Gtk.Align.START, + margin_bottom: marginBottom, + margin_start: marginLR, + margin_end: marginLR, + }); + } + + getComboBoxText(entries, setting) + { + let comboBox = new Gtk.ComboBoxText(this.widgetDefaults); + + for(let entry of entries) + comboBox.append(entry[0], entry[1]); + + this.settings.bind(setting, comboBox, 'active-id', this.flag); + + return comboBox; + } + + getSpinButton(min, max, setting) + { + let spinButton = new Gtk.SpinButton(this.widgetDefaults); + spinButton.set_range(min, max); + spinButton.set_increments(1, 2); + this.settings.bind(setting, spinButton, 'value', this.flag); + + return spinButton; + } +}); diff --git a/clapper_src/widget.js b/clapper_src/widget.js index 6eed3a29..b7c91c65 100644 --- a/clapper_src/widget.js +++ b/clapper_src/widget.js @@ -322,17 +322,8 @@ var Widget = GObject.registerClass({ if(this.controls.currentDuration === duration) return; - let increment = (duration < 1) - ? 0 - : (duration < 100) - ? 1 - : duration / 100; - - this.controls.positionAdjustment.set_upper(duration); - this.controls.positionAdjustment.set_step_increment(increment); - this.controls.positionAdjustment.set_page_increment(increment); - this.controls.currentDuration = duration; + this.controls.positionAdjustment.set_upper(duration); this.controls.durationFormated = Misc.getFormatedTime(duration); this.controls.updateElapsedLabel(); } @@ -341,7 +332,7 @@ var Widget = GObject.registerClass({ { if( !this.isSeekable - || this.controls.isPositionSeeking + || this.controls.isPositionDragging || !player.seek_done ) return; diff --git a/css/styles.css b/css/styles.css index e42ef444..5e77adf0 100644 --- a/css/styles.css +++ b/css/styles.css @@ -97,3 +97,12 @@ scale marks { .osd .volumescale trough highlight { min-width: 6px; } + +/* Preferences */ +.prefsbox { + margin: 5px; +} + +.prefsnotebook grid { + margin: 10px; +} diff --git a/data/com.github.rafostar.Clapper.gschema.xml b/data/com.github.rafostar.Clapper.gschema.xml new file mode 100644 index 00000000..7c0a5efa --- /dev/null +++ b/data/com.github.rafostar.Clapper.gschema.xml @@ -0,0 +1,21 @@ + + + + + "normal" + Mode used for seeking + + + 10 + Time amount to seek with single press of arrow keys + + + "second" + Unit to use with seeking value + + + "{}" + Custom values for GStreamer plugin ranking + + + diff --git a/data/meson.build b/data/meson.build index 6b684621..38de1d50 100644 --- a/data/meson.build +++ b/data/meson.build @@ -4,6 +4,9 @@ iconsdir = join_paths(sharedir, 'icons', 'hicolor') install_data('com.github.rafostar.Clapper.svg', install_dir: join_paths(iconsdir, 'scalable', 'apps') ) +install_data('com.github.rafostar.Clapper.gschema.xml', + install_dir: join_paths(sharedir, 'glib-2.0', 'schemas') +) install_data('com.github.rafostar.Clapper.xml', install_dir: join_paths(sharedir, 'mime', 'packages') ) diff --git a/pkgs/rpm/clapper.spec b/pkgs/rpm/clapper.spec index f064a428..8edd7dfc 100644 --- a/pkgs/rpm/clapper.spec +++ b/pkgs/rpm/clapper.spec @@ -105,11 +105,15 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/%{appname}/ %dir %{_datadir}/gjs-1.0 %{_datadir}/gjs-1.0/*.js -%{_datadir}/applications/*.desktop %{_datadir}/icons/hicolor/*/apps/*.svg +%{_datadir}/glib-2.0/schemas/%{appname}.gschema.xml %{_datadir}/mime/packages/%{appname}.xml +%{_datadir}/applications/*.desktop %changelog +* Sun Oct 25 2020 Rafostar - 0.0.0-4 +- Added gschema + * Wed Oct 14 2020 Rafostar - 0.0.0-3 - Update to GTK4 diff --git a/ui/clapper.ui b/ui/clapper.ui index fcf46626..c61b488d 100644 --- a/ui/clapper.ui +++ b/ui/clapper.ui @@ -2,6 +2,12 @@
+ + Preferences + app.prefs + +
+