mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 07:42:23 +02:00
Replace GTK headerbar with custom implementation
This avoids D&D controllers clash and allows to freely customize how maximize, minimize and close buttons work (differently for e.g. web application) and where are they placed
This commit is contained in:
@@ -18,10 +18,7 @@ radio {
|
|||||||
.gtk402 .osd trough highlight {
|
.gtk402 .osd trough highlight {
|
||||||
border-color: inherit;
|
border-color: inherit;
|
||||||
}
|
}
|
||||||
.osd headerbar {
|
.osdheaderbar button {
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
.osd headerbar button {
|
|
||||||
border: transparent;
|
border: transparent;
|
||||||
}
|
}
|
||||||
.adwrounded.csd {
|
.adwrounded.csd {
|
||||||
|
@@ -27,7 +27,7 @@ class ClapperApp extends AppBase
|
|||||||
window.isClapperApp = true;
|
window.isClapperApp = true;
|
||||||
window.add_css_class('nobackground');
|
window.add_css_class('nobackground');
|
||||||
|
|
||||||
const clapperWidget = new Widget(window);
|
const clapperWidget = new Widget();
|
||||||
window.set_child(clapperWidget);
|
window.set_child(clapperWidget);
|
||||||
|
|
||||||
const dummyHeaderbar = new Gtk.HeaderBar({
|
const dummyHeaderbar = new Gtk.HeaderBar({
|
||||||
|
@@ -4,11 +4,15 @@ const { HeaderBarBase } = imports.src.headerbarBase;
|
|||||||
var HeaderBar = GObject.registerClass(
|
var HeaderBar = GObject.registerClass(
|
||||||
class ClapperHeaderBar extends HeaderBarBase
|
class ClapperHeaderBar extends HeaderBarBase
|
||||||
{
|
{
|
||||||
_init(window)
|
_init()
|
||||||
{
|
{
|
||||||
super._init(window);
|
super._init();
|
||||||
|
this.add_css_class('osdheaderbar');
|
||||||
|
}
|
||||||
|
|
||||||
this.title_widget.visible = false;
|
_onWindowButtonActivate(action)
|
||||||
|
{
|
||||||
|
this.activate_action(action, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFloatButtonClicked()
|
_onFloatButtonClicked()
|
||||||
|
@@ -1,95 +1,193 @@
|
|||||||
const { GObject, Gtk, Pango } = imports.gi;
|
const { GObject, Gtk } = imports.gi;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
|
|
||||||
var HeaderBarBase = GObject.registerClass(
|
var HeaderBarBase = GObject.registerClass(
|
||||||
class ClapperHeaderBarBase extends Gtk.HeaderBar
|
class ClapperHeaderBarBase extends Gtk.Box
|
||||||
{
|
{
|
||||||
_init(window)
|
_init()
|
||||||
{
|
{
|
||||||
super._init({
|
super._init({
|
||||||
can_focus: false,
|
can_focus: false,
|
||||||
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
spacing: 6,
|
||||||
|
margin_top: 6,
|
||||||
|
margin_start: 6,
|
||||||
|
margin_end: 6,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.isMaximized = false;
|
||||||
|
this.isMenuOnLeft = true;
|
||||||
|
|
||||||
const clapperPath = Misc.getClapperPath();
|
const clapperPath = Misc.getClapperPath();
|
||||||
const uiBuilder = Gtk.Builder.new_from_file(
|
const uiBuilder = Gtk.Builder.new_from_file(
|
||||||
`${clapperPath}/ui/clapper.ui`
|
`${clapperPath}/ui/clapper.ui`
|
||||||
);
|
);
|
||||||
|
|
||||||
this.add_css_class('noborder');
|
this.menuWidget = new Gtk.Box({
|
||||||
this.set_title_widget(this._createWidgetForWindow(window));
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
valign: Gtk.Align.CENTER,
|
||||||
|
spacing: 6,
|
||||||
|
});
|
||||||
|
|
||||||
const mainMenuButton = new Gtk.MenuButton({
|
this.menuButton = new Gtk.MenuButton({
|
||||||
icon_name: 'open-menu-symbolic',
|
icon_name: 'open-menu-symbolic',
|
||||||
valign: Gtk.Align.CENTER,
|
valign: Gtk.Align.CENTER,
|
||||||
});
|
});
|
||||||
const mainMenuModel = uiBuilder.get_object('mainMenu');
|
const mainMenuModel = uiBuilder.get_object('mainMenu');
|
||||||
const mainMenuPopover = new HeaderBarPopover(mainMenuModel);
|
const mainMenuPopover = new HeaderBarPopover(mainMenuModel);
|
||||||
mainMenuButton.set_popover(mainMenuPopover);
|
this.menuButton.set_popover(mainMenuPopover);
|
||||||
mainMenuButton.add_css_class('circular');
|
this.menuButton.add_css_class('circular');
|
||||||
this.pack_start(mainMenuButton);
|
this.menuWidget.append(this.menuButton);
|
||||||
|
|
||||||
const buttonsBox = new Gtk.Box({
|
this.extraButtonsBox = new Gtk.Box({
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
valign: Gtk.Align.CENTER,
|
valign: Gtk.Align.CENTER,
|
||||||
});
|
});
|
||||||
buttonsBox.add_css_class('linked');
|
this.extraButtonsBox.add_css_class('linked');
|
||||||
|
|
||||||
const floatButton = new Gtk.Button({
|
const floatButton = new Gtk.Button({
|
||||||
icon_name: 'go-bottom-symbolic',
|
icon_name: 'go-bottom-symbolic',
|
||||||
});
|
});
|
||||||
floatButton.add_css_class('circular');
|
floatButton.add_css_class('circular');
|
||||||
floatButton.connect('clicked', this._onFloatButtonClicked.bind(this));
|
floatButton.connect('clicked',
|
||||||
buttonsBox.append(floatButton);
|
this._onFloatButtonClicked.bind(this)
|
||||||
|
);
|
||||||
|
this.extraButtonsBox.append(floatButton);
|
||||||
|
|
||||||
const fullscreenButton = new Gtk.Button({
|
const fullscreenButton = new Gtk.Button({
|
||||||
icon_name: 'view-fullscreen-symbolic',
|
icon_name: 'view-fullscreen-symbolic',
|
||||||
});
|
});
|
||||||
fullscreenButton.add_css_class('circular');
|
fullscreenButton.add_css_class('circular');
|
||||||
fullscreenButton.connect('clicked', this._onFullscreenButtonClicked.bind(this));
|
fullscreenButton.connect('clicked',
|
||||||
|
this._onFullscreenButtonClicked.bind(this)
|
||||||
|
);
|
||||||
|
this.extraButtonsBox.append(fullscreenButton);
|
||||||
|
this.menuWidget.append(this.extraButtonsBox);
|
||||||
|
|
||||||
buttonsBox.append(fullscreenButton);
|
this.spacerWidget = new Gtk.Box({
|
||||||
this.pack_start(buttonsBox);
|
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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHeaderBar(title, subtitle)
|
setMenuOnLeft(isOnLeft)
|
||||||
{
|
{
|
||||||
this.titleLabel.label = title;
|
if(this.isMenuOnLeft === isOnLeft)
|
||||||
this.subtitleLabel.visible = (subtitle !== null);
|
return;
|
||||||
|
|
||||||
if(subtitle)
|
if(isOnLeft) {
|
||||||
this.subtitleLabel.label = subtitle;
|
this.menuWidget.reorder_child_after(
|
||||||
|
this.extraButtonsBox, this.menuButton
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.menuWidget.reorder_child_after(
|
||||||
|
this.menuButton, this.extraButtonsBox
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createWidgetForWindow(window)
|
this.isMenuOnLeft = isOnLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaximized(isMaximized)
|
||||||
{
|
{
|
||||||
const box = new Gtk.Box({
|
if(this.isMaximized === isMaximized)
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
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 spacerAdded = false;
|
||||||
|
|
||||||
|
for(let name of layoutArr) {
|
||||||
|
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);
|
||||||
|
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,
|
valign: Gtk.Align.CENTER,
|
||||||
});
|
});
|
||||||
|
button.add_css_class('circular');
|
||||||
|
|
||||||
this.titleLabel = new Gtk.Label({
|
const action = (name === 'maximize')
|
||||||
halign: Gtk.Align.CENTER,
|
? 'window.toggle-maximized'
|
||||||
single_line_mode: true,
|
: 'window.' + name;
|
||||||
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',
|
button.connect('clicked',
|
||||||
GObject.BindingFlags.SYNC_CREATE
|
this._onWindowButtonActivate.bind(this, action)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subtitleLabel = new Gtk.Label({
|
return button;
|
||||||
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;
|
_onWindowButtonActivate(action)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFloatButtonClicked()
|
_onFloatButtonClicked()
|
||||||
|
@@ -91,7 +91,7 @@ class ClapperCustomRevealer extends Gtk.Revealer
|
|||||||
var RevealerTop = GObject.registerClass(
|
var RevealerTop = GObject.registerClass(
|
||||||
class ClapperRevealerTop extends CustomRevealer
|
class ClapperRevealerTop extends CustomRevealer
|
||||||
{
|
{
|
||||||
_init(window)
|
_init()
|
||||||
{
|
{
|
||||||
super._init({
|
super._init({
|
||||||
transition_duration: REVEAL_TIME,
|
transition_duration: REVEAL_TIME,
|
||||||
@@ -133,7 +133,7 @@ class ClapperRevealerTop extends CustomRevealer
|
|||||||
revealerBox.add_css_class('osd');
|
revealerBox.add_css_class('osd');
|
||||||
revealerBox.add_css_class('reavealertop');
|
revealerBox.add_css_class('reavealertop');
|
||||||
|
|
||||||
this.headerBar = new HeaderBar(window);
|
this.headerBar = new HeaderBar();
|
||||||
revealerBox.append(this.headerBar);
|
revealerBox.append(this.headerBar);
|
||||||
|
|
||||||
this.revealerGrid = new Gtk.Grid({
|
this.revealerGrid = new Gtk.Grid({
|
||||||
|
@@ -12,7 +12,7 @@ const { settings } = Misc;
|
|||||||
var Widget = GObject.registerClass(
|
var Widget = GObject.registerClass(
|
||||||
class ClapperWidget extends Gtk.Grid
|
class ClapperWidget extends Gtk.Grid
|
||||||
{
|
{
|
||||||
_init(window)
|
_init()
|
||||||
{
|
{
|
||||||
super._init();
|
super._init();
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
this.needsTracksUpdate = true;
|
this.needsTracksUpdate = true;
|
||||||
|
|
||||||
this.overlay = new Gtk.Overlay();
|
this.overlay = new Gtk.Overlay();
|
||||||
this.revealerTop = new Revealers.RevealerTop(window);
|
this.revealerTop = new Revealers.RevealerTop();
|
||||||
this.revealerBottom = new Revealers.RevealerBottom();
|
this.revealerBottom = new Revealers.RevealerBottom();
|
||||||
this.controls = new Controls();
|
this.controls = new Controls();
|
||||||
|
|
||||||
@@ -109,6 +109,7 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
if(this.fullscreenMode === isFullscreen)
|
if(this.fullscreenMode === isFullscreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
debug('changing fullscreen mode');
|
||||||
this.fullscreenMode = isFullscreen;
|
this.fullscreenMode = isFullscreen;
|
||||||
|
|
||||||
const root = this.get_root();
|
const root = this.get_root();
|
||||||
@@ -132,6 +133,8 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
this.player.playOnFullscreen = false;
|
this.player.playOnFullscreen = false;
|
||||||
this.player.play();
|
this.player.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug(`interface in fullscreen mode: ${isFullscreen}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveWindowSize(size)
|
_saveWindowSize(size)
|
||||||
@@ -160,8 +163,8 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
if(!mediaInfo)
|
if(!mediaInfo)
|
||||||
return GLib.SOURCE_REMOVE;
|
return GLib.SOURCE_REMOVE;
|
||||||
|
|
||||||
/* Set titlebar media title and path */
|
/* Set titlebar media title */
|
||||||
this.updateTitles(mediaInfo);
|
this.updateTitle(mediaInfo);
|
||||||
|
|
||||||
/* Show/hide position scale on LIVE */
|
/* Show/hide position scale on LIVE */
|
||||||
const isLive = mediaInfo.is_live();
|
const isLive = mediaInfo.is_live();
|
||||||
@@ -270,25 +273,18 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
return GLib.SOURCE_REMOVE;
|
return GLib.SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitles(mediaInfo)
|
updateTitle(mediaInfo)
|
||||||
{
|
{
|
||||||
let title = mediaInfo.get_title();
|
let title = mediaInfo.get_title();
|
||||||
let subtitle = this.player.playlistWidget.getActiveFilename();
|
|
||||||
|
|
||||||
if(!title) {
|
if(!title) {
|
||||||
|
const subtitle = this.player.playlistWidget.getActiveFilename();
|
||||||
|
|
||||||
title = (subtitle.includes('.'))
|
title = (subtitle.includes('.'))
|
||||||
? subtitle.split('.').slice(0, -1).join('.')
|
? subtitle.split('.').slice(0, -1).join('.')
|
||||||
: subtitle;
|
: subtitle;
|
||||||
|
|
||||||
subtitle = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = this.get_root();
|
|
||||||
const headerbar = root.get_titlebar();
|
|
||||||
|
|
||||||
if(headerbar && headerbar.updateHeaderBar)
|
|
||||||
headerbar.updateHeaderBar(title, subtitle);
|
|
||||||
|
|
||||||
this.revealerTop.setMediaTitle(title);
|
this.revealerTop.setMediaTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,15 +508,16 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
|
|
||||||
_onStateNotify(toplevel)
|
_onStateNotify(toplevel)
|
||||||
{
|
{
|
||||||
|
const isMaximized = Boolean(
|
||||||
|
toplevel.state & Gdk.ToplevelState.MAXIMIZED
|
||||||
|
);
|
||||||
const isFullscreen = Boolean(
|
const isFullscreen = Boolean(
|
||||||
toplevel.state & Gdk.ToplevelState.FULLSCREEN
|
toplevel.state & Gdk.ToplevelState.FULLSCREEN
|
||||||
);
|
);
|
||||||
|
const headerBar = this.revealerTop.headerBar;
|
||||||
|
|
||||||
if(this.fullscreenMode === isFullscreen)
|
headerBar.setMaximized(isMaximized);
|
||||||
return;
|
|
||||||
|
|
||||||
this.setFullscreenMode(isFullscreen);
|
this.setFullscreenMode(isFullscreen);
|
||||||
debug(`interface in fullscreen mode: ${isFullscreen}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onLayoutUpdate(surface, width, height)
|
_onLayoutUpdate(surface, width, height)
|
||||||
|
Reference in New Issue
Block a user