mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 23:32:04 +02:00
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:
2
TODO.md
2
TODO.md
@@ -6,7 +6,7 @@
|
||||
- [X] Switching video/audio/subtitles tracks from bottom bar (MPV)
|
||||
- [X] Over-amplification supported by default (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)
|
||||
- [ ] Picture-in-Picture mode window
|
||||
- [ ] Touch gestures/swipes support
|
||||
|
@@ -70,8 +70,24 @@ var App = GObject.registerClass({
|
||||
this.hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => {
|
||||
this.hideControlsTimeout = null;
|
||||
|
||||
if(this.window.isFullscreen && this.isCursorInPlayer)
|
||||
if(this.window.isFullscreen && this.isCursorInPlayer) {
|
||||
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;
|
||||
});
|
||||
@@ -84,6 +100,9 @@ var App = GObject.registerClass({
|
||||
|
||||
GLib.source_remove(this[`${name}Timeout`]);
|
||||
this[`${name}Timeout`] = null;
|
||||
|
||||
if(name === 'updateTime')
|
||||
debug('cleared update time interval');
|
||||
}
|
||||
|
||||
_buildUI()
|
||||
@@ -146,6 +165,9 @@ var App = GObject.registerClass({
|
||||
this.player.connect('error', this._onPlayerError.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(
|
||||
'button-press-event', this._onPlayerButtonPressEvent.bind(this)
|
||||
);
|
||||
@@ -177,12 +199,15 @@ var App = GObject.registerClass({
|
||||
this.interface.controls.setVolumeMarks(false);
|
||||
|
||||
if(isFullscreen) {
|
||||
this.setUpdateTimeInterval();
|
||||
this.interface.showControls(true);
|
||||
this.setHideControlsTimeout();
|
||||
this.interface.controls.unfullscreenButton.set_sensitive(true);
|
||||
this.interface.controls.unfullscreenButton.show();
|
||||
}
|
||||
else {
|
||||
this.clearTimeout('updateTime');
|
||||
this.interface.showControls(false);
|
||||
this.interface.controls.unfullscreenButton.set_sensitive(false);
|
||||
this.interface.controls.unfullscreenButton.hide();
|
||||
}
|
||||
@@ -308,7 +333,7 @@ var App = GObject.registerClass({
|
||||
this._handlePrimaryButtonPress(event, button);
|
||||
break;
|
||||
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();
|
||||
break;
|
||||
default:
|
||||
@@ -360,8 +385,11 @@ var App = GObject.registerClass({
|
||||
this.setHideCursorTimeout();
|
||||
|
||||
if(this.window.isFullscreen) {
|
||||
if(!this.interface.revealerTop.get_reveal_child()) {
|
||||
this.setUpdateTimeInterval();
|
||||
this.interface.revealControls(true);
|
||||
}
|
||||
this.setHideControlsTimeout();
|
||||
this.interface.revealControls(true);
|
||||
}
|
||||
else if(this.hideControlsTimeout) {
|
||||
this.clearTimeout('hideControls');
|
||||
|
@@ -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 Debug = imports.clapper_src.debug;
|
||||
|
||||
@@ -26,23 +26,64 @@ class ClapperInterface extends Gtk.Grid
|
||||
this.headerBar = 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.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_type: Gtk.RevealerTransitionType.SLIDE_UP,
|
||||
valign: Gtk.Align.END,
|
||||
});
|
||||
this.revealerBox = new Gtk.Box();
|
||||
this.revealerGridTop = new Gtk.Grid();
|
||||
this.revealerBoxBottom = new Gtk.Box();
|
||||
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.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.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.controls, 0, 1, 1, 1);
|
||||
|
||||
this.revealerTop.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
|
||||
this.revealerTop.show_all();
|
||||
this.revealerBottom.show_all();
|
||||
}
|
||||
|
||||
addPlayer(player)
|
||||
@@ -89,16 +130,18 @@ class ClapperInterface extends Gtk.Grid
|
||||
|
||||
revealControls(isReveal)
|
||||
{
|
||||
this.revealer.set_transition_duration(this.revealTime);
|
||||
this.revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_UP);
|
||||
this.revealer.set_reveal_child(isReveal);
|
||||
for(let pos of ['Bottom', 'Top']) {
|
||||
this[`revealer${pos}`].set_transition_duration(this.revealTime);
|
||||
this[`revealer${pos}`].set_reveal_child(isReveal);
|
||||
}
|
||||
}
|
||||
|
||||
showControls(isShow)
|
||||
{
|
||||
this.revealer.set_transition_duration(0);
|
||||
this.revealer.set_transition_type(Gtk.RevealerTransitionType.NONE);
|
||||
this.revealer.set_reveal_child(isShow);
|
||||
for(let pos of ['Bottom', 'Top']) {
|
||||
this[`revealer${pos}`].set_transition_duration(0);
|
||||
this[`revealer${pos}`].set_reveal_child(isShow);
|
||||
}
|
||||
}
|
||||
|
||||
setControlsOnVideo(isOnVideo)
|
||||
@@ -109,17 +152,16 @@ class ClapperInterface extends Gtk.Grid
|
||||
if(isOnVideo) {
|
||||
this.remove(this.controls);
|
||||
this.controls.pack_start(this.controls.unfullscreenButton.box, false, false, 0);
|
||||
this.overlay.add_overlay(this.revealer);
|
||||
this.revealerBox.pack_start(this.controls, false, true, 0);
|
||||
this.revealer.show();
|
||||
this.revealerBox.show();
|
||||
this.overlay.add_overlay(this.revealerBottom);
|
||||
this.overlay.add_overlay(this.revealerTop);
|
||||
this.revealerBoxBottom.pack_start(this.controls, false, true, 0);
|
||||
}
|
||||
else {
|
||||
this.revealerBox.remove(this.controls);
|
||||
this.revealerBoxBottom.remove(this.controls);
|
||||
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.controls.show();
|
||||
}
|
||||
|
||||
this.controlsInVideo = isOnVideo;
|
||||
@@ -253,6 +295,27 @@ class ClapperInterface extends Gtk.Grid
|
||||
|
||||
this.headerBar.set_title(title);
|
||||
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)
|
||||
@@ -444,6 +507,9 @@ class ClapperInterface extends Gtk.Grid
|
||||
|
||||
this.lastPositionValue = positionSeconds;
|
||||
this._player.seek_seconds(positionSeconds);
|
||||
|
||||
if(this.controls.fullscreenMode)
|
||||
this.updateTime();
|
||||
}
|
||||
|
||||
_onControlsVolumeChanged(volumeScale)
|
||||
|
@@ -24,6 +24,23 @@ scale marks {
|
||||
.videobox {
|
||||
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 */
|
||||
.positionscale value {
|
||||
|
Reference in New Issue
Block a user