mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 07:42:23 +02:00
@@ -6,6 +6,8 @@ Categories=GTK;GNOME;AudioVideo;Player;Video;TV;
|
||||
MimeType=application/claps;application/mpeg4-iod;application/mpeg4-muxcodetable;application/mxf;application/ogg;application/ram;application/sdp;application/streamingmedia;application/vnd.apple.mpegurl;application/vnd.ms-asf;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;application/x-extension-m4a;application/x-extension-mp4;application/x-flac;application/x-flash-video;application/x-matroska;application/x-ogg;application/x-streamingmedia;audio/3gpp;audio/3gpp2;audio/aac;audio/ac3;audio/amr;audio/amr-wb;audio/basic;audio/dv;audio/eac3;audio/flac;audio/m4a;audio/midi;audio/mp1;audio/mp2;audio/mp3;audio/mp4;audio/mpeg;audio/mpegurl;audio/mpg;audio/ogg;audio/opus;audio/scpls;audio/vnd.dolby.heaac.1;audio/vnd.dolby.heaac.2;audio/vnd.dolby.mlp;audio/vnd.dts;audio/vnd.dts.hd;audio/vnd.rn-realaudio;audio/wav;audio/webm;audio/x-aac;audio/x-aiff;audio/x-ape;audio/x-flac;audio/x-gsm;audio/x-it;audio/x-m4a;audio/x-matroska;audio/x-mod;audio/x-mp1;audio/x-mp2;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-mpg;audio/x-ms-asf;audio/x-ms-wma;audio/x-musepack;audio/x-pn-aiff;audio/x-pn-au;audio/x-pn-realaudio;audio/x-pn-wav;audio/x-real-audio;audio/x-realaudio;audio/x-s3m;audio/x-scpls;audio/x-shorten;audio/x-speex;audio/x-tta;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-wav;audio/x-wavpack;audio/x-xm;video/3gp;video/3gpp;video/3gpp2;video/divx;video/dv;video/fli;video/flv;video/mp2t;video/mp4;video/mp4v-es;video/mpeg;video/mpeg-system;video/msvideo;video/ogg;video/quicktime;video/vnd.mpegurl;video/vnd.rn-realvideo;video/webm;video/x-avi;video/x-flc;video/x-fli;video/x-flv;video/x-m4v;video/x-matroska;video/x-mpeg;video/x-mpeg-system;video/x-mpeg2;video/x-ms-asf;video/x-ms-wm;video/x-ms-wmv;video/x-ms-wmx;video/x-msvideo;video/x-nsv;video/x-ogm+ogg;video/x-theora;video/x-theora+ogg;x-content/audio-cdda;x-content/audio-player;x-content/video-dvd;x-scheme-handler/mms;x-scheme-handler/mmsh;x-scheme-handler/rtmp;x-scheme-handler/rtp;x-scheme-handler/rtsp;
|
||||
Exec=com.github.rafostar.Clapper %U
|
||||
Icon=com.github.rafostar.Clapper
|
||||
DBusActivatable=true
|
||||
StartupNotify=true
|
||||
Terminal=false
|
||||
Type=Application
|
||||
# Translators: Search terms to find this application. Do NOT translate the semicolons!
|
||||
|
3
data/com.github.rafostar.Clapper.service.in
Normal file
3
data/com.github.rafostar.Clapper.service.in
Normal file
@@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=@app_id@
|
||||
Exec=@bindir@/@app_id@ --gapplication-service
|
@@ -32,3 +32,15 @@ gnome.compile_resources('com.github.rafostar.Clapper.data',
|
||||
install: true,
|
||||
install_dir: pkgdatadir,
|
||||
)
|
||||
|
||||
dbus_conf = configuration_data()
|
||||
dbus_conf.set('app_id', meson.project_name())
|
||||
dbus_conf.set('bindir', bindir)
|
||||
|
||||
configure_file(
|
||||
input: 'com.github.rafostar.Clapper.service.in',
|
||||
output: 'com.github.rafostar.Clapper.service',
|
||||
configuration: dbus_conf,
|
||||
install: true,
|
||||
install_dir: join_paths(datadir, 'dbus-1', 'services'),
|
||||
)
|
||||
|
5
lib/gst/plugin/gstclapperpaintable.c
vendored
5
lib/gst/plugin/gstclapperpaintable.c
vendored
@@ -84,6 +84,10 @@ gst_clapper_paintable_dispose (GObject *object)
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_LOCK (self);
|
||||
gst_clear_object (&self->importer);
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_UNLOCK (self);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||
}
|
||||
|
||||
@@ -95,7 +99,6 @@ gst_clapper_paintable_finalize (GObject *object)
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
g_weak_ref_clear (&self->widget);
|
||||
gst_clear_object (&self->importer);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
g_mutex_clear (&self->importer_lock);
|
||||
|
@@ -109,6 +109,11 @@ retrieve_gl_context_on_main (GstClapperGLBaseImporter *self)
|
||||
|
||||
gdk_display = gdk_display_get_default ();
|
||||
|
||||
if (G_UNLIKELY (!gdk_display)) {
|
||||
GST_ERROR_OBJECT (self, "Could not retrieve Gdk display");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(gdk_context = gdk_display_create_gl_context (gdk_display, &error))) {
|
||||
GST_ERROR_OBJECT (self, "Error creating Gdk GL context: %s",
|
||||
error ? error->message : "No error set by Gdk");
|
||||
@@ -465,7 +470,8 @@ _realize_gdk_context_with_api (GdkGLContext *gdk_context, GdkGLAPI api)
|
||||
static gboolean
|
||||
gst_clapper_gl_base_importer_gdk_context_realize (GstClapperGLBaseImporter *self, GdkGLContext *gdk_context)
|
||||
{
|
||||
GdkGLAPI preferred_api;
|
||||
GdkGLAPI preferred_api = GDK_GL_API_GL;
|
||||
GdkDisplay *gdk_display;
|
||||
const gchar *gl_env;
|
||||
gboolean success;
|
||||
|
||||
@@ -483,13 +489,34 @@ gst_clapper_gl_base_importer_gdk_context_realize (GstClapperGLBaseImporter *self
|
||||
return _realize_gdk_context_with_api (gdk_context, preferred_api);
|
||||
}
|
||||
|
||||
gdk_display = gdk_gl_context_get_display (gdk_context);
|
||||
GST_DEBUG_OBJECT (self, "Auto selecting GL API for display: %s",
|
||||
gdk_display_get_name (gdk_display));
|
||||
|
||||
/* Apple decoder uses rectangle texture-target, which GLES does not support.
|
||||
* For Linux we prefer GLES in order to get HW colorspace conversion.
|
||||
* For Linux we prefer EGL + GLES in order to get direct HW colorspace conversion.
|
||||
* Windows will try EGL + GLES setup first and auto fallback to WGL. */
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_MACOS
|
||||
preferred_api = GDK_GL_API_GL;
|
||||
#else
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (gdk_display))
|
||||
preferred_api = GDK_GL_API_GLES;
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_EGL
|
||||
if (GDK_IS_X11_DISPLAY (gdk_display) && gdk_x11_display_get_egl_display (gdk_display))
|
||||
preferred_api = GDK_GL_API_GLES;
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WIN32_EGL
|
||||
if (GDK_IS_WIN32_DISPLAY (gdk_display) && gdk_win32_display_get_egl_display (gdk_display))
|
||||
preferred_api = GDK_GL_API_GLES;
|
||||
#endif
|
||||
|
||||
/* FIXME: Remove once GStreamer can handle DRM modifiers. This tries to avoid
|
||||
* "scrambled" image on Linux with Intel GPUs that are mostly used together with
|
||||
* x86 CPUs at the expense of using slightly slower non-direct DMABuf import.
|
||||
* See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1236 */
|
||||
#if GST_CLAPPER_GL_BASE_IMPORTER_HAVE_WAYLAND || GST_CLAPPER_GL_BASE_IMPORTER_HAVE_X11_EGL
|
||||
#if !defined(HAVE_GST_PATCHES) && (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64))
|
||||
preferred_api = GDK_GL_API_GL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!(success = _realize_gdk_context_with_api (gdk_context, preferred_api))) {
|
||||
|
99
src/app.js
99
src/app.js
@@ -1,27 +1,92 @@
|
||||
const { Gio, GObject, Gdk, Gtk } = imports.gi;
|
||||
const { AppBase } = imports.src.appBase;
|
||||
const { Gio, GObject, Gtk } = imports.gi;
|
||||
const { Widget } = imports.src.widget;
|
||||
const Debug = imports.src.debug;
|
||||
const FileOps = imports.src.fileOps;
|
||||
const Misc = imports.src.misc;
|
||||
const Actions = imports.src.actions;
|
||||
|
||||
const { debug } = Debug;
|
||||
const { settings } = Misc;
|
||||
|
||||
var App = GObject.registerClass({
|
||||
GTypeName: 'ClapperApp',
|
||||
},
|
||||
class ClapperApp extends AppBase
|
||||
class ClapperApp extends Gtk.Application
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init();
|
||||
super._init({
|
||||
application_id: Misc.appId,
|
||||
flags: Gio.ApplicationFlags.HANDLES_OPEN,
|
||||
});
|
||||
|
||||
this.flags |= Gio.ApplicationFlags.HANDLES_OPEN;
|
||||
this.doneFirstActivate = false;
|
||||
this.isFileAppend = false;
|
||||
this.mapSignal = null;
|
||||
}
|
||||
|
||||
vfunc_startup()
|
||||
vfunc_open(files, hint)
|
||||
{
|
||||
super.vfunc_startup();
|
||||
super.vfunc_open(files, hint);
|
||||
|
||||
this.activate();
|
||||
this._openFilesAsync(files).catch(debug);
|
||||
}
|
||||
|
||||
vfunc_activate()
|
||||
{
|
||||
super.vfunc_activate();
|
||||
|
||||
if(!this.doneFirstActivate)
|
||||
this._onFirstActivate();
|
||||
|
||||
this.active_window.present();
|
||||
}
|
||||
|
||||
async _openFilesAsync(files)
|
||||
{
|
||||
const urisArr = [];
|
||||
|
||||
for(let file of files) {
|
||||
const uri = file.get_uri();
|
||||
if(!uri.startsWith('file:')) {
|
||||
urisArr.push(uri);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If file is not a dir its URI will be returned in an array */
|
||||
const uris = await FileOps.getDirFilesUrisPromise(file).catch(debug);
|
||||
if(uris && uris.length)
|
||||
urisArr.push(...uris);
|
||||
}
|
||||
|
||||
const [playlist, subs] = Misc.parsePlaylistFiles(urisArr);
|
||||
const { player } = this.active_window.get_child();
|
||||
const action = (this.isFileAppend) ? 'append' : 'set';
|
||||
|
||||
if(playlist && playlist.length)
|
||||
player[`${action}_playlist`](playlist);
|
||||
if(subs)
|
||||
player.set_subtitles(subs);
|
||||
|
||||
/* Restore default behavior */
|
||||
this.isFileAppend = false;
|
||||
}
|
||||
|
||||
_onFirstActivate()
|
||||
{
|
||||
const window = new Gtk.ApplicationWindow({
|
||||
application: this,
|
||||
title: Misc.appName,
|
||||
});
|
||||
|
||||
window.add_css_class('adwrounded');
|
||||
|
||||
if(!settings.get_boolean('render-shadows'))
|
||||
window.add_css_class('gpufriendly');
|
||||
|
||||
window.add_css_class('gpufriendlyfs');
|
||||
|
||||
const window = this.active_window;
|
||||
const clapperWidget = new Widget();
|
||||
const dummyHeaderbar = new Gtk.Box({
|
||||
can_focus: false,
|
||||
@@ -33,14 +98,20 @@ class ClapperApp extends AppBase
|
||||
window.set_child(clapperWidget);
|
||||
window.set_titlebar(dummyHeaderbar);
|
||||
|
||||
this.mapSignal = window.connect('map', this._onWindowMap.bind(this));
|
||||
for(let name in Actions.actions) {
|
||||
const simpleAction = new Gio.SimpleAction({ name });
|
||||
simpleAction.connect('activate', (action) =>
|
||||
Actions.handleAction(action, window)
|
||||
);
|
||||
this.add_action(simpleAction);
|
||||
|
||||
const accels = Actions.actions[name];
|
||||
if(accels)
|
||||
this.set_accels_for_action(`app.${name}`, accels);
|
||||
}
|
||||
|
||||
vfunc_open(files, hint)
|
||||
{
|
||||
super.vfunc_open(files, hint);
|
||||
|
||||
this._openFilesAsync(files).then(() => this.activate()).catch(debug);
|
||||
this.mapSignal = window.connect('map', this._onWindowMap.bind(this));
|
||||
this.doneFirstActivate = true;
|
||||
}
|
||||
|
||||
_onWindowMap(window)
|
||||
|
101
src/appBase.js
101
src/appBase.js
@@ -1,101 +0,0 @@
|
||||
const { Gio, GLib, GObject, Gtk } = imports.gi;
|
||||
const Debug = imports.src.debug;
|
||||
const FileOps = imports.src.fileOps;
|
||||
const Misc = imports.src.misc;
|
||||
const Actions = imports.src.actions;
|
||||
|
||||
const { debug } = Debug;
|
||||
const { settings } = Misc;
|
||||
|
||||
var AppBase = GObject.registerClass({
|
||||
GTypeName: 'ClapperAppBase',
|
||||
},
|
||||
class ClapperAppBase extends Gtk.Application
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init({
|
||||
application_id: Misc.appId,
|
||||
});
|
||||
|
||||
this.doneFirstActivate = false;
|
||||
this.isFileAppend = false;
|
||||
}
|
||||
|
||||
vfunc_startup()
|
||||
{
|
||||
super.vfunc_startup();
|
||||
|
||||
const window = new Gtk.ApplicationWindow({
|
||||
application: this,
|
||||
title: Misc.appName,
|
||||
});
|
||||
|
||||
/* FIXME: AFAIK there is no way to detect theme rounded corners.
|
||||
* Having 2/4 corners rounded in floating mode is not good. */
|
||||
window.add_css_class('adwrounded');
|
||||
|
||||
if(!settings.get_boolean('render-shadows'))
|
||||
window.add_css_class('gpufriendly');
|
||||
|
||||
window.add_css_class('gpufriendlyfs');
|
||||
}
|
||||
|
||||
vfunc_activate()
|
||||
{
|
||||
super.vfunc_activate();
|
||||
|
||||
if(!this.doneFirstActivate)
|
||||
this._onFirstActivate();
|
||||
|
||||
this.active_window.present_with_time(
|
||||
Math.floor(GLib.get_monotonic_time() / 1000)
|
||||
);
|
||||
}
|
||||
|
||||
async _openFilesAsync(files)
|
||||
{
|
||||
const urisArr = [];
|
||||
|
||||
for(let file of files) {
|
||||
const uri = file.get_uri();
|
||||
if(!uri.startsWith('file:')) {
|
||||
urisArr.push(uri);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If file is not a dir its URI will be returned in an array */
|
||||
const uris = await FileOps.getDirFilesUrisPromise(file).catch(debug);
|
||||
if(uris && uris.length)
|
||||
urisArr.push(...uris);
|
||||
}
|
||||
|
||||
const [playlist, subs] = Misc.parsePlaylistFiles(urisArr);
|
||||
const { player } = this.active_window.get_child();
|
||||
const action = (this.isFileAppend) ? 'append' : 'set';
|
||||
|
||||
if(playlist && playlist.length)
|
||||
player[`${action}_playlist`](playlist);
|
||||
if(subs)
|
||||
player.set_subtitles(subs);
|
||||
|
||||
/* Restore default behavior */
|
||||
this.isFileAppend = false;
|
||||
}
|
||||
|
||||
_onFirstActivate()
|
||||
{
|
||||
for(let name in Actions.actions) {
|
||||
const simpleAction = new Gio.SimpleAction({ name });
|
||||
simpleAction.connect('activate', (action) =>
|
||||
Actions.handleAction(action, this.active_window)
|
||||
);
|
||||
this.add_action(simpleAction);
|
||||
|
||||
const accels = Actions.actions[name];
|
||||
if(accels)
|
||||
this.set_accels_for_action(`app.${name}`, accels);
|
||||
}
|
||||
this.doneFirstActivate = true;
|
||||
}
|
||||
});
|
274
src/headerbar.js
274
src/headerbar.js
@@ -1,11 +1,233 @@
|
||||
const { GObject } = imports.gi;
|
||||
const { HeaderBarBase } = imports.src.headerbarBase;
|
||||
const { GObject, Gtk } = imports.gi;
|
||||
const Debug = imports.src.debug;
|
||||
const Misc = imports.src.misc;
|
||||
|
||||
const { debug } = Debug;
|
||||
|
||||
var HeaderBar = GObject.registerClass({
|
||||
GTypeName: 'ClapperHeaderBar',
|
||||
},
|
||||
class ClapperHeaderBar extends HeaderBarBase
|
||||
class ClapperHeaderBar extends Gtk.Box
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init({
|
||||
can_focus: false,
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
spacing: 6,
|
||||
margin_top: 6,
|
||||
margin_start: 6,
|
||||
margin_end: 6,
|
||||
});
|
||||
this.add_css_class('osdheaderbar');
|
||||
|
||||
this.isMaximized = false;
|
||||
this.isMenuOnLeft = true;
|
||||
|
||||
const uiBuilder = Misc.getBuilderForName('clapper.ui');
|
||||
|
||||
this.menuWidget = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
valign: Gtk.Align.CENTER,
|
||||
spacing: 6,
|
||||
});
|
||||
|
||||
this.menuButton = new Gtk.MenuButton({
|
||||
icon_name: 'open-menu-symbolic',
|
||||
valign: Gtk.Align.CENTER,
|
||||
can_focus: false,
|
||||
});
|
||||
const menuToggleButton = this.menuButton.get_first_child();
|
||||
menuToggleButton.add_css_class('osd');
|
||||
const mainMenuModel = uiBuilder.get_object('mainMenu');
|
||||
const mainMenuPopover = new HeaderBarPopover(mainMenuModel);
|
||||
this.menuButton.set_popover(mainMenuPopover);
|
||||
this.menuButton.add_css_class('circular');
|
||||
this.menuWidget.append(this.menuButton);
|
||||
|
||||
this.extraButtonsBox = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
this.extraButtonsBox.add_css_class('linked');
|
||||
|
||||
const floatButton = new Gtk.Button({
|
||||
icon_name: 'pip-in-symbolic',
|
||||
can_focus: false,
|
||||
});
|
||||
floatButton.add_css_class('osd');
|
||||
floatButton.add_css_class('circular');
|
||||
floatButton.add_css_class('linkedleft');
|
||||
floatButton.connect('clicked',
|
||||
this._onFloatButtonClicked.bind(this)
|
||||
);
|
||||
this.extraButtonsBox.append(floatButton);
|
||||
|
||||
const separator = new Gtk.Separator({
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
});
|
||||
separator.add_css_class('linkseparator');
|
||||
this.extraButtonsBox.append(separator);
|
||||
|
||||
const fullscreenButton = new Gtk.Button({
|
||||
icon_name: 'view-fullscreen-symbolic',
|
||||
can_focus: false,
|
||||
});
|
||||
fullscreenButton.add_css_class('osd');
|
||||
fullscreenButton.add_css_class('circular');
|
||||
fullscreenButton.add_css_class('linkedright');
|
||||
fullscreenButton.connect('clicked',
|
||||
this._onFullscreenButtonClicked.bind(this)
|
||||
);
|
||||
this.extraButtonsBox.append(fullscreenButton);
|
||||
this.menuWidget.append(this.extraButtonsBox);
|
||||
|
||||
this.spacerWidget = new Gtk.Box({
|
||||
hexpand: true,
|
||||
});
|
||||
|
||||
this.minimizeWidget = this._getWindowButton('minimize');
|
||||
this.maximizeWidget = this._getWindowButton('maximize');
|
||||
this.closeWidget = this._getWindowButton('close');
|
||||
|
||||
const gtkSettings = Gtk.Settings.get_default();
|
||||
this._onLayoutUpdate(gtkSettings);
|
||||
|
||||
gtkSettings.connect(
|
||||
'notify::gtk-decoration-layout',
|
||||
this._onLayoutUpdate.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
setMenuOnLeft(isOnLeft)
|
||||
{
|
||||
if(this.isMenuOnLeft === isOnLeft)
|
||||
return;
|
||||
|
||||
if(isOnLeft) {
|
||||
this.menuWidget.reorder_child_after(
|
||||
this.extraButtonsBox, this.menuButton
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.menuWidget.reorder_child_after(
|
||||
this.menuButton, this.extraButtonsBox
|
||||
);
|
||||
}
|
||||
|
||||
this.isMenuOnLeft = isOnLeft;
|
||||
}
|
||||
|
||||
setMaximized(isMaximized)
|
||||
{
|
||||
if(this.isMaximized === isMaximized)
|
||||
return;
|
||||
|
||||
this.maximizeWidget.icon_name = (isMaximized)
|
||||
? 'window-restore-symbolic'
|
||||
: 'window-maximize-symbolic';
|
||||
|
||||
this.isMaximized = isMaximized;
|
||||
}
|
||||
|
||||
_onLayoutUpdate(gtkSettings)
|
||||
{
|
||||
const gtkLayout = gtkSettings.gtk_decoration_layout;
|
||||
|
||||
this._replaceButtons(gtkLayout);
|
||||
}
|
||||
|
||||
_replaceButtons(gtkLayout)
|
||||
{
|
||||
const modLayout = gtkLayout.replace(':', ',spacer,');
|
||||
const layoutArr = modLayout.split(',');
|
||||
|
||||
let lastWidget = null;
|
||||
|
||||
let showMinimize = false;
|
||||
let showMaximize = false;
|
||||
let showClose = false;
|
||||
|
||||
let menuAdded = false;
|
||||
let spacerAdded = false;
|
||||
|
||||
debug(`headerbar layout: ${modLayout}`);
|
||||
|
||||
for(let name of layoutArr) {
|
||||
/* Menu might be named "appmenu" */
|
||||
if(!menuAdded && (!name || name === 'appmenu' || name === 'icon'))
|
||||
name = 'menu';
|
||||
|
||||
const widget = this[`${name}Widget`];
|
||||
if(!widget) continue;
|
||||
|
||||
if(!widget.parent)
|
||||
this.append(widget);
|
||||
else
|
||||
this.reorder_child_after(widget, lastWidget);
|
||||
|
||||
switch(name) {
|
||||
case 'spacer':
|
||||
spacerAdded = true;
|
||||
break;
|
||||
case 'minimize':
|
||||
showMinimize = true;
|
||||
break;
|
||||
case 'maximize':
|
||||
showMaximize = true;
|
||||
break;
|
||||
case 'close':
|
||||
showClose = true;
|
||||
break;
|
||||
case 'menu':
|
||||
this.setMenuOnLeft(!spacerAdded);
|
||||
menuAdded = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
lastWidget = widget;
|
||||
}
|
||||
|
||||
this.minimizeWidget.visible = showMinimize;
|
||||
this.maximizeWidget.visible = showMaximize;
|
||||
this.closeWidget.visible = showClose;
|
||||
}
|
||||
|
||||
_getWindowButton(name)
|
||||
{
|
||||
const button = new Gtk.Button({
|
||||
icon_name: `window-${name}-symbolic`,
|
||||
valign: Gtk.Align.CENTER,
|
||||
can_focus: false,
|
||||
});
|
||||
button.add_css_class('osd');
|
||||
button.add_css_class('circular');
|
||||
|
||||
if(name === 'maximize')
|
||||
name = 'toggle-maximized';
|
||||
|
||||
button.connect('clicked',
|
||||
this._onWindowButtonActivate.bind(this, name)
|
||||
);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
_updateFloatIcon(isFloating)
|
||||
{
|
||||
const floatButton = this.extraButtonsBox.get_first_child();
|
||||
if(!floatButton) return;
|
||||
|
||||
const iconName = (isFloating)
|
||||
? 'pip-out-symbolic'
|
||||
: 'pip-in-symbolic';
|
||||
|
||||
if(floatButton.icon_name !== iconName)
|
||||
floatButton.icon_name = iconName;
|
||||
}
|
||||
|
||||
_onWindowButtonActivate(action)
|
||||
{
|
||||
this.activate_action(`window.${action}`, null);
|
||||
@@ -29,3 +251,49 @@ class ClapperHeaderBar extends HeaderBarBase
|
||||
this.root.fullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
var HeaderBarPopover = GObject.registerClass({
|
||||
GTypeName: 'ClapperHeaderBarPopover',
|
||||
},
|
||||
class ClapperHeaderBarPopover extends Gtk.PopoverMenu
|
||||
{
|
||||
_init(model)
|
||||
{
|
||||
super._init({
|
||||
menu_model: model,
|
||||
});
|
||||
|
||||
this.connect('map', this._onMap.bind(this));
|
||||
this.connect('closed', this._onClosed.bind(this));
|
||||
}
|
||||
|
||||
_onMap()
|
||||
{
|
||||
const { child } = this.root;
|
||||
|
||||
if(
|
||||
!child
|
||||
|| !child.player
|
||||
|| !child.player.widget
|
||||
)
|
||||
return;
|
||||
|
||||
child.revealControls();
|
||||
child.isPopoverOpen = true;
|
||||
}
|
||||
|
||||
_onClosed()
|
||||
{
|
||||
const { child } = this.root;
|
||||
|
||||
if(
|
||||
!child
|
||||
|| !child.player
|
||||
|| !child.player.widget
|
||||
)
|
||||
return;
|
||||
|
||||
child.revealControls();
|
||||
child.isPopoverOpen = false;
|
||||
}
|
||||
});
|
||||
|
@@ -1,288 +0,0 @@
|
||||
const { GObject, Gtk } = imports.gi;
|
||||
const Debug = imports.src.debug;
|
||||
const Misc = imports.src.misc;
|
||||
|
||||
const { debug } = Debug;
|
||||
|
||||
var HeaderBarBase = GObject.registerClass({
|
||||
GTypeName: 'ClapperHeaderBarBase',
|
||||
},
|
||||
class ClapperHeaderBarBase extends Gtk.Box
|
||||
{
|
||||
_init()
|
||||
{
|
||||
super._init({
|
||||
can_focus: false,
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
spacing: 6,
|
||||
margin_top: 6,
|
||||
margin_start: 6,
|
||||
margin_end: 6,
|
||||
});
|
||||
this.add_css_class('osdheaderbar');
|
||||
|
||||
this.isMaximized = false;
|
||||
this.isMenuOnLeft = true;
|
||||
|
||||
const uiBuilder = Misc.getBuilderForName('clapper.ui');
|
||||
|
||||
this.menuWidget = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
valign: Gtk.Align.CENTER,
|
||||
spacing: 6,
|
||||
});
|
||||
|
||||
this.menuButton = new Gtk.MenuButton({
|
||||
icon_name: 'open-menu-symbolic',
|
||||
valign: Gtk.Align.CENTER,
|
||||
can_focus: false,
|
||||
});
|
||||
const menuToggleButton = this.menuButton.get_first_child();
|
||||
menuToggleButton.add_css_class('osd');
|
||||
const mainMenuModel = uiBuilder.get_object('mainMenu');
|
||||
const mainMenuPopover = new HeaderBarPopover(mainMenuModel);
|
||||
this.menuButton.set_popover(mainMenuPopover);
|
||||
this.menuButton.add_css_class('circular');
|
||||
this.menuWidget.append(this.menuButton);
|
||||
|
||||
this.extraButtonsBox = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.HORIZONTAL,
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
this.extraButtonsBox.add_css_class('linked');
|
||||
|
||||
const floatButton = new Gtk.Button({
|
||||
icon_name: 'pip-in-symbolic',
|
||||
can_focus: false,
|
||||
});
|
||||
floatButton.add_css_class('osd');
|
||||
floatButton.add_css_class('circular');
|
||||
floatButton.add_css_class('linkedleft');
|
||||
floatButton.connect('clicked',
|
||||
this._onFloatButtonClicked.bind(this)
|
||||
);
|
||||
this.extraButtonsBox.append(floatButton);
|
||||
|
||||
const separator = new Gtk.Separator({
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
});
|
||||
separator.add_css_class('linkseparator');
|
||||
this.extraButtonsBox.append(separator);
|
||||
|
||||
const fullscreenButton = new Gtk.Button({
|
||||
icon_name: 'view-fullscreen-symbolic',
|
||||
can_focus: false,
|
||||
});
|
||||
fullscreenButton.add_css_class('osd');
|
||||
fullscreenButton.add_css_class('circular');
|
||||
fullscreenButton.add_css_class('linkedright');
|
||||
fullscreenButton.connect('clicked',
|
||||
this._onFullscreenButtonClicked.bind(this)
|
||||
);
|
||||
this.extraButtonsBox.append(fullscreenButton);
|
||||
this.menuWidget.append(this.extraButtonsBox);
|
||||
|
||||
this.spacerWidget = new Gtk.Box({
|
||||
hexpand: true,
|
||||
});
|
||||
|
||||
this.minimizeWidget = this._getWindowButton('minimize');
|
||||
this.maximizeWidget = this._getWindowButton('maximize');
|
||||
this.closeWidget = this._getWindowButton('close');
|
||||
|
||||
const gtkSettings = Gtk.Settings.get_default();
|
||||
this._onLayoutUpdate(gtkSettings);
|
||||
|
||||
gtkSettings.connect(
|
||||
'notify::gtk-decoration-layout',
|
||||
this._onLayoutUpdate.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
setMenuOnLeft(isOnLeft)
|
||||
{
|
||||
if(this.isMenuOnLeft === isOnLeft)
|
||||
return;
|
||||
|
||||
if(isOnLeft) {
|
||||
this.menuWidget.reorder_child_after(
|
||||
this.extraButtonsBox, this.menuButton
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.menuWidget.reorder_child_after(
|
||||
this.menuButton, this.extraButtonsBox
|
||||
);
|
||||
}
|
||||
|
||||
this.isMenuOnLeft = isOnLeft;
|
||||
}
|
||||
|
||||
setMaximized(isMaximized)
|
||||
{
|
||||
if(this.isMaximized === isMaximized)
|
||||
return;
|
||||
|
||||
this.maximizeWidget.icon_name = (isMaximized)
|
||||
? 'window-restore-symbolic'
|
||||
: 'window-maximize-symbolic';
|
||||
|
||||
this.isMaximized = isMaximized;
|
||||
}
|
||||
|
||||
_onLayoutUpdate(gtkSettings)
|
||||
{
|
||||
const gtkLayout = gtkSettings.gtk_decoration_layout;
|
||||
|
||||
this._replaceButtons(gtkLayout);
|
||||
}
|
||||
|
||||
_replaceButtons(gtkLayout)
|
||||
{
|
||||
const modLayout = gtkLayout.replace(':', ',spacer,');
|
||||
const layoutArr = modLayout.split(',');
|
||||
|
||||
let lastWidget = null;
|
||||
|
||||
let showMinimize = false;
|
||||
let showMaximize = false;
|
||||
let showClose = false;
|
||||
|
||||
let menuAdded = false;
|
||||
let spacerAdded = false;
|
||||
|
||||
debug(`headerbar layout: ${modLayout}`);
|
||||
|
||||
for(let name of layoutArr) {
|
||||
/* Menu might be named "appmenu" */
|
||||
if(!menuAdded && (!name || name === 'appmenu' || name === 'icon'))
|
||||
name = 'menu';
|
||||
|
||||
const widget = this[`${name}Widget`];
|
||||
if(!widget) continue;
|
||||
|
||||
if(!widget.parent)
|
||||
this.append(widget);
|
||||
else
|
||||
this.reorder_child_after(widget, lastWidget);
|
||||
|
||||
switch(name) {
|
||||
case 'spacer':
|
||||
spacerAdded = true;
|
||||
break;
|
||||
case 'minimize':
|
||||
showMinimize = true;
|
||||
break;
|
||||
case 'maximize':
|
||||
showMaximize = true;
|
||||
break;
|
||||
case 'close':
|
||||
showClose = true;
|
||||
break;
|
||||
case 'menu':
|
||||
this.setMenuOnLeft(!spacerAdded);
|
||||
menuAdded = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
lastWidget = widget;
|
||||
}
|
||||
|
||||
this.minimizeWidget.visible = showMinimize;
|
||||
this.maximizeWidget.visible = showMaximize;
|
||||
this.closeWidget.visible = showClose;
|
||||
}
|
||||
|
||||
_getWindowButton(name)
|
||||
{
|
||||
const button = new Gtk.Button({
|
||||
icon_name: `window-${name}-symbolic`,
|
||||
valign: Gtk.Align.CENTER,
|
||||
can_focus: false,
|
||||
});
|
||||
button.add_css_class('osd');
|
||||
button.add_css_class('circular');
|
||||
|
||||
if(name === 'maximize')
|
||||
name = 'toggle-maximized';
|
||||
|
||||
button.connect('clicked',
|
||||
this._onWindowButtonActivate.bind(this, name)
|
||||
);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
_updateFloatIcon(isFloating)
|
||||
{
|
||||
const floatButton = this.extraButtonsBox.get_first_child();
|
||||
if(!floatButton) return;
|
||||
|
||||
const iconName = (isFloating)
|
||||
? 'pip-out-symbolic'
|
||||
: 'pip-in-symbolic';
|
||||
|
||||
if(floatButton.icon_name !== iconName)
|
||||
floatButton.icon_name = iconName;
|
||||
}
|
||||
|
||||
_onWindowButtonActivate(action)
|
||||
{
|
||||
}
|
||||
|
||||
_onFloatButtonClicked(button)
|
||||
{
|
||||
}
|
||||
|
||||
_onFullscreenButtonClicked(button)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
var HeaderBarPopover = GObject.registerClass({
|
||||
GTypeName: 'ClapperHeaderBarPopover',
|
||||
},
|
||||
class ClapperHeaderBarPopover extends Gtk.PopoverMenu
|
||||
{
|
||||
_init(model)
|
||||
{
|
||||
super._init({
|
||||
menu_model: model,
|
||||
});
|
||||
|
||||
this.connect('map', this._onMap.bind(this));
|
||||
this.connect('closed', this._onClosed.bind(this));
|
||||
}
|
||||
|
||||
_onMap()
|
||||
{
|
||||
const { child } = this.root;
|
||||
|
||||
if(
|
||||
!child
|
||||
|| !child.player
|
||||
|| !child.player.widget
|
||||
)
|
||||
return;
|
||||
|
||||
child.revealControls();
|
||||
child.isPopoverOpen = true;
|
||||
}
|
||||
|
||||
_onClosed()
|
||||
{
|
||||
const { child } = this.root;
|
||||
|
||||
if(
|
||||
!child
|
||||
|| !child.player
|
||||
|| !child.player.widget
|
||||
)
|
||||
return;
|
||||
|
||||
child.revealControls();
|
||||
child.isPopoverOpen = false;
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user