mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 07:42:23 +02:00
Major code cleanup
This commit is contained in:
@@ -1,17 +1,12 @@
|
||||
const { Gdk, Gio, GLib, GObject, Gtk, GstPlayer } = imports.gi;
|
||||
const Debug = imports.clapper_src.debug;
|
||||
const { Gio, GObject, Gtk } = imports.gi;
|
||||
const { HeaderBar } = imports.clapper_src.headerbar;
|
||||
const { Interface } = imports.clapper_src.interface;
|
||||
const { Player } = imports.clapper_src.player;
|
||||
const { Widget } = imports.clapper_src.widget;
|
||||
const Menu = imports.clapper_src.menu;
|
||||
const { Window } = imports.clapper_src.window;
|
||||
|
||||
const APP_NAME = pkg.name.substring(
|
||||
const APP_NAME = 'Clapper' || pkg.name.substring(
|
||||
pkg.name.lastIndexOf('.') + 1
|
||||
);
|
||||
|
||||
let { debug } = Debug;
|
||||
|
||||
var App = GObject.registerClass(
|
||||
class ClapperApp extends Gtk.Application
|
||||
{
|
||||
@@ -25,41 +20,24 @@ class ClapperApp extends Gtk.Application
|
||||
playlist: [],
|
||||
};
|
||||
Object.assign(this, defaults, opts);
|
||||
|
||||
this.cssProvider = new Gtk.CssProvider();
|
||||
this.cssProvider.load_from_path(
|
||||
`${pkg.datadir}/${pkg.name}/css/styles.css`
|
||||
);
|
||||
|
||||
this.window = null;
|
||||
this.interface = null;
|
||||
this.player = null;
|
||||
this.dragAllowed = false;
|
||||
|
||||
this.posX = 0;
|
||||
this.posY = 0;
|
||||
}
|
||||
|
||||
vfunc_startup()
|
||||
{
|
||||
super.vfunc_startup();
|
||||
this.window = new Window(this, APP_NAME);
|
||||
|
||||
this.windowRealizeSignal = this.window.connect(
|
||||
'realize', this._onWindowRealize.bind(this)
|
||||
);
|
||||
this.window.connect(
|
||||
'fullscreen-changed', this._onWindowFullscreenChanged.bind(this)
|
||||
);
|
||||
this.window.connect(
|
||||
'close-request', this._onWindowCloseRequest.bind(this)
|
||||
);
|
||||
let window = new Gtk.ApplicationWindow({
|
||||
application: this,
|
||||
title: APP_NAME,
|
||||
});
|
||||
|
||||
for(let action of Menu.actions) {
|
||||
let simpleAction = new Gio.SimpleAction({
|
||||
name: action.name
|
||||
});
|
||||
simpleAction.connect('activate', () => action(this.active_window, APP_NAME));
|
||||
simpleAction.connect('activate', () =>
|
||||
action(this.active_window, APP_NAME)
|
||||
);
|
||||
this.add_action(simpleAction);
|
||||
}
|
||||
let uiBuilder = Gtk.Builder.new_from_file(
|
||||
@@ -68,17 +46,11 @@ class ClapperApp extends Gtk.Application
|
||||
let models = {
|
||||
settingsMenu: uiBuilder.get_object('settingsMenu')
|
||||
};
|
||||
let headerBar = new HeaderBar(this.window, models);
|
||||
let headerBar = new HeaderBar(window, models);
|
||||
window.set_titlebar(headerBar);
|
||||
|
||||
this.interface = new Interface();
|
||||
this.interface.addHeaderBar(headerBar, APP_NAME);
|
||||
|
||||
this.interface.controls.unfullscreenButton.connect(
|
||||
'clicked', () => this.active_window.unfullscreen()
|
||||
);
|
||||
|
||||
this.window.set_titlebar(this.interface.headerBar);
|
||||
this.window.set_child(this.interface);
|
||||
let clapperWidget = new Widget();
|
||||
window.set_child(clapperWidget);
|
||||
}
|
||||
|
||||
vfunc_activate()
|
||||
@@ -89,310 +61,21 @@ class ClapperApp extends Gtk.Application
|
||||
'show', this._onWindowShow.bind(this)
|
||||
);
|
||||
this.active_window.present();
|
||||
|
||||
Gtk.StyleContext.add_provider_for_display(
|
||||
Gdk.Display.get_default(),
|
||||
this.cssProvider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
);
|
||||
}
|
||||
|
||||
run(arr)
|
||||
{
|
||||
arr = arr || [];
|
||||
super.run(arr);
|
||||
}
|
||||
|
||||
setHideCursorTimeout()
|
||||
{
|
||||
this.clearTimeout('hideCursor');
|
||||
this.hideCursorTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
||||
this.hideCursorTimeout = null;
|
||||
|
||||
if(this.player.motionController.is_pointer)
|
||||
this.player.widget.set_cursor(this.blankCursor);
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
setHideControlsTimeout()
|
||||
{
|
||||
this.clearTimeout('hideControls');
|
||||
this.hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => {
|
||||
this.hideControlsTimeout = null;
|
||||
|
||||
if(this.window.isFullscreen && this.player.motionController.is_pointer) {
|
||||
this.clearTimeout('updateTime');
|
||||
this.interface.revealControls(false);
|
||||
}
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
setUpdateTimeInterval()
|
||||
{
|
||||
this.clearTimeout('updateTime');
|
||||
let nextUpdate = this.interface.updateTime();
|
||||
this.updateTimeTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, nextUpdate, () => {
|
||||
this.updateTimeTimeout = null;
|
||||
|
||||
if(this.window.isFullscreen)
|
||||
this.setUpdateTimeInterval();
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
clearTimeout(name)
|
||||
{
|
||||
if(!this[`${name}Timeout`])
|
||||
return;
|
||||
|
||||
GLib.source_remove(this[`${name}Timeout`]);
|
||||
this[`${name}Timeout`] = null;
|
||||
|
||||
if(name === 'updateTime')
|
||||
debug('cleared update time interval');
|
||||
}
|
||||
|
||||
_onWindowRealize()
|
||||
{
|
||||
this.window.disconnect(this.windowRealizeSignal);
|
||||
|
||||
this.player = new Player();
|
||||
|
||||
if(!this.player.widget)
|
||||
return this.quit();
|
||||
|
||||
this.player.widget.width_request = 960;
|
||||
this.player.widget.height_request = 540;
|
||||
|
||||
this.interface.addPlayer(this.player);
|
||||
this.player.connect('state-changed', this._onPlayerStateChanged.bind(this));
|
||||
|
||||
this.player.clickGesture.connect(
|
||||
'pressed', this._onPlayerPressed.bind(this)
|
||||
);
|
||||
this.player.keyController.connect(
|
||||
'key-pressed', this._onPlayerKeyPressed.bind(this)
|
||||
);
|
||||
this.player.motionController.connect(
|
||||
'enter', this._onPlayerEnter.bind(this)
|
||||
);
|
||||
this.player.motionController.connect(
|
||||
'leave', this._onPlayerLeave.bind(this)
|
||||
);
|
||||
this.player.motionController.connect(
|
||||
'motion', this._onPlayerMotion.bind(this)
|
||||
);
|
||||
this.player.dragGesture.connect(
|
||||
'drag-update', this._onPlayerDragUpdate.bind(this)
|
||||
);
|
||||
|
||||
/* Widget signals that are disconnected after first run */
|
||||
this._playerRealizeSignal = this.player.widget.connect(
|
||||
'realize', this._onPlayerRealize.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
_onWindowFullscreenChanged(window, isFullscreen)
|
||||
{
|
||||
if(isFullscreen) {
|
||||
this.setUpdateTimeInterval();
|
||||
this.setHideControlsTimeout();
|
||||
}
|
||||
else {
|
||||
this.clearTimeout('updateTime');
|
||||
}
|
||||
|
||||
this.interface.setFullscreenMode(isFullscreen);
|
||||
}
|
||||
|
||||
_onPlayerKeyPressed(self, keyval, keycode, state)
|
||||
{
|
||||
let bool = false;
|
||||
|
||||
switch(keyval) {
|
||||
case Gdk.KEY_space:
|
||||
case Gdk.KEY_Return:
|
||||
this.player.toggle_play();
|
||||
break;
|
||||
case Gdk.KEY_Right:
|
||||
bool = true;
|
||||
case Gdk.KEY_Left:
|
||||
// disabled due to missing "seek on drop" support
|
||||
//this.interface.controls.handleScaleIncrement('position', bool);
|
||||
break;
|
||||
case Gdk.KEY_Up:
|
||||
bool = true;
|
||||
case Gdk.KEY_Down:
|
||||
this.interface.controls.handleScaleIncrement('volume', bool);
|
||||
break;
|
||||
case Gdk.KEY_F11:
|
||||
this.window.toggleFullscreen();
|
||||
break;
|
||||
case Gdk.KEY_Escape:
|
||||
if(this.window.isFullscreen)
|
||||
this.window.unfullscreen();
|
||||
break;
|
||||
case Gdk.KEY_q:
|
||||
case Gdk.KEY_Q:
|
||||
this._onWindowCloseRequest();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onPlayerRealize()
|
||||
{
|
||||
this.player.widget.disconnect(this._playerRealizeSignal);
|
||||
this.player.renderer.expose();
|
||||
|
||||
this.defaultCursor = Gdk.Cursor.new_from_name('default', null);
|
||||
this.blankCursor = Gdk.Cursor.new_from_name('none', null);
|
||||
|
||||
this.setHideCursorTimeout();
|
||||
super.run(arr || []);
|
||||
}
|
||||
|
||||
_onWindowShow(window)
|
||||
{
|
||||
this.window.disconnect(this.windowShowSignal);
|
||||
window.disconnect(this.windowShowSignal);
|
||||
this.windowShowSignal = null;
|
||||
|
||||
if(this.playlist.length)
|
||||
this.player.set_playlist(this.playlist);
|
||||
}
|
||||
|
||||
_onPlayerStateChanged(self, state)
|
||||
{
|
||||
if(state === GstPlayer.PlayerState.BUFFERING)
|
||||
return;
|
||||
|
||||
let isInhibited = false;
|
||||
let flags = Gtk.ApplicationInhibitFlags.SUSPEND
|
||||
| Gtk.ApplicationInhibitFlags.IDLE;
|
||||
|
||||
if(state === GstPlayer.PlayerState.PLAYING) {
|
||||
if(this.inhibitCookie)
|
||||
return;
|
||||
|
||||
this.inhibitCookie = this.inhibit(
|
||||
this.window,
|
||||
flags,
|
||||
'video is playing'
|
||||
);
|
||||
if(!this.inhibitCookie)
|
||||
debug(new Error('could not inhibit session!'));
|
||||
|
||||
isInhibited = (this.inhibitCookie > 0);
|
||||
}
|
||||
else {
|
||||
//if(!this.inhibitCookie)
|
||||
return;
|
||||
|
||||
/* Uninhibit seems to be broken as of GTK 3.99.2
|
||||
this.uninhibit(this.inhibitCookie);
|
||||
this.inhibitCookie = null;
|
||||
*/
|
||||
}
|
||||
|
||||
debug(`set prevent suspend to: ${isInhibited}`);
|
||||
}
|
||||
|
||||
_onPlayerPressed(gesture, nPress, x, y)
|
||||
{
|
||||
let button = gesture.get_current_button();
|
||||
let isDouble = (nPress % 2 == 0);
|
||||
this.dragAllowed = !isDouble;
|
||||
|
||||
switch(button) {
|
||||
case Gdk.BUTTON_PRIMARY:
|
||||
if(isDouble)
|
||||
this.window.toggleFullscreen();
|
||||
break;
|
||||
case Gdk.BUTTON_SECONDARY:
|
||||
this.player.toggle_play();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onPlayerEnter(controller, x, y)
|
||||
{
|
||||
this.setHideCursorTimeout();
|
||||
if(this.window.isFullscreen)
|
||||
this.setHideControlsTimeout();
|
||||
}
|
||||
|
||||
_onPlayerLeave(controller)
|
||||
{
|
||||
this.clearTimeout('hideCursor');
|
||||
this.clearTimeout('hideControls');
|
||||
}
|
||||
|
||||
_onPlayerMotion(controller, posX, posY)
|
||||
{
|
||||
/* GTK4 sometimes generates motions with same coords */
|
||||
if(this.posX === posX && this.posY === posY)
|
||||
return;
|
||||
|
||||
/* Do not show cursor on small movements */
|
||||
if(
|
||||
Math.abs(this.posX - posX) >= 0.5
|
||||
|| Math.abs(this.posY - posY) >= 0.5
|
||||
) {
|
||||
this.player.widget.set_cursor(this.defaultCursor);
|
||||
this.setHideCursorTimeout();
|
||||
|
||||
if(this.window.isFullscreen) {
|
||||
if(!this.interface.revealerTop.get_reveal_child()) {
|
||||
this.setUpdateTimeInterval();
|
||||
this.interface.revealControls(true);
|
||||
}
|
||||
this.setHideControlsTimeout();
|
||||
}
|
||||
else if(this.hideControlsTimeout) {
|
||||
this.clearTimeout('hideControls');
|
||||
}
|
||||
}
|
||||
|
||||
this.posX = posX;
|
||||
this.posY = posY;
|
||||
}
|
||||
|
||||
_onPlayerDragUpdate(gesture, offsetX, offsetY)
|
||||
{
|
||||
if(!this.dragAllowed || this.active_window.isFullscreen)
|
||||
return;
|
||||
|
||||
let { gtk_double_click_distance } = this.player.widget.get_settings();
|
||||
|
||||
if (
|
||||
Math.abs(offsetX) > gtk_double_click_distance
|
||||
|| Math.abs(offsetY) > gtk_double_click_distance
|
||||
) {
|
||||
let [isActive, startX, startY] = gesture.get_start_point();
|
||||
if(!isActive) return;
|
||||
|
||||
this.active_window.get_surface().begin_move(
|
||||
gesture.get_device(),
|
||||
gesture.get_current_button(),
|
||||
startX,
|
||||
startY,
|
||||
gesture.get_current_event_time()
|
||||
);
|
||||
|
||||
gesture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
_onWindowCloseRequest()
|
||||
{
|
||||
this.interface.emit('destroy');
|
||||
this.quit();
|
||||
if(this.playlist.length) {
|
||||
let { player } = window.get_child();
|
||||
player.set_playlist(this.playlist);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -103,17 +103,13 @@ class ClapperPopoverButton extends IconButton
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
});
|
||||
|
||||
this.popover.set_parent(this);
|
||||
this.popover.set_child(this.popoverBox);
|
||||
this.popover.set_offset(0, -this.margin_top);
|
||||
|
||||
if(this.isFullscreen)
|
||||
this.popover.add_css_class('osd');
|
||||
|
||||
this.changeStateSignal = this.popover.connect('closed', () =>
|
||||
this.unset_state_flags(Gtk.StateFlags.CHECKED)
|
||||
);
|
||||
this.destroySignal = this.connect('destroy', this._onDestroy.bind(this));
|
||||
this.popover.connect('closed', this._onClosed.bind(this));
|
||||
}
|
||||
|
||||
setFullscreenMode(isFullscreen)
|
||||
@@ -136,16 +132,19 @@ class ClapperPopoverButton extends IconButton
|
||||
vfunc_clicked()
|
||||
{
|
||||
this.set_state_flags(Gtk.StateFlags.CHECKED, false);
|
||||
|
||||
this.popover.set_parent(this);
|
||||
this.popover.popup();
|
||||
}
|
||||
|
||||
_onDestroy()
|
||||
_onClosed()
|
||||
{
|
||||
this.disconnect(this.destroySignal);
|
||||
let root = this.get_root();
|
||||
let clapperWidget = root.get_child();
|
||||
|
||||
clapperWidget.player.widget.grab_focus();
|
||||
|
||||
this.popover.disconnect(this.changeStateSignal);
|
||||
this.popover.unparent();
|
||||
this.popoverBox.emit('destroy');
|
||||
this.popover.emit('destroy');
|
||||
this.unset_state_flags(Gtk.StateFlags.CHECKED);
|
||||
}
|
||||
});
|
||||
|
243
clapper_src/controls.js
vendored
243
clapper_src/controls.js
vendored
@@ -1,25 +1,15 @@
|
||||
const { GObject, Gdk, Gtk } = imports.gi;
|
||||
const { GObject, Gtk } = imports.gi;
|
||||
const Buttons = imports.clapper_src.buttons;
|
||||
const Debug = imports.clapper_src.debug;
|
||||
const Misc = imports.clapper_src.misc;
|
||||
|
||||
const CONTROLS_MARGIN = 4;
|
||||
const CONTROLS_SPACING = 4;
|
||||
|
||||
let { debug } = Debug;
|
||||
|
||||
var Controls = GObject.registerClass({
|
||||
Signals: {
|
||||
'position-seeking-changed': {
|
||||
param_types: [GObject.TYPE_BOOLEAN]
|
||||
},
|
||||
'track-change-requested': {
|
||||
param_types: [GObject.TYPE_STRING, GObject.TYPE_INT]
|
||||
},
|
||||
'visualization-change-requested': {
|
||||
param_types: [GObject.TYPE_STRING]
|
||||
},
|
||||
}
|
||||
}, class ClapperControls extends Gtk.Box
|
||||
var Controls = GObject.registerClass(
|
||||
class ClapperControls extends Gtk.Box
|
||||
{
|
||||
_init()
|
||||
{
|
||||
@@ -29,8 +19,14 @@ var Controls = GObject.registerClass({
|
||||
margin_end: CONTROLS_MARGIN,
|
||||
spacing: CONTROLS_SPACING,
|
||||
valign: Gtk.Align.END,
|
||||
can_focus: false,
|
||||
});
|
||||
|
||||
this.currentVolume = 0;
|
||||
this.currentPosition = 0;
|
||||
this.currentDuration = 0;
|
||||
this.isPositionSeeking = false;
|
||||
|
||||
this.durationFormated = '00:00:00';
|
||||
this.elapsedInitial = '00:00:00/00:00:00';
|
||||
this.buttonsArr = [];
|
||||
@@ -57,10 +53,11 @@ var Controls = GObject.registerClass({
|
||||
this.unfullscreenButton = this.addButton(
|
||||
'view-restore-symbolic',
|
||||
);
|
||||
this.unfullscreenButton.connect('clicked', this._onUnfullscreenClicked.bind(this));
|
||||
this.unfullscreenButton.set_visible(false);
|
||||
|
||||
this.add_css_class('playercontrols');
|
||||
this.destroySignal = this.connect('destroy', this._onDestroy.bind(this));
|
||||
this.realizeSignal = this.connect('realize', this._onRealize.bind(this));
|
||||
}
|
||||
|
||||
setFullscreenMode(isFullscreen)
|
||||
@@ -69,6 +66,7 @@ var Controls = GObject.registerClass({
|
||||
button.setFullscreenMode(isFullscreen);
|
||||
|
||||
this.unfullscreenButton.set_visible(isFullscreen);
|
||||
this.set_can_focus(isFullscreen);
|
||||
}
|
||||
|
||||
setLiveMode(isLive, isSeekable)
|
||||
@@ -79,6 +77,16 @@ var Controls = GObject.registerClass({
|
||||
this.positionScale.visible = isSeekable;
|
||||
}
|
||||
|
||||
updateElapsedLabel(value)
|
||||
{
|
||||
value = value || 0;
|
||||
|
||||
let elapsed = Misc.getFormatedTime(value)
|
||||
+ '/' + this.durationFormated;
|
||||
|
||||
this.elapsedButton.set_label(elapsed);
|
||||
}
|
||||
|
||||
addButton(buttonIcon)
|
||||
{
|
||||
let button = (buttonIcon instanceof Gtk.Button)
|
||||
@@ -135,7 +143,7 @@ var Controls = GObject.registerClass({
|
||||
});
|
||||
checkButton.connect(
|
||||
'toggled',
|
||||
this._onCheckButtonToggled.bind(this, checkButton)
|
||||
this._onCheckButtonToggled.bind(this)
|
||||
);
|
||||
box.append(checkButton);
|
||||
}
|
||||
@@ -160,23 +168,69 @@ var Controls = GObject.registerClass({
|
||||
}
|
||||
}
|
||||
|
||||
handleScaleIncrement(type, isUp)
|
||||
_handleTrackChange(checkButton)
|
||||
{
|
||||
if(type === 'volume' && !this.volumeButton.visible)
|
||||
let clapperWidget = this.get_ancestor(Gtk.Grid);
|
||||
|
||||
/* Reenabling audio is slow (as expected),
|
||||
* so it is better to toggle mute instead */
|
||||
if(checkButton.type === 'audio') {
|
||||
if(checkButton.activeId < 0)
|
||||
return clapperWidget.player.set_mute(true);
|
||||
|
||||
if(clapperWidget.player.get_mute())
|
||||
clapperWidget.player.set_mute(false);
|
||||
|
||||
return clapperWidget.player[
|
||||
`set_${checkButton.type}_track`
|
||||
](checkButton.activeId);
|
||||
}
|
||||
|
||||
if(checkButton.activeId < 0) {
|
||||
/* Disabling video leaves last frame frozen,
|
||||
* so we hide it by making it transparent */
|
||||
if(checkButton.type === 'video')
|
||||
clapperWidget.player.widget.set_opacity(0);
|
||||
|
||||
return clapperWidget.player[
|
||||
`set_${checkButton.type}_track_enabled`
|
||||
](false);
|
||||
}
|
||||
|
||||
let setTrack = `set_${checkButton.type}_track`;
|
||||
|
||||
clapperWidget.player[setTrack](checkButton.activeId);
|
||||
clapperWidget.player[`${setTrack}_enabled`](true);
|
||||
|
||||
if(checkButton.type === 'video' && !clapperWidget.player.widget.opacity)
|
||||
clapperWidget.player.widget.set_opacity(1);
|
||||
}
|
||||
|
||||
_handleVisualizationChange(checkButton)
|
||||
{
|
||||
let clapperWidget = this.get_ancestor(Gtk.Grid);
|
||||
let isEnabled = clapperWidget.player.get_visualization_enabled();
|
||||
|
||||
if(!checkButton.activeId) {
|
||||
if(isEnabled) {
|
||||
clapperWidget.player.set_visualization_enabled(false);
|
||||
debug('disabled visualizations');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let currVis = clapperWidget.player.get_current_visualization();
|
||||
|
||||
if(currVis === checkButton.activeId)
|
||||
return;
|
||||
|
||||
let value = this[`${type}Scale`].get_value();
|
||||
let maxValue = this[`${type}Adjustment`].get_upper();
|
||||
let increment = this[`${type}Adjustment`].get_page_increment();
|
||||
debug(`set visualization: ${checkButton.activeId}`);
|
||||
clapperWidget.player.set_visualization(checkButton.activeId);
|
||||
|
||||
value += (isUp) ? increment : -increment;
|
||||
value = (value < 0)
|
||||
? 0
|
||||
: (value > maxValue)
|
||||
? maxValue
|
||||
: value;
|
||||
|
||||
this[`${type}Scale`].set_value(value);
|
||||
if(!isEnabled) {
|
||||
clapperWidget.player.set_visualization_enabled(true);
|
||||
debug('enabled visualizations');
|
||||
}
|
||||
}
|
||||
|
||||
_addTogglePlayButton()
|
||||
@@ -186,6 +240,9 @@ var Controls = GObject.registerClass({
|
||||
'media-playback-pause-symbolic'
|
||||
);
|
||||
this.togglePlayButton.add_css_class('playbackicon');
|
||||
this.togglePlayButton.connect(
|
||||
'clicked', this._onTogglePlayClicked.bind(this)
|
||||
);
|
||||
this.addButton(this.togglePlayButton);
|
||||
}
|
||||
|
||||
@@ -201,9 +258,10 @@ var Controls = GObject.registerClass({
|
||||
can_focus: false,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
this.positionScale.add_css_class('positionscale');
|
||||
this.positionScale.connect('value-changed', this._onPositionScaleValueChanged.bind(this));
|
||||
this.positionScale.connect(
|
||||
'value-changed', this._onPositionScaleValueChanged.bind(this)
|
||||
);
|
||||
|
||||
/* GTK4 is missing pressed/released signals for GtkRange/GtkScale.
|
||||
* We cannot add controllers, cause it already has them, so we
|
||||
@@ -229,14 +287,6 @@ var Controls = GObject.registerClass({
|
||||
this.volumeButton = this.addPopoverButton(
|
||||
'audio-volume-muted-symbolic'
|
||||
);
|
||||
let scrollController = new Gtk.EventControllerScroll();
|
||||
scrollController.set_flags(
|
||||
Gtk.EventControllerScrollFlags.VERTICAL
|
||||
| Gtk.EventControllerScrollFlags.DISCRETE
|
||||
);
|
||||
scrollController.connect('scroll', this._onScroll.bind(this));
|
||||
this.volumeButton.add_controller(scrollController);
|
||||
|
||||
this.volumeScale = new Gtk.Scale({
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
inverted: true,
|
||||
@@ -245,6 +295,9 @@ var Controls = GObject.registerClass({
|
||||
round_digits: 2,
|
||||
vexpand: true,
|
||||
});
|
||||
this.volumeScale.connect(
|
||||
'value-changed', this._onVolumeScaleValueChanged.bind(this)
|
||||
);
|
||||
this.volumeScale.add_css_class('volumescale');
|
||||
this.volumeAdjustment = this.volumeScale.get_adjustment();
|
||||
|
||||
@@ -264,18 +317,28 @@ var Controls = GObject.registerClass({
|
||||
this.volumeButton.popoverBox.append(this.volumeScale);
|
||||
}
|
||||
|
||||
_getFormatedTime(time)
|
||||
_onRealize()
|
||||
{
|
||||
let hours = ('0' + Math.floor(time / 3600)).slice(-2);
|
||||
time -= hours * 3600;
|
||||
let minutes = ('0' + Math.floor(time / 60)).slice(-2);
|
||||
time -= minutes * 60;
|
||||
let seconds = ('0' + Math.floor(time)).slice(-2);
|
||||
this.disconnect(this.realizeSignal);
|
||||
this.realizeSignal = null;
|
||||
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
let { player } = this.get_ancestor(Gtk.Grid);
|
||||
let scrollController = new Gtk.EventControllerScroll();
|
||||
scrollController.set_flags(
|
||||
Gtk.EventControllerScrollFlags.VERTICAL
|
||||
| Gtk.EventControllerScrollFlags.DISCRETE
|
||||
);
|
||||
scrollController.connect('scroll', player._onScroll.bind(player));
|
||||
this.volumeButton.add_controller(scrollController);
|
||||
}
|
||||
|
||||
_onCheckButtonToggled(self, checkButton)
|
||||
_onUnfullscreenClicked(button)
|
||||
{
|
||||
let root = button.get_root();
|
||||
root.unfullscreen();
|
||||
}
|
||||
|
||||
_onCheckButtonToggled(checkButton)
|
||||
{
|
||||
if(!checkButton.get_active())
|
||||
return;
|
||||
@@ -284,61 +347,73 @@ var Controls = GObject.registerClass({
|
||||
case 'video':
|
||||
case 'audio':
|
||||
case 'subtitle':
|
||||
this.emit(
|
||||
'track-change-requested',
|
||||
checkButton.type,
|
||||
checkButton.activeId
|
||||
);
|
||||
this._handleTrackChange(checkButton);
|
||||
break;
|
||||
case 'visualization':
|
||||
this.emit(
|
||||
`${checkButton.type}-change-requested`,
|
||||
checkButton.activeId
|
||||
);
|
||||
this._handleVisualizationChange(checkButton);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onPositionScaleValueChanged()
|
||||
_onTogglePlayClicked()
|
||||
{
|
||||
let elapsed = this._getFormatedTime(this.positionScale.get_value())
|
||||
+ '/' + this.durationFormated;
|
||||
/* Parent of controls changes, so get ancestor instead */
|
||||
let { player } = this.get_ancestor(Gtk.Grid);
|
||||
player.toggle_play();
|
||||
}
|
||||
|
||||
this.elapsedButton.set_label(elapsed);
|
||||
_onPositionScaleValueChanged(scale)
|
||||
{
|
||||
let value = Math.round(scale.get_value());
|
||||
this.updateElapsedLabel(value);
|
||||
|
||||
if(this.currentPosition === value || this.isPositionSeeking)
|
||||
return;
|
||||
|
||||
let { player } = this.get_ancestor(Gtk.Grid);
|
||||
player.seek_seconds(value);
|
||||
}
|
||||
|
||||
_onVolumeScaleValueChanged(scale)
|
||||
{
|
||||
let volume = Number(scale.get_value().toFixed(2));
|
||||
let icon = (volume <= 0)
|
||||
? 'muted'
|
||||
: (volume <= 0.33)
|
||||
? 'low'
|
||||
: (volume <= 0.66)
|
||||
? 'medium'
|
||||
: (volume <= 1)
|
||||
? 'high'
|
||||
: 'overamplified';
|
||||
|
||||
let iconName = `audio-volume-${icon}-symbolic`;
|
||||
if(this.volumeButton.icon_name !== iconName)
|
||||
{
|
||||
debug(`set volume icon: ${icon}`);
|
||||
this.volumeButton.set_icon_name(iconName);
|
||||
}
|
||||
|
||||
if(this.currentVolume === volume)
|
||||
return;
|
||||
|
||||
let { player } = this.get_ancestor(Gtk.Grid);
|
||||
player.set_volume(volume);
|
||||
}
|
||||
|
||||
_onPositionScaleDragging(scale)
|
||||
{
|
||||
let isPositionSeeking = scale.has_css_class('dragging');
|
||||
|
||||
if(this.isPositionSeeking === isPositionSeeking)
|
||||
if((this.isPositionSeeking = isPositionSeeking))
|
||||
return;
|
||||
|
||||
this.isPositionSeeking = isPositionSeeking;
|
||||
this.emit('position-seeking-changed', this.isPositionSeeking);
|
||||
}
|
||||
let clapperWidget = this.get_ancestor(Gtk.Grid);
|
||||
if(!clapperWidget) return;
|
||||
|
||||
_onScroll(controller, dx, dy)
|
||||
{
|
||||
let isVertical = Math.abs(dy) >= Math.abs(dx);
|
||||
let isIncrease = (isVertical) ? dy < 0 : dx < 0;
|
||||
let type = (isVertical) ? 'volume' : 'position';
|
||||
|
||||
this.handleScaleIncrement(type, isIncrease);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_onDestroy()
|
||||
{
|
||||
this.disconnect(this.destroySignal);
|
||||
|
||||
this.visualizationsButton.emit('destroy');
|
||||
this.videoTracksButton.emit('destroy');
|
||||
this.audioTracksButton.emit('destroy');
|
||||
this.subtitleTracksButton.emit('destroy');
|
||||
this.volumeButton.emit('destroy');
|
||||
let positionSeconds = Math.round(scale.get_value());
|
||||
clapperWidget.player.seek_seconds(positionSeconds);
|
||||
}
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
const { GLib, GObject, Gtk, Pango } = imports.gi;
|
||||
const { GLib, GObject, Gst, Gtk, Pango } = imports.gi;
|
||||
|
||||
var HeaderBar = GObject.registerClass(
|
||||
class ClapperHeaderBar extends Gtk.HeaderBar
|
||||
@@ -14,7 +14,8 @@ class ClapperHeaderBar extends Gtk.HeaderBar
|
||||
let openMenuButton = new Gtk.MenuButton({
|
||||
icon_name: 'open-menu-symbolic'
|
||||
});
|
||||
openMenuButton.set_menu_model(models.settingsMenu);
|
||||
let settingsPopover = new HeaderBarPopover(models.settingsMenu);
|
||||
openMenuButton.set_popover(settingsPopover);
|
||||
this.pack_end(openMenuButton);
|
||||
|
||||
let fullscreenButton = new Gtk.Button({
|
||||
@@ -27,9 +28,9 @@ class ClapperHeaderBar extends Gtk.HeaderBar
|
||||
updateHeaderBar(mediaInfo)
|
||||
{
|
||||
let title = mediaInfo.get_title();
|
||||
let subtitle = mediaInfo.get_uri() || null;
|
||||
let subtitle = mediaInfo.get_uri();
|
||||
|
||||
if(subtitle && subtitle.startsWith('file://')) {
|
||||
if(Gst.Uri.get_protocol(subtitle) === 'file') {
|
||||
subtitle = GLib.path_get_basename(
|
||||
GLib.filename_from_uri(subtitle)[0]
|
||||
);
|
||||
@@ -84,3 +85,24 @@ class ClapperHeaderBar extends Gtk.HeaderBar
|
||||
return box;
|
||||
}
|
||||
});
|
||||
|
||||
var HeaderBarPopover = GObject.registerClass(
|
||||
class ClapperHeaderBarPopover extends Gtk.PopoverMenu
|
||||
{
|
||||
_init(model)
|
||||
{
|
||||
super._init({
|
||||
menu_model: model,
|
||||
});
|
||||
|
||||
this.connect('closed', this._onClosed.bind(this));
|
||||
}
|
||||
|
||||
_onClosed()
|
||||
{
|
||||
let root = this.get_root();
|
||||
let clapperWidget = root.get_child();
|
||||
|
||||
clapperWidget.player.widget.grab_focus();
|
||||
}
|
||||
});
|
||||
|
51
clapper_src/misc.js
Normal file
51
clapper_src/misc.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const { GstPlayer, Gtk } = imports.gi;
|
||||
const Debug = imports.clapper_src.debug;
|
||||
|
||||
let { debug } = Debug;
|
||||
let inhibitCookie;
|
||||
|
||||
function inhibitForState(state, window)
|
||||
{
|
||||
let isInhibited = false;
|
||||
let flags = Gtk.ApplicationInhibitFlags.SUSPEND
|
||||
| Gtk.ApplicationInhibitFlags.IDLE;
|
||||
|
||||
if(state === GstPlayer.PlayerState.PLAYING) {
|
||||
if(inhibitCookie)
|
||||
return;
|
||||
|
||||
let app = window.get_application();
|
||||
|
||||
inhibitCookie = app.inhibit(
|
||||
window,
|
||||
flags,
|
||||
'video is playing'
|
||||
);
|
||||
if(!inhibitCookie)
|
||||
debug(new Error('could not inhibit session!'));
|
||||
|
||||
isInhibited = (inhibitCookie > 0);
|
||||
}
|
||||
else {
|
||||
//if(!inhibitCookie)
|
||||
return;
|
||||
|
||||
/* Uninhibit seems to be broken as of GTK 3.99.2
|
||||
this.uninhibit(inhibitCookie);
|
||||
inhibitCookie = null;
|
||||
*/
|
||||
}
|
||||
|
||||
debug(`set prevent suspend to: ${isInhibited}`);
|
||||
}
|
||||
|
||||
function getFormatedTime(time)
|
||||
{
|
||||
let hours = ('0' + Math.floor(time / 3600)).slice(-2);
|
||||
time -= hours * 3600;
|
||||
let minutes = ('0' + Math.floor(time / 60)).slice(-2);
|
||||
time -= minutes * 60;
|
||||
let seconds = ('0' + Math.floor(time)).slice(-2);
|
||||
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
const { Gio, GObject, Gst, GstPlayer, Gtk } = imports.gi;
|
||||
const { Gdk, Gio, GLib, GObject, Gst, GstPlayer, Gtk } = imports.gi;
|
||||
const ByteArray = imports.byteArray;
|
||||
const Debug = imports.clapper_src.debug;
|
||||
|
||||
@@ -44,15 +44,13 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
video_renderer: renderer
|
||||
});
|
||||
|
||||
/* Assign elements to player for later access */
|
||||
this.gtkglsink = gtkglsink;
|
||||
this.glsinkbin = glsinkbin;
|
||||
this.dispatcher = dispatcher;
|
||||
this.renderer = renderer;
|
||||
|
||||
this.gstRegistry = Gst.Registry.get();
|
||||
this.is_local_file = false;
|
||||
this.seek_done = false;
|
||||
this.seek_done = true;
|
||||
this.dragAllowed = false;
|
||||
|
||||
this.posX = 0;
|
||||
this.posY = 0;
|
||||
this.keyPressCount = 0;
|
||||
|
||||
this._playerSignals = [];
|
||||
this._widgetSignals = [];
|
||||
@@ -73,37 +71,54 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
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;
|
||||
this.playlist_ext = opts.playlist_ext || 'claps';
|
||||
|
||||
this.keyController = new Gtk.EventControllerKey();
|
||||
this.motionController = new Gtk.EventControllerMotion();
|
||||
this.scrollController = new Gtk.EventControllerScroll();
|
||||
this.clickGesture = new Gtk.GestureClick();
|
||||
this.dragGesture = new Gtk.GestureDrag();
|
||||
this._hideCursorTimeout = null;
|
||||
this._hideControlsTimeout = null;
|
||||
this._updateTimeTimeout = null;
|
||||
|
||||
this.scrollController.set_flags(
|
||||
Gtk.EventControllerScrollFlags.BOTH_AXES
|
||||
);
|
||||
this.clickGesture.set_button(0);
|
||||
this.cursorInPlayer = false;
|
||||
|
||||
this.widget.add_controller(this.keyController);
|
||||
this.widget.add_controller(this.motionController);
|
||||
this.widget.add_controller(this.scrollController);
|
||||
this.widget.add_controller(this.clickGesture);
|
||||
this.widget.add_controller(this.dragGesture);
|
||||
let clickGesture = new Gtk.GestureClick();
|
||||
clickGesture.set_button(0);
|
||||
clickGesture.connect('pressed', this._onWidgetPressed.bind(this));
|
||||
this.widget.add_controller(clickGesture);
|
||||
|
||||
this.connect('state-changed', this._onStateChanged.bind(this));
|
||||
this.connect('uri-loaded', this._onUriLoaded.bind(this));
|
||||
this.connect('end-of-stream', this._onStreamEnded.bind(this));
|
||||
this.connect('warning', this._onPlayerWarning.bind(this));
|
||||
this.connect('error', this._onPlayerError.bind(this));
|
||||
this.connectWidget('destroy', this._onWidgetDestroy.bind(this));
|
||||
let dragGesture = new Gtk.GestureDrag();
|
||||
dragGesture.connect('drag-update', this._onWidgetDragUpdate.bind(this));
|
||||
this.widget.add_controller(dragGesture);
|
||||
|
||||
let keyController = new Gtk.EventControllerKey();
|
||||
keyController.connect('key-pressed', this._onWidgetKeyPressed.bind(this));
|
||||
keyController.connect('key-released', this._onWidgetKeyReleased.bind(this));
|
||||
this.widget.add_controller(keyController);
|
||||
|
||||
let scrollController = new Gtk.EventControllerScroll();
|
||||
scrollController.set_flags(Gtk.EventControllerScrollFlags.BOTH_AXES);
|
||||
scrollController.connect('scroll', this._onScroll.bind(this));
|
||||
this.widget.add_controller(scrollController);
|
||||
|
||||
let motionController = new Gtk.EventControllerMotion();
|
||||
motionController.connect('enter', this._onWidgetEnter.bind(this));
|
||||
motionController.connect('leave', this._onWidgetLeave.bind(this));
|
||||
motionController.connect('motion', this._onWidgetMotion.bind(this));
|
||||
this.widget.add_controller(motionController);
|
||||
|
||||
this.selfConnect('state-changed', this._onStateChanged.bind(this));
|
||||
this.selfConnect('uri-loaded', this._onUriLoaded.bind(this));
|
||||
this.selfConnect('end-of-stream', this._onStreamEnded.bind(this));
|
||||
this.selfConnect('warning', this._onPlayerWarning.bind(this));
|
||||
this.selfConnect('error', this._onPlayerError.bind(this));
|
||||
|
||||
this._realizeSignal = this.widget.connect('realize', this._onWidgetRealize.bind(this));
|
||||
}
|
||||
|
||||
set_media(source)
|
||||
@@ -133,7 +148,7 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
return this.set_media(this._playlist[this._trackId]);
|
||||
}
|
||||
|
||||
if(file.get_path().endsWith(`.${this.playlist_ext}`))
|
||||
if(file.get_path().endsWith('.claps'))
|
||||
return this.load_playlist_file(file);
|
||||
|
||||
this.is_local_file = true;
|
||||
@@ -200,6 +215,12 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
|
||||
seek(position)
|
||||
{
|
||||
if(this.state === GstPlayer.PlayerState.STOPPED)
|
||||
this.pause();
|
||||
|
||||
if(position < 0)
|
||||
position = 0;
|
||||
|
||||
this.seek_done = false;
|
||||
debug(`player is seeking to position: ${position}`);
|
||||
|
||||
@@ -219,6 +240,34 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
this.seek(position * 1000000000);
|
||||
}
|
||||
|
||||
set_volume(volume)
|
||||
{
|
||||
if(volume < 0)
|
||||
volume = 0;
|
||||
else if(volume > 2)
|
||||
volume = 2;
|
||||
|
||||
super.set_volume(volume);
|
||||
}
|
||||
|
||||
adjust_position(isIncrease)
|
||||
{
|
||||
let { controls } = this.widget.get_ancestor(Gtk.Grid);
|
||||
|
||||
let value = (isIncrease) ? 10 : -10;
|
||||
let positionSeconds = controls.positionScale.get_value() + value;
|
||||
controls.positionScale.set_value(positionSeconds);
|
||||
}
|
||||
|
||||
adjust_volume(isIncrease)
|
||||
{
|
||||
let { controls } = this.widget.get_ancestor(Gtk.Grid);
|
||||
|
||||
let value = (isIncrease) ? 0.05 : -0.05;
|
||||
let volume = controls.volumeScale.get_value() + value;
|
||||
controls.volumeScale.set_value(volume);
|
||||
}
|
||||
|
||||
toggle_play()
|
||||
{
|
||||
let action = (this.state === GstPlayer.PlayerState.PLAYING)
|
||||
@@ -238,7 +287,8 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
{
|
||||
debug(`changing rank of plugin: ${name}`);
|
||||
|
||||
let feature = this.gstRegistry.lookup_feature(name);
|
||||
let gstRegistry = Gst.Registry.get();
|
||||
let feature = gstRegistry.lookup_feature(name);
|
||||
if(!feature)
|
||||
return debug(`plugin unavailable: ${name}`);
|
||||
|
||||
@@ -248,20 +298,72 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
debug(`changed rank: ${oldRank} -> ${rank} for ${name}`);
|
||||
}
|
||||
|
||||
connect(signal, fn)
|
||||
selfConnect(signal, fn)
|
||||
{
|
||||
let connection = super.connect(signal, fn);
|
||||
this._playerSignals.push(connection);
|
||||
|
||||
return connection;
|
||||
this._playerSignals.push(
|
||||
super.connect(signal, fn)
|
||||
);
|
||||
}
|
||||
|
||||
connectWidget(signal, fn)
|
||||
_setHideCursorTimeout()
|
||||
{
|
||||
let connection = this.widget.connect(signal, fn);
|
||||
this._widgetSignals.push(connection);
|
||||
this._clearTimeout('hideCursor');
|
||||
this._hideCursorTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
||||
this._hideCursorTimeout = null;
|
||||
|
||||
return connection;
|
||||
if(this.cursorInPlayer) {
|
||||
let blankCursor = Gdk.Cursor.new_from_name('none', null);
|
||||
this.widget.set_cursor(blankCursor);
|
||||
}
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
_setHideControlsTimeout()
|
||||
{
|
||||
this._clearTimeout('hideControls');
|
||||
this._hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => {
|
||||
this._hideControlsTimeout = null;
|
||||
|
||||
if(this.cursorInPlayer) {
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
if(clapperWidget.fullscreenMode) {
|
||||
this._clearTimeout('updateTime');
|
||||
clapperWidget.revealControls(false);
|
||||
}
|
||||
}
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
_setUpdateTimeInterval()
|
||||
{
|
||||
this._clearTimeout('updateTime');
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
let nextUpdate = clapperWidget.updateTime();
|
||||
|
||||
this._updateTimeTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, nextUpdate, () => {
|
||||
this._updateTimeTimeout = null;
|
||||
|
||||
if(clapperWidget.fullscreenMode)
|
||||
this._setUpdateTimeInterval();
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
_clearTimeout(name)
|
||||
{
|
||||
if(!this[`_${name}Timeout`])
|
||||
return;
|
||||
|
||||
GLib.source_remove(this[`_${name}Timeout`]);
|
||||
this[`_${name}Timeout`] = null;
|
||||
|
||||
if(name === 'updateTime')
|
||||
debug('cleared update time interval');
|
||||
}
|
||||
|
||||
_onStateChanged(player, state)
|
||||
@@ -272,6 +374,11 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
this.seek_done = true;
|
||||
debug('seeking finished');
|
||||
}
|
||||
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
if(!clapperWidget) return;
|
||||
|
||||
clapperWidget._onPlayerStateChanged(player, state);
|
||||
}
|
||||
|
||||
_onStreamEnded(player)
|
||||
@@ -300,12 +407,227 @@ class ClapperPlayer extends GstPlayer.Player
|
||||
debug(error);
|
||||
}
|
||||
|
||||
_onWidgetDestroy()
|
||||
_onWidgetRealize()
|
||||
{
|
||||
this.widget.disconnect(this._realizeSignal);
|
||||
this._realizeSignal = null;
|
||||
|
||||
let root = this.widget.get_root();
|
||||
if(!root) return;
|
||||
|
||||
root.connect('close-request', this._onCloseRequest.bind(this));
|
||||
}
|
||||
|
||||
_onWidgetKeyPressed(controller, keyval, keycode, state)
|
||||
{
|
||||
this.keyPressCount++;
|
||||
|
||||
let bool = false;
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
|
||||
switch(keyval) {
|
||||
case Gdk.KEY_Up:
|
||||
bool = true;
|
||||
case Gdk.KEY_Down:
|
||||
this.adjust_volume(bool);
|
||||
break;
|
||||
case Gdk.KEY_Right:
|
||||
bool = true;
|
||||
case Gdk.KEY_Left:
|
||||
clapperWidget.controls.isPositionSeeking = true;
|
||||
this._clearTimeout('hideControls');
|
||||
if(this.keyPressCount > 1) {
|
||||
clapperWidget.revealerBottom.set_can_focus(false);
|
||||
clapperWidget.revealerBottom.revealChild(true);
|
||||
}
|
||||
this.adjust_position(bool);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onWidgetKeyReleased(controller, keyval, keycode, state)
|
||||
{
|
||||
this.keyPressCount = 0;
|
||||
|
||||
let value;
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
|
||||
switch(keyval) {
|
||||
case Gdk.KEY_space:
|
||||
this.toggle_play();
|
||||
break;
|
||||
case Gdk.KEY_Return:
|
||||
if(clapperWidget.fullscreenMode) {
|
||||
clapperWidget.revealControls(true);
|
||||
this._setHideControlsTimeout();
|
||||
}
|
||||
break;
|
||||
case Gdk.KEY_Right:
|
||||
case Gdk.KEY_Left:
|
||||
value = clapperWidget.controls.positionScale.get_value();
|
||||
this.seek_seconds(value);
|
||||
this._setHideControlsTimeout();
|
||||
clapperWidget.controls.isPositionSeeking = false;
|
||||
break;
|
||||
case Gdk.KEY_F11:
|
||||
clapperWidget.toggleFullscreen();
|
||||
break;
|
||||
case Gdk.KEY_Escape:
|
||||
if(clapperWidget.fullscreenMode) {
|
||||
let root = this.widget.get_root();
|
||||
root.unfullscreen();
|
||||
}
|
||||
break;
|
||||
case Gdk.KEY_q:
|
||||
case Gdk.KEY_Q:
|
||||
let root = this.widget.get_root();
|
||||
root.emit('close-request');
|
||||
root.destroy();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onWidgetPressed(gesture, nPress, x, y)
|
||||
{
|
||||
let button = gesture.get_current_button();
|
||||
let isDouble = (nPress % 2 == 0);
|
||||
this.dragAllowed = !isDouble;
|
||||
|
||||
switch(button) {
|
||||
case Gdk.BUTTON_PRIMARY:
|
||||
if(isDouble) {
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
clapperWidget.toggleFullscreen();
|
||||
}
|
||||
break;
|
||||
case Gdk.BUTTON_SECONDARY:
|
||||
this.toggle_play();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_onWidgetEnter(controller, x, y)
|
||||
{
|
||||
this.cursorInPlayer = true;
|
||||
|
||||
this._setHideCursorTimeout();
|
||||
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
if(clapperWidget.fullscreenMode)
|
||||
this._setHideControlsTimeout();
|
||||
}
|
||||
|
||||
_onWidgetLeave(controller)
|
||||
{
|
||||
this.cursorInPlayer = false;
|
||||
|
||||
this._clearTimeout('hideCursor');
|
||||
this._clearTimeout('hideControls');
|
||||
}
|
||||
|
||||
_onWidgetMotion(controller, posX, posY)
|
||||
{
|
||||
this.cursorInPlayer = true;
|
||||
|
||||
/* GTK4 sometimes generates motions with same coords */
|
||||
if(this.posX === posX && this.posY === posY)
|
||||
return;
|
||||
|
||||
/* Do not show cursor on small movements */
|
||||
if(
|
||||
Math.abs(this.posX - posX) >= 0.5
|
||||
|| Math.abs(this.posY - posY) >= 0.5
|
||||
) {
|
||||
let defaultCursor = Gdk.Cursor.new_from_name('default', null);
|
||||
this.widget.set_cursor(defaultCursor);
|
||||
this._setHideCursorTimeout();
|
||||
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
|
||||
if(clapperWidget.fullscreenMode) {
|
||||
if(!this._updateTimeTimeout)
|
||||
this._setUpdateTimeInterval();
|
||||
|
||||
if(!clapperWidget.revealerTop.get_reveal_child()) {
|
||||
/* Do not grab controls key focus on mouse movement */
|
||||
clapperWidget.revealerBottom.set_can_focus(false);
|
||||
clapperWidget.revealControls(true);
|
||||
}
|
||||
this._setHideControlsTimeout();
|
||||
}
|
||||
else {
|
||||
if(this._hideControlsTimeout)
|
||||
this._clearTimeout('hideControls');
|
||||
if(this._updateTimeTimeout)
|
||||
this._clearTimeout('updateTime');
|
||||
}
|
||||
}
|
||||
|
||||
this.posX = posX;
|
||||
this.posY = posY;
|
||||
}
|
||||
|
||||
_onWidgetDragUpdate(gesture, offsetX, offsetY)
|
||||
{
|
||||
if(!this.dragAllowed)
|
||||
return;
|
||||
|
||||
let clapperWidget = this.widget.get_ancestor(Gtk.Grid);
|
||||
if(clapperWidget.fullscreenMode)
|
||||
return;
|
||||
|
||||
let { gtk_double_click_distance } = this.widget.get_settings();
|
||||
|
||||
if (
|
||||
Math.abs(offsetX) > gtk_double_click_distance
|
||||
|| Math.abs(offsetY) > gtk_double_click_distance
|
||||
) {
|
||||
let [isActive, startX, startY] = gesture.get_start_point();
|
||||
if(!isActive) return;
|
||||
|
||||
let root = this.widget.get_root();
|
||||
if(!root) return;
|
||||
|
||||
root.get_surface().begin_move(
|
||||
gesture.get_device(),
|
||||
gesture.get_current_button(),
|
||||
startX,
|
||||
startY,
|
||||
gesture.get_current_event_time()
|
||||
);
|
||||
|
||||
gesture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
_onScroll(controller, dx, dy)
|
||||
{
|
||||
let isHorizontal = Math.abs(dx) >= Math.abs(dy);
|
||||
let isIncrease = (isHorizontal) ? dx < 0 : dy < 0;
|
||||
|
||||
if(isHorizontal)
|
||||
this.adjust_position(isIncrease);
|
||||
else
|
||||
this.adjust_volume(isIncrease);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_onCloseRequest(window)
|
||||
{
|
||||
while(this._widgetSignals.length)
|
||||
this.widget.disconnect(this._widgetSignals.pop());
|
||||
|
||||
while(this._playerSignals.length)
|
||||
this.disconnect(this._playerSignals.pop());
|
||||
|
||||
if(this.state !== GstPlayer.PlayerState.STOPPED)
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
const { Gdk, GLib, GObject, Gtk, Pango } = imports.gi;
|
||||
const { GLib, GObject, Gtk, Pango } = imports.gi;
|
||||
const Debug = imports.clapper_src.debug;
|
||||
|
||||
const REVEAL_TIME = 800;
|
||||
@@ -14,6 +14,7 @@ class ClapperCustomRevealer extends Gtk.Revealer
|
||||
|
||||
let defaults = {
|
||||
visible: false,
|
||||
can_focus: false,
|
||||
};
|
||||
Object.assign(opts, defaults);
|
||||
|
||||
@@ -31,6 +32,9 @@ class ClapperCustomRevealer extends Gtk.Revealer
|
||||
else
|
||||
this._setHideTimeout();
|
||||
|
||||
/* Restore focusability after we are done */
|
||||
if(!isReveal) this.set_can_focus(true);
|
||||
|
||||
this._timedReveal(isReveal, REVEAL_TIME);
|
||||
}
|
||||
|
||||
@@ -94,18 +98,6 @@ class ClapperRevealerTop extends CustomRevealer
|
||||
});
|
||||
|
||||
this.revealerName = 'top';
|
||||
/*
|
||||
this.set_events(
|
||||
Gdk.EventMask.BUTTON_PRESS_MASK
|
||||
| Gdk.EventMask.BUTTON_RELEASE_MASK
|
||||
| Gdk.EventMask.TOUCH_MASK
|
||||
| Gdk.EventMask.SCROLL_MASK
|
||||
| Gdk.EventMask.TOUCHPAD_GESTURE_MASK
|
||||
| Gdk.EventMask.POINTER_MOTION_MASK
|
||||
| Gdk.EventMask.ENTER_NOTIFY_MASK
|
||||
| Gdk.EventMask.LEAVE_NOTIFY_MASK
|
||||
);
|
||||
*/
|
||||
let initTime = GLib.DateTime.new_now_local().format('%X');
|
||||
this.timeFormat = (initTime.length > 8)
|
||||
? '%I:%M %p'
|
||||
@@ -198,7 +190,7 @@ class ClapperRevealerBottom extends CustomRevealer
|
||||
set_visible(isVisible)
|
||||
{
|
||||
let isChange = super.set_visible(isVisible);
|
||||
if(!isChange) return;
|
||||
if(!isChange || !this.can_focus) return;
|
||||
|
||||
let parent = this.get_parent();
|
||||
let playerWidget = parent.get_first_child();
|
||||
|
@@ -1,12 +1,14 @@
|
||||
const { Gdk, GLib, GObject, Gtk, Gst, GstPlayer, Pango } = imports.gi;
|
||||
const { Gdk, GLib, GObject, Gtk, GstPlayer } = imports.gi;
|
||||
const { Controls } = imports.clapper_src.controls;
|
||||
const Debug = imports.clapper_src.debug;
|
||||
const Misc = imports.clapper_src.misc;
|
||||
const { Player } = imports.clapper_src.player;
|
||||
const Revealers = imports.clapper_src.revealers;
|
||||
|
||||
let { debug } = Debug;
|
||||
|
||||
var Interface = GObject.registerClass(
|
||||
class ClapperInterface extends Gtk.Grid
|
||||
var Widget = GObject.registerClass(
|
||||
class ClapperWidget extends Gtk.Grid
|
||||
{
|
||||
_init(opts)
|
||||
{
|
||||
@@ -15,19 +17,23 @@ class ClapperInterface extends Gtk.Grid
|
||||
super._init();
|
||||
|
||||
let defaults = {
|
||||
seekOnDrop: true
|
||||
cssPath: `${pkg.datadir}/${pkg.name}/css/styles.css`,
|
||||
};
|
||||
Object.assign(this, defaults, opts);
|
||||
|
||||
let cssProvider = new Gtk.CssProvider();
|
||||
cssProvider.load_from_path(this.cssPath);
|
||||
Gtk.StyleContext.add_provider_for_display(
|
||||
Gdk.Display.get_default(),
|
||||
cssProvider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
);
|
||||
|
||||
this.fullscreenMode = false;
|
||||
this.isSeekable = false;
|
||||
|
||||
this.lastVolumeValue = null;
|
||||
this.lastPositionValue = 0;
|
||||
this.lastRevealerEventTime = 0;
|
||||
this.needsTracksUpdate = true;
|
||||
this.headerBar = null;
|
||||
this.defaultTitle = null;
|
||||
this.mediaInfoSignal = null;
|
||||
|
||||
this.videoBox = new Gtk.Box();
|
||||
@@ -41,54 +47,21 @@ class ClapperInterface extends Gtk.Grid
|
||||
this.attach(this.videoBox, 0, 0, 1, 1);
|
||||
this.attach(this.controls, 0, 1, 1, 1);
|
||||
|
||||
this.destroySignal = this.connect('destroy', this._onDestroy.bind(this));
|
||||
}
|
||||
this.mapSignal = this.connect('map', this._onMap.bind(this));
|
||||
|
||||
addPlayer(player)
|
||||
{
|
||||
this._player = player;
|
||||
this._player.widget.vexpand = true;
|
||||
this._player.widget.hexpand = true;
|
||||
this.player = new Player();
|
||||
this.player.widget.width_request = 960;
|
||||
this.player.widget.height_request = 540;
|
||||
|
||||
this._player.connect('state-changed', this._onPlayerStateChanged.bind(this));
|
||||
this._player.connect('volume-changed', this._onPlayerVolumeChanged.bind(this));
|
||||
this._player.connect('duration-changed', this._onPlayerDurationChanged.bind(this));
|
||||
this._player.connect('position-updated', this._onPlayerPositionUpdated.bind(this));
|
||||
this.player.selfConnect('position-updated', this._onPlayerPositionUpdated.bind(this));
|
||||
this.player.selfConnect('duration-changed', this._onPlayerDurationChanged.bind(this));
|
||||
this.player.selfConnect('volume-changed', this._onPlayerVolumeChanged.bind(this));
|
||||
|
||||
this._player.scrollController.connect(
|
||||
'scroll', (ctl, dx, dy) => this.controls._onScroll(ctl, dx, dy)
|
||||
);
|
||||
this.controls.togglePlayButton.connect(
|
||||
'clicked', this._onControlsTogglePlayClicked.bind(this)
|
||||
);
|
||||
this.scaleSig = this.controls.positionScale.connect(
|
||||
'value-changed', this._onControlsPositionChanged.bind(this)
|
||||
);
|
||||
this.controls.volumeScale.connect(
|
||||
'value-changed', this._onControlsVolumeChanged.bind(this)
|
||||
);
|
||||
this.controls.connect(
|
||||
'position-seeking-changed', this._onPositionSeekingChanged.bind(this)
|
||||
);
|
||||
this.controls.connect(
|
||||
'track-change-requested', this._onTrackChangeRequested.bind(this)
|
||||
);
|
||||
this.controls.connect(
|
||||
'visualization-change-requested', this._onVisualizationChangeRequested.bind(this)
|
||||
);
|
||||
//this.revealerTop.connect('event-after', (self, event) => this._player.widget.event(event));
|
||||
|
||||
this.overlay.set_child(this._player.widget);
|
||||
this.overlay.set_child(this.player.widget);
|
||||
this.overlay.add_overlay(this.revealerTop);
|
||||
this.overlay.add_overlay(this.revealerBottom);
|
||||
}
|
||||
|
||||
addHeaderBar(headerBar, defaultTitle)
|
||||
{
|
||||
this.headerBar = headerBar;
|
||||
this.defaultTitle = defaultTitle || null;
|
||||
}
|
||||
|
||||
revealControls(isReveal)
|
||||
{
|
||||
for(let pos of ['Top', 'Bottom'])
|
||||
@@ -101,30 +74,18 @@ class ClapperInterface extends Gtk.Grid
|
||||
this[`revealer${pos}`].showChild(isShow);
|
||||
}
|
||||
|
||||
setFullscreenMode(isFullscreen)
|
||||
toggleFullscreen()
|
||||
{
|
||||
if(this.fullscreenMode === isFullscreen)
|
||||
return;
|
||||
let root = this.get_root();
|
||||
if(!root) return;
|
||||
|
||||
if(isFullscreen) {
|
||||
this.remove(this.controls);
|
||||
this.revealerBottom.append(this.controls);
|
||||
}
|
||||
else {
|
||||
this.revealerBottom.remove(this.controls);
|
||||
this.attach(this.controls, 0, 1, 1, 1);
|
||||
}
|
||||
|
||||
this.controls.setFullscreenMode(isFullscreen);
|
||||
this.showControls(isFullscreen);
|
||||
|
||||
this.fullscreenMode = isFullscreen;
|
||||
debug(`interface in fullscreen mode: ${isFullscreen}`);
|
||||
let un = (this.fullscreenMode) ? 'un' : '';
|
||||
root[`${un}fullscreen`]();
|
||||
}
|
||||
|
||||
_onMediaInfoUpdated(player, mediaInfo)
|
||||
{
|
||||
this._player.disconnect(this.mediaInfoSignal);
|
||||
player.disconnect(this.mediaInfoSignal);
|
||||
|
||||
/* Set titlebar media title and path */
|
||||
this.updateTitles(mediaInfo);
|
||||
@@ -193,7 +154,7 @@ class ClapperInterface extends Gtk.Grid
|
||||
}
|
||||
|
||||
for(let type of ['video', 'audio', 'subtitle']) {
|
||||
let currStream = this._player[`get_current_${type}_track`]();
|
||||
let currStream = player[`get_current_${type}_track`]();
|
||||
let activeId = (currStream) ? currStream.get_index() : -1;
|
||||
|
||||
if(currStream && type !== 'subtitle') {
|
||||
@@ -227,17 +188,27 @@ class ClapperInterface extends Gtk.Grid
|
||||
|
||||
updateTitles(mediaInfo)
|
||||
{
|
||||
if(this.headerBar)
|
||||
this.headerBar.updateHeaderBar(mediaInfo);
|
||||
let root = this.get_root();
|
||||
if(!root) return;
|
||||
|
||||
this.revealerTop.setMediaTitle(this.headerBar.titleLabel.label);
|
||||
let title;
|
||||
let headerbar = root.get_titlebar();
|
||||
|
||||
if(headerbar && headerbar.updateHeaderBar) {
|
||||
headerbar.updateHeaderBar(mediaInfo);
|
||||
title = headerbar.titleLabel.label;
|
||||
}
|
||||
else
|
||||
title = mediaInfo.get_title() || mediaInfo.get_uri();
|
||||
|
||||
this.revealerTop.setMediaTitle(title);
|
||||
}
|
||||
|
||||
updateTime()
|
||||
{
|
||||
let currTime = GLib.DateTime.new_now_local();
|
||||
let endTime = currTime.add_seconds(
|
||||
this.controls.positionAdjustment.get_upper() - this.lastPositionValue
|
||||
this.controls.positionAdjustment.get_upper() - this.controls.currentPosition
|
||||
);
|
||||
let nextUpdate = this.revealerTop.setTimes(currTime, endTime);
|
||||
|
||||
@@ -283,78 +254,20 @@ class ClapperInterface extends Gtk.Grid
|
||||
debug(`show visualizations button: ${isShow}`);
|
||||
}
|
||||
|
||||
_onTrackChangeRequested(self, type, activeId)
|
||||
{
|
||||
/* Reenabling audio is slow (as expected),
|
||||
* so it is better to toggle mute instead */
|
||||
if(type === 'audio') {
|
||||
if(activeId < 0)
|
||||
return this._player.set_mute(true);
|
||||
|
||||
if(this._player.get_mute())
|
||||
this._player.set_mute(false);
|
||||
|
||||
return this._player[`set_${type}_track`](activeId);
|
||||
}
|
||||
|
||||
if(activeId < 0) {
|
||||
/* Disabling video leaves last frame frozen,
|
||||
* so we hide it by making it transparent */
|
||||
if(type === 'video')
|
||||
this._player.widget.set_opacity(0);
|
||||
|
||||
return this._player[`set_${type}_track_enabled`](false);
|
||||
}
|
||||
|
||||
this._player[`set_${type}_track`](activeId);
|
||||
this._player[`set_${type}_track_enabled`](true);
|
||||
|
||||
if(type === 'video' && !this._player.widget.opacity) {
|
||||
this._player.widget.set_opacity(1);
|
||||
this._player.renderer.expose();
|
||||
}
|
||||
}
|
||||
|
||||
_onVisualizationChangeRequested(self, visName)
|
||||
{
|
||||
let isEnabled = this._player.get_visualization_enabled();
|
||||
|
||||
if(!visName) {
|
||||
if(isEnabled) {
|
||||
this._player.set_visualization_enabled(false);
|
||||
debug('disabled visualizations');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let currVis = this._player.get_current_visualization();
|
||||
|
||||
if(currVis === visName)
|
||||
return;
|
||||
|
||||
debug(`set visualization: ${visName}`);
|
||||
this._player.set_visualization(visName);
|
||||
|
||||
if(!isEnabled) {
|
||||
this._player.set_visualization_enabled(true);
|
||||
debug('enabled visualizations');
|
||||
}
|
||||
}
|
||||
|
||||
_onPlayerStateChanged(player, state)
|
||||
{
|
||||
switch(state) {
|
||||
case GstPlayer.PlayerState.BUFFERING:
|
||||
if(!this._player.is_local_file)
|
||||
if(!player.is_local_file)
|
||||
this.needsTracksUpdate = true;
|
||||
break;
|
||||
case GstPlayer.PlayerState.STOPPED:
|
||||
this.lastPositionValue = 0;
|
||||
this.controls.positionAdjustment.set_value(0);
|
||||
this.controls.currentPosition = 0;
|
||||
this.controls.positionScale.set_value(0);
|
||||
this.controls.togglePlayButton.setPrimaryIcon();
|
||||
this.needsTracksUpdate = true;
|
||||
if(this.mediaInfoSignal) {
|
||||
this._player.disconnect(this.mediaInfoSignal);
|
||||
player.disconnect(this.mediaInfoSignal);
|
||||
this.mediaInfoSignal = null;
|
||||
}
|
||||
case GstPlayer.PlayerState.PAUSED:
|
||||
@@ -364,7 +277,7 @@ class ClapperInterface extends Gtk.Grid
|
||||
this.controls.togglePlayButton.setSecondaryIcon();
|
||||
if(this.needsTracksUpdate && !this.mediaInfoSignal) {
|
||||
this.needsTracksUpdate = false;
|
||||
this.mediaInfoSignal = this._player.connect(
|
||||
this.mediaInfoSignal = player.connect(
|
||||
'media_info_updated', this._onMediaInfoUpdated.bind(this)
|
||||
);
|
||||
}
|
||||
@@ -372,127 +285,100 @@ class ClapperInterface extends Gtk.Grid
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(state === GstPlayer.PlayerState.BUFFERING)
|
||||
return;
|
||||
|
||||
let window = this.get_root();
|
||||
Misc.inhibitForState(state, window);
|
||||
}
|
||||
|
||||
_onPlayerDurationChanged(player)
|
||||
{
|
||||
let duration = this._player.get_duration() / 1000000000;
|
||||
let duration = Math.floor(player.get_duration() / 1000000000);
|
||||
|
||||
/* Sometimes GstPlayer might re-emit
|
||||
* duration changed during playback */
|
||||
if(this.controls.currentDuration === duration)
|
||||
return;
|
||||
|
||||
let increment = (duration < 1)
|
||||
? 0
|
||||
: (duration < 100)
|
||||
? 1
|
||||
: duration / 100;
|
||||
|
||||
this.controls.positionAdjustment.set_upper(Math.floor(duration));
|
||||
this.controls.positionAdjustment.set_upper(duration);
|
||||
this.controls.positionAdjustment.set_step_increment(increment);
|
||||
this.controls.positionAdjustment.set_page_increment(increment);
|
||||
|
||||
this.controls.durationFormated = this.controls._getFormatedTime(duration);
|
||||
this.controls._onPositionScaleValueChanged();
|
||||
this.controls.currentDuration = duration;
|
||||
this.controls.durationFormated = Misc.getFormatedTime(duration);
|
||||
this.controls.updateElapsedLabel();
|
||||
}
|
||||
|
||||
_onPlayerPositionUpdated(player, position)
|
||||
{
|
||||
if(
|
||||
!this.isSeekable
|
||||
|| !this._player.seek_done
|
||||
|| !player.seek_done
|
||||
|| this.controls.isPositionSeeking
|
||||
|| this._player.state === GstPlayer.PlayerState.BUFFERING
|
||||
|| player.state === GstPlayer.PlayerState.BUFFERING
|
||||
)
|
||||
return;
|
||||
|
||||
let positionSeconds = Math.round(position / 1000000000);
|
||||
|
||||
if(positionSeconds === this.lastPositionValue)
|
||||
if(positionSeconds === this.controls.currentPosition)
|
||||
return;
|
||||
|
||||
this.lastPositionValue = positionSeconds;
|
||||
this.controls.currentPosition = positionSeconds;
|
||||
this.controls.positionScale.set_value(positionSeconds);
|
||||
}
|
||||
|
||||
_onPlayerVolumeChanged()
|
||||
_onPlayerVolumeChanged(player)
|
||||
{
|
||||
let volume = Number(this._player.get_volume().toFixed(2));
|
||||
|
||||
if(volume === this.lastVolumeValue)
|
||||
let volume = Number(player.get_volume().toFixed(2));
|
||||
if(volume === this.currentVolume)
|
||||
return;
|
||||
|
||||
this.lastVolumeValue = volume;
|
||||
this.controls.currentVolume = volume;
|
||||
this.controls.volumeScale.set_value(volume);
|
||||
}
|
||||
|
||||
_onPositionSeekingChanged(self, isPositionSeeking)
|
||||
_onStateNotify(toplevel)
|
||||
{
|
||||
if(isPositionSeeking || !this.seekOnDrop)
|
||||
let isFullscreen = Boolean(
|
||||
toplevel.state & Gdk.ToplevelState.FULLSCREEN
|
||||
);
|
||||
if(this.fullscreenMode === isFullscreen)
|
||||
return;
|
||||
|
||||
this._onControlsPositionChanged(this.controls.positionScale);
|
||||
}
|
||||
this.fullscreenMode = isFullscreen;
|
||||
|
||||
_onControlsTogglePlayClicked()
|
||||
{
|
||||
this._player.toggle_play();
|
||||
}
|
||||
|
||||
_onControlsPositionChanged(positionScale)
|
||||
{
|
||||
if(this.seekOnDrop && this.controls.isPositionSeeking)
|
||||
return;
|
||||
|
||||
let positionSeconds = Math.round(positionScale.get_value());
|
||||
|
||||
if(positionSeconds === this.lastPositionValue)
|
||||
return;
|
||||
|
||||
this.lastPositionValue = positionSeconds;
|
||||
this._player.seek_seconds(positionSeconds);
|
||||
|
||||
/* Needed to enable preview after playback is stopped */
|
||||
if(this._player.state === GstPlayer.PlayerState.STOPPED)
|
||||
this._player.pause();
|
||||
|
||||
if(this.fullscreenMode)
|
||||
this.updateTime();
|
||||
}
|
||||
|
||||
_onControlsVolumeChanged(volumeScale)
|
||||
{
|
||||
let volume = Number(volumeScale.get_value().toFixed(2));
|
||||
|
||||
let icon = (volume <= 0)
|
||||
? 'muted'
|
||||
: (volume <= 0.33)
|
||||
? 'low'
|
||||
: (volume <= 0.66)
|
||||
? 'medium'
|
||||
: (volume <= 1)
|
||||
? 'high'
|
||||
: 'overamplified';
|
||||
|
||||
let iconName = `audio-volume-${icon}-symbolic`;
|
||||
if(this.controls.volumeButton.icon_name !== iconName)
|
||||
{
|
||||
debug(`set volume icon: ${icon}`);
|
||||
this.controls.volumeButton.set_icon_name(iconName);
|
||||
if(isFullscreen) {
|
||||
this.remove(this.controls);
|
||||
this.revealerBottom.append(this.controls);
|
||||
}
|
||||
else {
|
||||
this.revealerBottom.remove(this.controls);
|
||||
this.attach(this.controls, 0, 1, 1, 1);
|
||||
}
|
||||
|
||||
if(volume === this.lastVolumeValue)
|
||||
return;
|
||||
this.controls.setFullscreenMode(isFullscreen);
|
||||
this.showControls(isFullscreen);
|
||||
this.player.widget.grab_focus();
|
||||
|
||||
this.lastVolumeValue = volume;
|
||||
this._player.set_volume(volume);
|
||||
debug(`interface in fullscreen mode: ${isFullscreen}`);
|
||||
}
|
||||
|
||||
_onDestroy()
|
||||
_onMap()
|
||||
{
|
||||
this.disconnect(this.destroySignal);
|
||||
this.disconnect(this.mapSignal);
|
||||
|
||||
if(
|
||||
this._player
|
||||
&& this._player.state !== GstPlayer.PlayerState.STOPPED
|
||||
)
|
||||
this._player.stop();
|
||||
let root = this.get_root();
|
||||
if(!root) return;
|
||||
|
||||
this.controls.emit('destroy');
|
||||
let surface = root.get_surface();
|
||||
surface.connect('notify::state', this._onStateNotify.bind(this));
|
||||
}
|
||||
});
|
@@ -1,49 +1,18 @@
|
||||
const { Gdk, GObject, Gtk } = imports.gi;
|
||||
|
||||
var Window = GObject.registerClass({
|
||||
Signals: {
|
||||
'fullscreen-changed': {
|
||||
param_types: [GObject.TYPE_BOOLEAN]
|
||||
},
|
||||
}
|
||||
}, class ClapperWindow extends Gtk.ApplicationWindow
|
||||
var Window = GObject.registerClass(
|
||||
class ClapperWindow extends Gtk.ApplicationWindow
|
||||
{
|
||||
_init(application, title)
|
||||
{
|
||||
super._init({
|
||||
application: application,
|
||||
title: title || 'Clapper',
|
||||
resizable: true,
|
||||
destroy_with_parent: true,
|
||||
title: title,
|
||||
});
|
||||
this.isFullscreen = false;
|
||||
this.mapSignal = this.connect('map', this._onMap.bind(this));
|
||||
}
|
||||
|
||||
toggleFullscreen()
|
||||
updateTitlebar(mediaInfo)
|
||||
{
|
||||
let un = (this.isFullscreen) ? 'un' : '';
|
||||
this[`${un}fullscreen`]();
|
||||
}
|
||||
|
||||
_onStateNotify(toplevel)
|
||||
{
|
||||
let isFullscreen = Boolean(
|
||||
toplevel.state & Gdk.ToplevelState.FULLSCREEN
|
||||
);
|
||||
|
||||
if(this.isFullscreen === isFullscreen)
|
||||
return;
|
||||
|
||||
this.isFullscreen = isFullscreen;
|
||||
this.emit('fullscreen-changed', this.isFullscreen);
|
||||
}
|
||||
|
||||
_onMap()
|
||||
{
|
||||
this.disconnect(this.mapSignal);
|
||||
|
||||
let surface = this.get_surface();
|
||||
surface.connect('notify::state', this._onStateNotify.bind(this));
|
||||
|
||||
}
|
||||
});
|
||||
|
@@ -5,7 +5,6 @@ imports.searchPath.unshift('@importspath@');
|
||||
const ClapperSrc = imports.clapper_src;
|
||||
|
||||
var { App } = ClapperSrc.app;
|
||||
var { Interface } = ClapperSrc.interface;
|
||||
var { Player } = ClapperSrc.player;
|
||||
var { Widget } = ClapperSrc.widget;
|
||||
|
||||
imports.searchPath.shift();
|
||||
|
Reference in New Issue
Block a user