Initial GTK4 port

Port most of the player to GTK4. Some things are still broken or disabled due to GTK change, but will be gradually fixed.
This commit is contained in:
Rafostar
2020-10-05 21:19:29 +02:00
parent e7e9b9c07d
commit bae0b805ea
10 changed files with 229 additions and 121 deletions

View File

@@ -1,5 +1,6 @@
const { Gdk, GLib, GObject, Gtk, GstPlayer } = imports.gi;
const Debug = imports.clapper_src.debug;
const { HeaderBar } = imports.clapper_src.headerbar;
const { Interface } = imports.clapper_src.interface;
const { Player } = imports.clapper_src.player;
const { Window } = imports.clapper_src.window;
@@ -47,24 +48,31 @@ var App = GObject.registerClass({
super.vfunc_startup();
this.window = new Window(this, APP_NAME);
this.window.connect('close-request', this._onWindowCloseRequest.bind(this));
this.window.connect('unmap', () => this.quit());
this.windowRealizeSignal = this.window.connect(
'realize', this._onWindowRealize.bind(this)
);
/*
this.window.connect(
'key-press-event', this._onWindowKeyPressEvent.bind(this)
);
*/
this.window.connect(
'fullscreen-changed', this._onWindowFullscreenChanged.bind(this)
);
this.interface = new Interface();
let headerBar = new Gtk.HeaderBar({
title: APP_NAME,
show_close_button: true,
});
headerBar.pack_end(this.interface.controls.openMenuButton);
headerBar.pack_end(this.interface.controls.fullscreenButton);
let headerStart = [];
let headerEnd = [
this.interface.controls.openMenuButton,
this.interface.controls.fullscreenButton
];
let headerBar = new HeaderBar(this.window, headerStart, headerEnd);
this.interface.addHeaderBar(headerBar, APP_NAME);
this.interface.controls.fullscreenButton.connect(
'clicked', () => this._onInterfaceToggleFullscreenClicked(true)
);
@@ -73,13 +81,19 @@ var App = GObject.registerClass({
);
this.window.set_titlebar(this.interface.headerBar);
this.window.add(this.interface);
this.window.set_child(this.interface);
}
vfunc_activate()
{
super.vfunc_activate();
this.window.show_all();
this.window.show();
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(),
this.cssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
);
}
run(arr)
@@ -146,24 +160,23 @@ var App = GObject.registerClass({
{
this.window.disconnect(this.windowRealizeSignal);
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
this.cssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
);
this.player = new Player();
if(!this.player.widget)
return this.quit();
/*
this.player.widget.add_events(
Gdk.EventMask.SCROLL_MASK
| Gdk.EventMask.ENTER_NOTIFY_MASK
| Gdk.EventMask.LEAVE_NOTIFY_MASK
);
*/
this.interface.addPlayer(this.player);
this.player.connect('warning', this._onPlayerWarning.bind(this));
this.player.connect('error', this._onPlayerError.bind(this));
this.player.connect('state-changed', this._onPlayerStateChanged.bind(this));
/*
this.player.connectWidget(
'button-press-event', this._onPlayerButtonPressEvent.bind(this)
);
@@ -176,13 +189,13 @@ var App = GObject.registerClass({
this.player.connectWidget(
'motion-notify-event', this._onPlayerMotionNotifyEvent.bind(this)
);
*/
/* Widget signals that are disconnected after first run */
this._playerRealizeSignal = this.player.widget.connect(
'realize', this._onPlayerRealize.bind(this)
);
this._playerDrawSignal = this.player.widget.connect(
'draw', this._onPlayerDraw.bind(this)
this._playerMapSignal = this.player.widget.connect(
'map', this._onPlayerMap.bind(this)
);
}
@@ -265,7 +278,7 @@ var App = GObject.registerClass({
this.player.renderer.expose();
let display = this.player.widget.get_display();
/*
this.defaultCursor = Gdk.Cursor.new_from_name(
display, 'default'
);
@@ -274,12 +287,13 @@ var App = GObject.registerClass({
);
this.playerWindow = this.player.widget.get_window();
*/
this.setHideCursorTimeout();
}
_onPlayerDraw(self, data)
_onPlayerMap(self, data)
{
this.player.widget.disconnect(this._playerDrawSignal);
this.player.widget.disconnect(this._playerMapSignal);
this.emit('ready', true);
if(this.playlist.length)
@@ -312,7 +326,7 @@ var App = GObject.registerClass({
this.inhibitCookie = null;
}
debug('set prevent suspend to: ' + this.is_inhibited(flags));
//debug('set prevent suspend to: ' + this.is_inhibited(flags));
}
_onPlayerButtonPressEvent(self, event)
@@ -420,4 +434,11 @@ var App = GObject.registerClass({
{
debug(error);
}
_onWindowCloseRequest()
{
this.window.destroy();
this.player.widget.emit('destroy');
this.interface.emit('destroy');
}
});

View File

@@ -9,26 +9,26 @@ class BoxedIconButton extends Gtk.Button
margin_top: 4,
margin_bottom: 4,
can_focus: false,
can_default: false,
//can_default: false,
});
this.isFullscreen = isFullscreen || false;
size = size || Gtk.IconSize.SMALL_TOOLBAR;
let image = Gtk.Image.new_from_icon_name(icon, size);
if(image)
this.set_image(image);
let image = Gtk.Image.new_from_icon_name(icon);
//if(image)
//this.set_image(image);
/*
this.image.defaultSize = size;
this.image.fullscreenSize = (size === Gtk.IconSize.SMALL_TOOLBAR)
? Gtk.IconSize.LARGE_TOOLBAR
: Gtk.IconSize.DND;
this.get_style_context().add_class('flat');
*/
this.add_css_class('flat');
this.box = new Gtk.Box();
this.box.pack_start(this, false, false, 0);
this.box.append(this);
super.show();
}
@@ -49,12 +49,12 @@ class BoxedIconButton extends Gtk.Button
this.isFullscreen = isFullscreen;
}
/*
show_all()
{
this.box.show_all();
}
*/
show()
{
this.box.show();
@@ -74,16 +74,16 @@ class BoxedPopoverButton extends BoxedIconButton
super._init(icon, size, isFullscreen);
this.popover = new Gtk.Popover({
relative_to: this.box
default_widget: this.box
});
this.popoverBox = new Gtk.Box({
orientation: Gtk.Orientation.VERTICAL
});
this.popover.add(this.popoverBox);
this.popover.set_child(this.popoverBox);
this.popoverBox.show();
if(this.isFullscreen)
this.popover.get_style_context().add_class('osd');
this.popover.add_css_class('osd');
}
setFullscreenMode(isEnabled)
@@ -91,8 +91,8 @@ class BoxedPopoverButton extends BoxedIconButton
if(this.isFullscreen === isEnabled)
return;
let action = (isEnabled) ? 'add_class' : 'remove_class';
this.popover.get_style_context()[action]('osd');
let action = (isEnabled) ? 'add' : 'remove';
this.popover[action + '_css_class']('osd');
super.setFullscreenMode(isEnabled);
}

View File

@@ -57,19 +57,18 @@ var Controls = GObject.registerClass({
);
this.fullscreenButton = Gtk.Button.new_from_icon_name(
'view-fullscreen-symbolic',
Gtk.IconSize.SMALL_TOOLBAR
//Gtk.IconSize.SMALL_TOOLBAR
);
this.setDefaultWidgetBehaviour(this.fullscreenButton);
this.openMenuButton = Gtk.Button.new_from_icon_name(
'open-menu-symbolic',
Gtk.IconSize.SMALL_TOOLBAR
//Gtk.IconSize.SMALL_TOOLBAR
);
this.setDefaultWidgetBehaviour(this.openMenuButton);
this.forall(this.setDefaultWidgetBehaviour);
//this.forall(this.setDefaultWidgetBehaviour);
this.realizeSignal = this.connect(
'realize', this._onControlsRealize.bind(this)
);
this.realizeSignal = this.connect('realize', this._onControlsRealize.bind(this));
this.destroySignal = this.connect('destroy', this._onControlsDestroy.bind(this));
}
pack_start(widget, expand, fill, padding)
@@ -81,7 +80,7 @@ var Controls = GObject.registerClass({
)
widget = widget.box;
super.pack_start(widget, expand, fill, padding);
super.append(widget);
}
setFullscreenMode(isFullscreen)
@@ -121,6 +120,8 @@ var Controls = GObject.registerClass({
addRadioButtons(box, array, activeId)
{
return;
let group = null;
let children = box.get_children();
let lastEl = (children.length > array.length)
@@ -175,7 +176,7 @@ var Controls = GObject.registerClass({
setDefaultWidgetBehaviour(widget)
{
widget.can_focus = false;
widget.can_default = false;
//widget.can_default = false;
}
setVolumeMarks(isAdded)
@@ -212,17 +213,21 @@ var Controls = GObject.registerClass({
);
this.togglePlayButton.setPlayImage = () =>
{
/*
this.togglePlayButton.image.set_from_icon_name(
'media-playback-start-symbolic',
this.togglePlayButton.image.icon_size
);
*/
}
this.togglePlayButton.setPauseImage = () =>
{
/*
this.togglePlayButton.image.set_from_icon_name(
'media-playback-pause-symbolic',
this.togglePlayButton.image.icon_size
);
*/
}
}
@@ -234,19 +239,18 @@ var Controls = GObject.registerClass({
draw_value: true,
hexpand: true,
});
let style = this.positionScale.get_style_context();
style.add_class('positionscale');
this.positionScale.connect(
'format-value', this._onPositionScaleFormatValue.bind(this)
this.positionScale.add_css_class('positionscale');
this.positionScale.set_format_value_func(
this._onPositionScaleFormatValue.bind(this)
);
/*
this.positionScale.connect(
'button-press-event', this._onPositionScaleButtonPressEvent.bind(this)
);
this.positionScale.connect(
'button-release-event', this._onPositionScaleButtonReleaseEvent.bind(this)
);
*/
this.positionAdjustment = this.positionScale.get_adjustment();
this.pack_start(this.positionScale, true, true, 0);
}
@@ -256,10 +260,10 @@ var Controls = GObject.registerClass({
this.volumeButton = this.addPopoverButton(
'audio-volume-muted-symbolic'
);
this.volumeButton.add_events(Gdk.EventMask.SCROLL_MASK);
this.volumeButton.connect(
'scroll-event', (self, event) => this._onScrollEvent(event)
);
//this.volumeButton.add_events(Gdk.EventMask.SCROLL_MASK);
//this.volumeButton.connect(
// 'scroll-event', (self, event) => this._onScrollEvent(event)
//);
this.volumeScale = new Gtk.Scale({
orientation: Gtk.Orientation.VERTICAL,
inverted: true,
@@ -268,7 +272,7 @@ var Controls = GObject.registerClass({
round_digits: 2,
vexpand: true,
});
this.volumeScale.get_style_context().add_class('volumescale');
this.volumeScale.add_css_class('volumescale');
this.volumeAdjustment = this.volumeScale.get_adjustment();
this.volumeAdjustment.set_upper(2);
@@ -276,8 +280,8 @@ var Controls = GObject.registerClass({
this.volumeAdjustment.set_page_increment(0.05);
this.setDefaultWidgetBehaviour(this.volumeScale);
this.volumeButton.popoverBox.add(this.volumeScale);
this.volumeButton.popoverBox.show_all();
this.volumeButton.popoverBox.append(this.volumeScale);
//this.volumeButton.popoverBox.show_all();
this.setVolumeMarks(true);
}
@@ -375,4 +379,10 @@ var Controls = GObject.registerClass({
break;
}
}
_onControlsDestroy()
{
this.disconnect(this.destroySignal);
this.positionScale.set_format_value_func(null);
}
});

73
clapper_src/headerbar.js Normal file
View File

@@ -0,0 +1,73 @@
const { GLib, GObject, Gtk, Pango } = imports.gi;
var HeaderBar = GObject.registerClass(
class ClapperHeaderBar extends Gtk.HeaderBar
{
_init(window, startButtons, endButtons)
{
super._init();
this.set_title_widget(this._createWidgetForWindow(window));
startButtons.forEach(btn => this.pack_start(btn));
endButtons.forEach(btn => this.pack_end(btn));
}
updateHeaderBar(mediaInfo)
{
let title = mediaInfo.get_title();
let subtitle = mediaInfo.get_uri() || null;
if(subtitle && subtitle.startsWith('file://')) {
subtitle = GLib.filename_from_uri(subtitle)[0];
subtitle = GLib.path_get_basename(subtitle);
}
if(!title) {
title = (!subtitle)
? this.defaultTitle
: (subtitle.includes('.'))
? subtitle.split('.').slice(0, -1).join('.')
: subtitle;
subtitle = null;
}
this.titleLabel.label = title;
this.subtitleLabel.visible = (subtitle !== null);
if(subtitle)
this.subtitleLabel.label = subtitle;
}
_createWidgetForWindow(window)
{
let box = new Gtk.Box ({
orientation: Gtk.Orientation.VERTICAL,
valign: Gtk.Align.CENTER
});
this.titleLabel = new Gtk.Label({
halign: Gtk.Align.CENTER,
single_line_mode: true,
ellipsize: Pango.EllipsizeMode.END,
width_chars: 5,
});
this.titleLabel.add_css_class('title');
this.titleLabel.set_parent(box);
window.bind_property('title', this.titleLabel, 'label',
GObject.BindingFlags.SYNC_CREATE
);
this.subtitleLabel = new Gtk.Label({
halign: Gtk.Align.CENTER,
single_line_mode: true,
ellipsize: Pango.EllipsizeMode.END,
});
this.subtitleLabel.add_css_class('subtitle');
this.subtitleLabel.set_parent(box);
this.subtitleLabel.visible = false;
return box;
}
});

View File

@@ -33,29 +33,33 @@ class ClapperInterface extends Gtk.Grid
this.revealerBottom = new Revealers.RevealerBottom();
this.controls = new Controls();
this.videoBox.get_style_context().add_class('videobox');
this.videoBox.pack_start(this.overlay, true, true, 0);
this.videoBox.add_css_class('videobox');
this.videoBox.append(this.overlay);
this.attach(this.videoBox, 0, 0, 1, 1);
this.attach(this.controls, 0, 1, 1, 1);
this.destroySignal = this.connect('destroy', this._onInterfaceDestroy.bind(this));
}
addPlayer(player)
{
this._player = player;
this._player.widget.expand = true;
this._player.widget.vexpand = true;
this._player.widget.hexpand = true;
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.connectWidget(
'scroll-event', (self, event) => this.controls._onScrollEvent(event)
);
*/
this.controls.togglePlayButton.connect(
'clicked', this._onControlsTogglePlayClicked.bind(this)
);
this.controls.positionScale.connect(
this.scaleSig = this.controls.positionScale.connect(
'value-changed', this._onControlsPositionChanged.bind(this)
);
this.controls.volumeScale.connect(
@@ -70,9 +74,9 @@ class ClapperInterface extends Gtk.Grid
this.controls.connect(
'visualization-change-requested', this._onVisualizationChangeRequested.bind(this)
);
this.revealerTop.connect('event-after', (self, event) => this._player.widget.event(event));
//this.revealerTop.connect('event-after', (self, event) => this._player.widget.event(event));
this.overlay.add(this._player.widget);
this.overlay.set_child(this._player.widget);
this.overlay.add_overlay(this.revealerTop);
this.overlay.add_overlay(this.revealerBottom);
@@ -122,8 +126,8 @@ class ClapperInterface extends Gtk.Grid
{
let mediaInfo = this._player.get_media_info();
// set titlebar media title and path
this.updateHeaderBar(mediaInfo);
/* Set titlebar media title and path */
this.updateTitles(mediaInfo);
// we can also check if video is "live" or "seekable" (right now unused)
// it might be a good idea to hide position seek bar and disable seeking
@@ -220,32 +224,12 @@ class ClapperInterface extends Gtk.Grid
}
}
updateHeaderBar(mediaInfo)
updateTitles(mediaInfo)
{
if(!this.headerBar)
return;
if(this.headerBar)
this.headerBar.updateHeaderBar(mediaInfo);
let title = mediaInfo.get_title();
let subtitle = mediaInfo.get_uri() || null;
if(subtitle.startsWith('file://')) {
subtitle = GLib.filename_from_uri(subtitle)[0];
subtitle = GLib.path_get_basename(subtitle);
}
if(!title) {
title = (!subtitle)
? this.defaultTitle
: (subtitle.includes('.'))
? subtitle.split('.').slice(0, -1).join('.')
: subtitle;
subtitle = null;
}
this.headerBar.set_title(title);
this.headerBar.set_subtitle(subtitle);
this.revealerTop.setMediaTitle(title);
this.revealerTop.setMediaTitle(this.headerBar.titleLabel.label);
}
updateTime()
@@ -300,8 +284,8 @@ class ClapperInterface extends Gtk.Grid
_onTrackChangeRequested(self, type, activeId)
{
// reenabling audio is slow (as expected),
// so it is better to toggle mute instead
/* 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);
@@ -313,8 +297,8 @@ class ClapperInterface extends Gtk.Grid
}
if(activeId < 0) {
// disabling video leaves last frame frozen,
// so we hide it by making it transparent
/* Disabling video leaves last frame frozen,
* so we hide it by making it transparent */
if(type === 'video')
this._player.widget.set_opacity(0);
@@ -468,7 +452,7 @@ class ClapperInterface extends Gtk.Grid
: 'overamplified';
let iconName = `audio-volume-${icon}-symbolic`;
/*
if(this.controls.volumeButton.image.icon_name !== iconName)
{
debug(`set volume icon: ${icon}`);
@@ -477,11 +461,17 @@ class ClapperInterface extends Gtk.Grid
this.controls.volumeButton.image.icon_size
);
}
*/
if(volume === this.lastVolumeValue)
return;
this.lastVolumeValue = volume;
this._player.set_volume(volume);
}
_onInterfaceDestroy()
{
this.disconnect(this.destroySignal);
this.controls.emit('destroy');
}
});

View File

@@ -21,7 +21,16 @@ class ClapperPlayer extends GstPlayer.Player
if(!Gst.is_initialized())
Gst.init(null);
let gtkglsink = Gst.ElementFactory.make('gtkglsink', 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;
@@ -35,8 +44,7 @@ class ClapperPlayer extends GstPlayer.Player
video_renderer: renderer
});
// assign elements to player for later access
// and make sure that GJS will not free them early
/* Assign elements to player for later access */
this.gtkglsink = gtkglsink;
this.glsinkbin = glsinkbin;
this.dispatcher = dispatcher;
@@ -61,6 +69,10 @@ class ClapperPlayer extends GstPlayer.Player
this.set_config(config);
this.set_mute(false);
/* FIXME: remove once GUI buttons are back */
this.set_volume(0.5);
this.set_plugin_rank('vah264dec', 300);
this.loop = GLib.MainLoop.new(null, false);
this.run_loop = opts.run_loop || false;
this.widget = gtkglsink.widget;

View File

@@ -130,7 +130,7 @@ class ClapperRevealerTop extends CustomRevealer
});
this.revealerName = 'top';
/*
this.set_events(
Gdk.EventMask.BUTTON_PRESS_MASK
| Gdk.EventMask.BUTTON_RELEASE_MASK
@@ -141,7 +141,7 @@ class ClapperRevealerTop extends CustomRevealer
| 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'
@@ -150,13 +150,13 @@ class ClapperRevealerTop extends CustomRevealer
this.revealerGrid = new Gtk.Grid({
column_spacing: 8
});
let gridContext = this.revealerGrid.get_style_context();
gridContext.add_class('osd');
gridContext.add_class('reavealertop');
this.revealerGrid.add_css_class('osd');
this.revealerGrid.add_css_class('reavealertop');
this.mediaTitle = new Gtk.Label({
ellipsize: Pango.EllipsizeMode.END,
expand: true,
vexpand: true,
hexpand: true,
margin_top: 14,
margin_start: 12,
xalign: 0,
@@ -169,17 +169,17 @@ class ClapperRevealerTop extends CustomRevealer
yalign: 0,
};
this.currentTime = new Gtk.Label(timeLabelOpts);
this.currentTime.get_style_context().add_class('osdtime');
this.currentTime.add_css_class('osdtime');
this.endTime = new Gtk.Label(timeLabelOpts);
this.endTime.get_style_context().add_class('osdendtime');
this.endTime.add_css_class('osdendtime');
this.revealerGrid.attach(this.mediaTitle, 0, 0, 1, 1);
this.revealerGrid.attach(this.currentTime, 1, 0, 1, 1);
this.revealerGrid.attach(this.endTime, 1, 0, 1, 1);
this.add(this.revealerGrid);
this.revealerGrid.show_all();
this.set_child(this.revealerGrid);
//this.revealerGrid.show_all();
}
setMediaTitle(title)
@@ -217,10 +217,10 @@ class ClapperRevealerBottom extends CustomRevealer
this.revealerName = 'bottom';
this.revealerBox = new Gtk.Box();
this.revealerBox.get_style_context().add_class('osd');
this.revealerBox.add_css_class('osd');
this.add(this.revealerBox);
this.revealerBox.show_all();
this.set_child(this.revealerBox);
//this.revealerBox.show_all();
}
addWidget(widget)

View File

@@ -13,11 +13,12 @@ var Window = GObject.registerClass({
super._init({
application: application,
title: title || 'Clapper',
border_width: 0,
//border_width: 0,
resizable: true,
window_position: Gtk.WindowPosition.CENTER,
//window_position: Gtk.WindowPosition.CENTER,
width_request: 960,
height_request: 642
height_request: 642,
destroy_with_parent: true,
});
this.isFullscreen = false;
}
@@ -27,7 +28,7 @@ var Window = GObject.registerClass({
let un = (this.isFullscreen) ? 'un' : '';
this[`${un}fullscreen`]();
}
/*
vfunc_window_state_event(event)
{
super.vfunc_window_state_event(event);
@@ -43,4 +44,5 @@ var Window = GObject.registerClass({
this.isFullscreen = isFullscreen;
this.emit('fullscreen-changed', this.isFullscreen);
}
*/
});

View File

@@ -1,5 +1,5 @@
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.Gtk = '3.0';
imports.gi.versions.Gdk = '4.0';
imports.gi.versions.Gtk = '4.0';
imports.searchPath.unshift('@importspath@');
const ClapperSrc = imports.clapper_src;

View File

@@ -1,5 +1,5 @@
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.Gtk = '3.0';
imports.gi.versions.Gdk = '4.0';
imports.gi.versions.Gtk = '4.0';
const { Gst } = imports.gi;
const { App } = imports.clapper_src.app;