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.
This commit is contained in:
Rafostar
2020-10-25 10:14:14 +01:00
parent 576440faff
commit 9354042379
13 changed files with 460 additions and 111 deletions

View File

@@ -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')])

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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);

130
clapper_src/playerBase.js Normal file
View File

@@ -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;
}
}
});

89
clapper_src/prefs.js Normal file
View File

@@ -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);
}
});

118
clapper_src/prefsBase.js Normal file
View File

@@ -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 = '<span font="12"><b>' + text + '</b></span>';
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;
}
});

View File

@@ -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;

View File

@@ -97,3 +97,12 @@ scale marks {
.osd .volumescale trough highlight {
min-width: 6px;
}
/* Preferences */
.prefsbox {
margin: 5px;
}
.prefsnotebook grid {
margin: 10px;
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="com.github.rafostar.Clapper">
<schema id="com.github.rafostar.Clapper" path="/com/github/rafostar/Clapper/">
<key name="seeking-mode" type="s">
<default>"normal"</default>
<summary>Mode used for seeking</summary>
</key>
<key name="seeking-value" type="i">
<default>10</default>
<summary>Time amount to seek with single press of arrow keys</summary>
</key>
<key name="seeking-unit" type="s">
<default>"second"</default>
<summary>Unit to use with seeking value</summary>
</key>
<key name="plugin-ranking" type="s">
<default>"{}"</default>
<summary>Custom values for GStreamer plugin ranking</summary>
</key>
</schema>
</schemalist>

View File

@@ -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')
)

View File

@@ -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 <rafostar.github@gmail.com> - 0.0.0-4
- Added gschema
* Wed Oct 14 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-3
- Update to GTK4

View File

@@ -2,6 +2,12 @@
<interface>
<menu id="settingsMenu">
<section>
<item>
<attribute name="label" translatable="yes">Preferences</attribute>
<attribute name="action">app.prefs</attribute>
</item>
</section>
<section>
<!--
<item>
<attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>