Add top overlay with title and current hour

This adds Kodi-like semi-transparent overlay with current media title, hour and estimated time when video will end. The overlay is visible only on fullscreen mode.
This commit is contained in:
Rafostar
2020-09-15 21:08:46 +02:00
parent 779796c2c3
commit 73e7f1e2a0
4 changed files with 133 additions and 22 deletions

View File

@@ -6,7 +6,7 @@
- [X] Switching video/audio/subtitles tracks from bottom bar (MPV) - [X] Switching video/audio/subtitles tracks from bottom bar (MPV)
- [X] Over-amplification supported by default (VLC) - [X] Over-amplification supported by default (VLC)
- [X] Audio visualizations (VLC) - [X] Audio visualizations (VLC)
- [ ] Clock with current hour and "Ends at" time on top overlay (Kodi) - [X] Clock with current hour and "Ends at" time on top overlay (Kodi)
- [ ] Auto select subtitles matching OS language (Totem) - [ ] Auto select subtitles matching OS language (Totem)
- [ ] Picture-in-Picture mode window - [ ] Picture-in-Picture mode window
- [ ] Touch gestures/swipes support - [ ] Touch gestures/swipes support

View File

@@ -70,8 +70,24 @@ var App = GObject.registerClass({
this.hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => { this.hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => {
this.hideControlsTimeout = null; this.hideControlsTimeout = null;
if(this.window.isFullscreen && this.isCursorInPlayer) if(this.window.isFullscreen && this.isCursorInPlayer) {
this.clearTimeout('updateTime');
this.interface.revealControls(false); 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; return GLib.SOURCE_REMOVE;
}); });
@@ -84,6 +100,9 @@ var App = GObject.registerClass({
GLib.source_remove(this[`${name}Timeout`]); GLib.source_remove(this[`${name}Timeout`]);
this[`${name}Timeout`] = null; this[`${name}Timeout`] = null;
if(name === 'updateTime')
debug('cleared update time interval');
} }
_buildUI() _buildUI()
@@ -146,6 +165,9 @@ var App = GObject.registerClass({
this.player.connect('error', this._onPlayerError.bind(this)); this.player.connect('error', this._onPlayerError.bind(this));
this.player.connect('state-changed', this._onPlayerStateChanged.bind(this)); this.player.connect('state-changed', this._onPlayerStateChanged.bind(this));
this.interface.revealerTop.connect(
'button-press-event', this._onPlayerButtonPressEvent.bind(this)
);
this.player.connectWidget( this.player.connectWidget(
'button-press-event', this._onPlayerButtonPressEvent.bind(this) 'button-press-event', this._onPlayerButtonPressEvent.bind(this)
); );
@@ -177,12 +199,15 @@ var App = GObject.registerClass({
this.interface.controls.setVolumeMarks(false); this.interface.controls.setVolumeMarks(false);
if(isFullscreen) { if(isFullscreen) {
this.setUpdateTimeInterval();
this.interface.showControls(true); this.interface.showControls(true);
this.setHideControlsTimeout(); this.setHideControlsTimeout();
this.interface.controls.unfullscreenButton.set_sensitive(true); this.interface.controls.unfullscreenButton.set_sensitive(true);
this.interface.controls.unfullscreenButton.show(); this.interface.controls.unfullscreenButton.show();
} }
else { else {
this.clearTimeout('updateTime');
this.interface.showControls(false);
this.interface.controls.unfullscreenButton.set_sensitive(false); this.interface.controls.unfullscreenButton.set_sensitive(false);
this.interface.controls.unfullscreenButton.hide(); this.interface.controls.unfullscreenButton.hide();
} }
@@ -308,7 +333,7 @@ var App = GObject.registerClass({
this._handlePrimaryButtonPress(event, button); this._handlePrimaryButtonPress(event, button);
break; break;
case Gdk.BUTTON_SECONDARY: case Gdk.BUTTON_SECONDARY:
if(event.get_event_type() !== Gdk.EventType.DOUBLE_BUTTON_PRESS) if(event.get_event_type() === Gdk.EventType.BUTTON_PRESS)
this.player.toggle_play(); this.player.toggle_play();
break; break;
default: default:
@@ -360,9 +385,12 @@ var App = GObject.registerClass({
this.setHideCursorTimeout(); this.setHideCursorTimeout();
if(this.window.isFullscreen) { if(this.window.isFullscreen) {
this.setHideControlsTimeout(); if(!this.interface.revealerTop.get_reveal_child()) {
this.setUpdateTimeInterval();
this.interface.revealControls(true); this.interface.revealControls(true);
} }
this.setHideControlsTimeout();
}
else if(this.hideControlsTimeout) { else if(this.hideControlsTimeout) {
this.clearTimeout('hideControls'); this.clearTimeout('hideControls');
} }

View File

@@ -1,4 +1,4 @@
const { GLib, GObject, Gtk, Gst, GstPlayer } = imports.gi; const { Gdk, GLib, GObject, Gtk, Gst, GstPlayer } = imports.gi;
const { Controls } = imports.clapper_src.controls; const { Controls } = imports.clapper_src.controls;
const Debug = imports.clapper_src.debug; const Debug = imports.clapper_src.debug;
@@ -26,23 +26,64 @@ class ClapperInterface extends Gtk.Grid
this.headerBar = null; this.headerBar = null;
this.defaultTitle = null; this.defaultTitle = null;
let initTime = GLib.DateTime.new_now_local().format('%X');
this.timeFormat = (initTime.length > 8)
? '%I:%M %p'
: '%H:%M';
this.videoBox = new Gtk.Box(); this.videoBox = new Gtk.Box();
this.overlay = new Gtk.Overlay(); this.overlay = new Gtk.Overlay();
this.revealer = new Gtk.Revealer({ this.revealerTop = new Gtk.Revealer({
transition_duration: this.revealTime,
transition_type: Gtk.RevealerTransitionType.CROSSFADE,
valign: Gtk.Align.START,
});
this.revealerBottom = new Gtk.Revealer({
transition_duration: this.revealTime, transition_duration: this.revealTime,
transition_type: Gtk.RevealerTransitionType.SLIDE_UP, transition_type: Gtk.RevealerTransitionType.SLIDE_UP,
valign: Gtk.Align.END, valign: Gtk.Align.END,
}); });
this.revealerBox = new Gtk.Box(); this.revealerGridTop = new Gtk.Grid();
this.revealerBoxBottom = new Gtk.Box();
this.controls = new Controls(); this.controls = new Controls();
this.fsTitle = new Gtk.Label({
expand: true,
margin_left: 12,
xalign: 0,
yalign: 0.22,
});
let timeLabelOpts = {
margin_right: 10,
xalign: 1,
yalign: 0,
};
this.fsTime = new Gtk.Label(timeLabelOpts);
this.fsEndTime = new Gtk.Label(timeLabelOpts);
this.revealerGridTop.attach(this.fsTitle, 0, 0, 1, 1);
this.revealerGridTop.attach(this.fsTime, 1, 0, 1, 1);
this.revealerGridTop.attach(this.fsEndTime, 1, 0, 1, 1);
this.videoBox.get_style_context().add_class('videobox'); this.videoBox.get_style_context().add_class('videobox');
this.revealerBox.get_style_context().add_class('osd'); let revealerGridTopContext = this.revealerGridTop.get_style_context();
revealerGridTopContext.add_class('osd');
revealerGridTopContext.add_class('reavealertop');
this.revealerBoxBottom.get_style_context().add_class('osd');
this.fsTime.get_style_context().add_class('osdtime');
this.fsEndTime.get_style_context().add_class('osdendtime');
this.videoBox.pack_start(this.overlay, true, true, 0); this.videoBox.pack_start(this.overlay, true, true, 0);
this.revealer.add(this.revealerBox); this.revealerBottom.add(this.revealerBoxBottom);
this.revealerTop.add(this.revealerGridTop);
this.attach(this.videoBox, 0, 0, 1, 1); this.attach(this.videoBox, 0, 0, 1, 1);
this.attach(this.controls, 0, 1, 1, 1); this.attach(this.controls, 0, 1, 1, 1);
this.revealerTop.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
this.revealerTop.show_all();
this.revealerBottom.show_all();
} }
addPlayer(player) addPlayer(player)
@@ -89,16 +130,18 @@ class ClapperInterface extends Gtk.Grid
revealControls(isReveal) revealControls(isReveal)
{ {
this.revealer.set_transition_duration(this.revealTime); for(let pos of ['Bottom', 'Top']) {
this.revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_UP); this[`revealer${pos}`].set_transition_duration(this.revealTime);
this.revealer.set_reveal_child(isReveal); this[`revealer${pos}`].set_reveal_child(isReveal);
}
} }
showControls(isShow) showControls(isShow)
{ {
this.revealer.set_transition_duration(0); for(let pos of ['Bottom', 'Top']) {
this.revealer.set_transition_type(Gtk.RevealerTransitionType.NONE); this[`revealer${pos}`].set_transition_duration(0);
this.revealer.set_reveal_child(isShow); this[`revealer${pos}`].set_reveal_child(isShow);
}
} }
setControlsOnVideo(isOnVideo) setControlsOnVideo(isOnVideo)
@@ -109,17 +152,16 @@ class ClapperInterface extends Gtk.Grid
if(isOnVideo) { if(isOnVideo) {
this.remove(this.controls); this.remove(this.controls);
this.controls.pack_start(this.controls.unfullscreenButton.box, false, false, 0); this.controls.pack_start(this.controls.unfullscreenButton.box, false, false, 0);
this.overlay.add_overlay(this.revealer); this.overlay.add_overlay(this.revealerBottom);
this.revealerBox.pack_start(this.controls, false, true, 0); this.overlay.add_overlay(this.revealerTop);
this.revealer.show(); this.revealerBoxBottom.pack_start(this.controls, false, true, 0);
this.revealerBox.show();
} }
else { else {
this.revealerBox.remove(this.controls); this.revealerBoxBottom.remove(this.controls);
this.controls.remove(this.controls.unfullscreenButton.box); this.controls.remove(this.controls.unfullscreenButton.box);
this.overlay.remove(this.revealer); this.overlay.remove(this.revealerBottom);
this.overlay.remove(this.revealerTop);
this.attach(this.controls, 0, 1, 1, 1); this.attach(this.controls, 0, 1, 1, 1);
this.controls.show();
} }
this.controlsInVideo = isOnVideo; this.controlsInVideo = isOnVideo;
@@ -253,6 +295,27 @@ class ClapperInterface extends Gtk.Grid
this.headerBar.set_title(title); this.headerBar.set_title(title);
this.headerBar.set_subtitle(subtitle); this.headerBar.set_subtitle(subtitle);
this.fsTitle.label = title;
}
updateTime()
{
let currTime = GLib.DateTime.new_now_local();
let endTime = currTime.add_seconds(
this.controls.positionAdjustment.get_upper() - this.lastPositionValue
);
let now = currTime.format(this.timeFormat);
this.fsTime.set_label(now);
this.fsEndTime.set_label(`Ends at: ${endTime.format(this.timeFormat)}`);
// Make sure that next timeout is always run after clock changes,
// by delaying it for additional few milliseconds
let nextUpdate = 60002 - parseInt(currTime.get_seconds() * 1000);
debug(`updated current time: ${now}`);
return nextUpdate;
} }
showVisualizationsButton(isShow) showVisualizationsButton(isShow)
@@ -444,6 +507,9 @@ class ClapperInterface extends Gtk.Grid
this.lastPositionValue = positionSeconds; this.lastPositionValue = positionSeconds;
this._player.seek_seconds(positionSeconds); this._player.seek_seconds(positionSeconds);
if(this.controls.fullscreenMode)
this.updateTime();
} }
_onControlsVolumeChanged(volumeScale) _onControlsVolumeChanged(volumeScale)

View File

@@ -24,6 +24,23 @@ scale marks {
.videobox { .videobox {
background: black; background: black;
} }
.reavealertop {
min-height: 100px;
box-shadow: inset 0px 200px 10px -124px rgba(0,0,0,0.4);
font-size: 32px;
font-weight: 500;
background: transparent;
}
.osdtime {
margin-top: 0px;
font-size: 40px;
font-weight: 700;
}
.osdendtime {
margin-top: 42px;
font-size: 24px;
font-weight: 600;
}
/* Position Scale */ /* Position Scale */
.positionscale value { .positionscale value {