mirror of
				https://github.com/Rafostar/clapper.git
				synced 2025-10-31 10:25:45 +01:00 
			
		
		
		
	Major code cleanup
This commit is contained in:
		| @@ -1,17 +1,12 @@ | |||||||
| const { Gdk, Gio, GLib, GObject, Gtk, GstPlayer } = imports.gi; | const { Gio, GObject, Gtk } = imports.gi; | ||||||
| const Debug = imports.clapper_src.debug; |  | ||||||
| const { HeaderBar } = imports.clapper_src.headerbar; | const { HeaderBar } = imports.clapper_src.headerbar; | ||||||
| const { Interface } = imports.clapper_src.interface; | const { Widget } = imports.clapper_src.widget; | ||||||
| const { Player } = imports.clapper_src.player; |  | ||||||
| const Menu = imports.clapper_src.menu; | const Menu = imports.clapper_src.menu; | ||||||
| const { Window } = imports.clapper_src.window; |  | ||||||
|  |  | ||||||
| const APP_NAME = pkg.name.substring( | const APP_NAME = 'Clapper' || pkg.name.substring( | ||||||
|     pkg.name.lastIndexOf('.') + 1 |     pkg.name.lastIndexOf('.') + 1 | ||||||
| ); | ); | ||||||
|  |  | ||||||
| let { debug } = Debug; |  | ||||||
|  |  | ||||||
| var App = GObject.registerClass( | var App = GObject.registerClass( | ||||||
| class ClapperApp extends Gtk.Application | class ClapperApp extends Gtk.Application | ||||||
| { | { | ||||||
| @@ -25,41 +20,24 @@ class ClapperApp extends Gtk.Application | |||||||
|             playlist: [], |             playlist: [], | ||||||
|         }; |         }; | ||||||
|         Object.assign(this, defaults, opts); |         Object.assign(this, defaults, opts); | ||||||
|  |  | ||||||
|         this.cssProvider = new Gtk.CssProvider(); |  | ||||||
|         this.cssProvider.load_from_path( |  | ||||||
|             `${pkg.datadir}/${pkg.name}/css/styles.css` |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         this.window = null; |  | ||||||
|         this.interface = null; |  | ||||||
|         this.player = null; |  | ||||||
|         this.dragAllowed = false; |  | ||||||
|  |  | ||||||
|         this.posX = 0; |  | ||||||
|         this.posY = 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     vfunc_startup() |     vfunc_startup() | ||||||
|     { |     { | ||||||
|         super.vfunc_startup(); |         super.vfunc_startup(); | ||||||
|         this.window = new Window(this, APP_NAME); |  | ||||||
|  |  | ||||||
|         this.windowRealizeSignal = this.window.connect( |         let window = new Gtk.ApplicationWindow({ | ||||||
|             'realize', this._onWindowRealize.bind(this) |             application: this, | ||||||
|         ); |             title: APP_NAME, | ||||||
|         this.window.connect( |         }); | ||||||
|             'fullscreen-changed', this._onWindowFullscreenChanged.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.window.connect( |  | ||||||
|             'close-request', this._onWindowCloseRequest.bind(this) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         for(let action of Menu.actions) { |         for(let action of Menu.actions) { | ||||||
|             let simpleAction = new Gio.SimpleAction({ |             let simpleAction = new Gio.SimpleAction({ | ||||||
|                 name: action.name |                 name: action.name | ||||||
|             }); |             }); | ||||||
|             simpleAction.connect('activate', () => action(this.active_window, APP_NAME)); |             simpleAction.connect('activate', () => | ||||||
|  |                 action(this.active_window, APP_NAME) | ||||||
|  |             ); | ||||||
|             this.add_action(simpleAction); |             this.add_action(simpleAction); | ||||||
|         } |         } | ||||||
|         let uiBuilder = Gtk.Builder.new_from_file( |         let uiBuilder = Gtk.Builder.new_from_file( | ||||||
| @@ -68,17 +46,11 @@ class ClapperApp extends Gtk.Application | |||||||
|         let models = { |         let models = { | ||||||
|             settingsMenu: uiBuilder.get_object('settingsMenu') |             settingsMenu: uiBuilder.get_object('settingsMenu') | ||||||
|         }; |         }; | ||||||
|         let headerBar = new HeaderBar(this.window, models); |         let headerBar = new HeaderBar(window, models); | ||||||
|  |         window.set_titlebar(headerBar); | ||||||
|  |  | ||||||
|         this.interface = new Interface(); |         let clapperWidget = new Widget(); | ||||||
|         this.interface.addHeaderBar(headerBar, APP_NAME); |         window.set_child(clapperWidget); | ||||||
|  |  | ||||||
|         this.interface.controls.unfullscreenButton.connect( |  | ||||||
|             'clicked', () => this.active_window.unfullscreen() |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         this.window.set_titlebar(this.interface.headerBar); |  | ||||||
|         this.window.set_child(this.interface); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     vfunc_activate() |     vfunc_activate() | ||||||
| @@ -89,310 +61,21 @@ class ClapperApp extends Gtk.Application | |||||||
|             'show', this._onWindowShow.bind(this) |             'show', this._onWindowShow.bind(this) | ||||||
|         ); |         ); | ||||||
|         this.active_window.present(); |         this.active_window.present(); | ||||||
|  |  | ||||||
|         Gtk.StyleContext.add_provider_for_display( |  | ||||||
|             Gdk.Display.get_default(), |  | ||||||
|             this.cssProvider, |  | ||||||
|             Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     run(arr) |     run(arr) | ||||||
|     { |     { | ||||||
|         arr = arr || []; |         super.run(arr || []); | ||||||
|         super.run(arr); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     setHideCursorTimeout() |  | ||||||
|     { |  | ||||||
|         this.clearTimeout('hideCursor'); |  | ||||||
|         this.hideCursorTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { |  | ||||||
|             this.hideCursorTimeout = null; |  | ||||||
|  |  | ||||||
|             if(this.player.motionController.is_pointer) |  | ||||||
|                 this.player.widget.set_cursor(this.blankCursor); |  | ||||||
|  |  | ||||||
|             return GLib.SOURCE_REMOVE; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     setHideControlsTimeout() |  | ||||||
|     { |  | ||||||
|         this.clearTimeout('hideControls'); |  | ||||||
|         this.hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => { |  | ||||||
|             this.hideControlsTimeout = null; |  | ||||||
|  |  | ||||||
|             if(this.window.isFullscreen && this.player.motionController.is_pointer) { |  | ||||||
|                 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; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     clearTimeout(name) |  | ||||||
|     { |  | ||||||
|         if(!this[`${name}Timeout`]) |  | ||||||
|             return; |  | ||||||
|  |  | ||||||
|         GLib.source_remove(this[`${name}Timeout`]); |  | ||||||
|         this[`${name}Timeout`] = null; |  | ||||||
|  |  | ||||||
|         if(name === 'updateTime') |  | ||||||
|             debug('cleared update time interval'); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onWindowRealize() |  | ||||||
|     { |  | ||||||
|         this.window.disconnect(this.windowRealizeSignal); |  | ||||||
|  |  | ||||||
|         this.player = new Player(); |  | ||||||
|  |  | ||||||
|         if(!this.player.widget) |  | ||||||
|             return this.quit(); |  | ||||||
|  |  | ||||||
|         this.player.widget.width_request = 960; |  | ||||||
|         this.player.widget.height_request = 540; |  | ||||||
|  |  | ||||||
|         this.interface.addPlayer(this.player); |  | ||||||
|         this.player.connect('state-changed', this._onPlayerStateChanged.bind(this)); |  | ||||||
|  |  | ||||||
|         this.player.clickGesture.connect( |  | ||||||
|             'pressed', this._onPlayerPressed.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.player.keyController.connect( |  | ||||||
|             'key-pressed', this._onPlayerKeyPressed.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.player.motionController.connect( |  | ||||||
|             'enter', this._onPlayerEnter.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.player.motionController.connect( |  | ||||||
|             'leave', this._onPlayerLeave.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.player.motionController.connect( |  | ||||||
|             'motion', this._onPlayerMotion.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.player.dragGesture.connect( |  | ||||||
|             'drag-update', this._onPlayerDragUpdate.bind(this) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         /* Widget signals that are disconnected after first run */ |  | ||||||
|         this._playerRealizeSignal = this.player.widget.connect( |  | ||||||
|             'realize', this._onPlayerRealize.bind(this) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onWindowFullscreenChanged(window, isFullscreen) |  | ||||||
|     { |  | ||||||
|         if(isFullscreen) { |  | ||||||
|             this.setUpdateTimeInterval(); |  | ||||||
|             this.setHideControlsTimeout(); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             this.clearTimeout('updateTime'); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.interface.setFullscreenMode(isFullscreen); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerKeyPressed(self, keyval, keycode, state) |  | ||||||
|     { |  | ||||||
|         let bool = false; |  | ||||||
|  |  | ||||||
|         switch(keyval) { |  | ||||||
|             case Gdk.KEY_space: |  | ||||||
|             case Gdk.KEY_Return: |  | ||||||
|                 this.player.toggle_play(); |  | ||||||
|                 break; |  | ||||||
|             case Gdk.KEY_Right: |  | ||||||
|                 bool = true; |  | ||||||
|             case Gdk.KEY_Left: |  | ||||||
|                 // disabled due to missing "seek on drop" support |  | ||||||
|                 //this.interface.controls.handleScaleIncrement('position', bool); |  | ||||||
|                 break; |  | ||||||
|             case Gdk.KEY_Up: |  | ||||||
|                 bool = true; |  | ||||||
|             case Gdk.KEY_Down: |  | ||||||
|                 this.interface.controls.handleScaleIncrement('volume', bool); |  | ||||||
|                 break; |  | ||||||
|             case Gdk.KEY_F11: |  | ||||||
|                 this.window.toggleFullscreen(); |  | ||||||
|                 break; |  | ||||||
|             case Gdk.KEY_Escape: |  | ||||||
|                 if(this.window.isFullscreen) |  | ||||||
|                     this.window.unfullscreen(); |  | ||||||
|                 break; |  | ||||||
|             case Gdk.KEY_q: |  | ||||||
|             case Gdk.KEY_Q: |  | ||||||
|                 this._onWindowCloseRequest(); |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerRealize() |  | ||||||
|     { |  | ||||||
|         this.player.widget.disconnect(this._playerRealizeSignal); |  | ||||||
|         this.player.renderer.expose(); |  | ||||||
|  |  | ||||||
|         this.defaultCursor = Gdk.Cursor.new_from_name('default', null); |  | ||||||
|         this.blankCursor = Gdk.Cursor.new_from_name('none', null); |  | ||||||
|  |  | ||||||
|         this.setHideCursorTimeout(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onWindowShow(window) |     _onWindowShow(window) | ||||||
|     { |     { | ||||||
|          this.window.disconnect(this.windowShowSignal); |          window.disconnect(this.windowShowSignal); | ||||||
|          this.windowShowSignal = null; |          this.windowShowSignal = null; | ||||||
|  |  | ||||||
|          if(this.playlist.length) |          if(this.playlist.length) { | ||||||
|             this.player.set_playlist(this.playlist); |             let { player } = window.get_child(); | ||||||
|     } |             player.set_playlist(this.playlist); | ||||||
|  |          } | ||||||
|     _onPlayerStateChanged(self, state) |  | ||||||
|     { |  | ||||||
|         if(state === GstPlayer.PlayerState.BUFFERING) |  | ||||||
|             return; |  | ||||||
|  |  | ||||||
|         let isInhibited = false; |  | ||||||
|         let flags = Gtk.ApplicationInhibitFlags.SUSPEND |  | ||||||
|             | Gtk.ApplicationInhibitFlags.IDLE; |  | ||||||
|  |  | ||||||
|         if(state === GstPlayer.PlayerState.PLAYING) { |  | ||||||
|             if(this.inhibitCookie) |  | ||||||
|                 return; |  | ||||||
|  |  | ||||||
|             this.inhibitCookie = this.inhibit( |  | ||||||
|                 this.window, |  | ||||||
|                 flags, |  | ||||||
|                 'video is playing' |  | ||||||
|             ); |  | ||||||
|             if(!this.inhibitCookie) |  | ||||||
|                 debug(new Error('could not inhibit session!')); |  | ||||||
|  |  | ||||||
|             isInhibited = (this.inhibitCookie > 0); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             //if(!this.inhibitCookie) |  | ||||||
|                 return; |  | ||||||
|  |  | ||||||
|             /* Uninhibit seems to be broken as of GTK 3.99.2 |  | ||||||
|             this.uninhibit(this.inhibitCookie); |  | ||||||
|             this.inhibitCookie = null; |  | ||||||
|             */ |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         debug(`set prevent suspend to: ${isInhibited}`); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerPressed(gesture, nPress, x, y) |  | ||||||
|     { |  | ||||||
|         let button = gesture.get_current_button(); |  | ||||||
|         let isDouble = (nPress % 2 == 0); |  | ||||||
|         this.dragAllowed = !isDouble; |  | ||||||
|  |  | ||||||
|         switch(button) { |  | ||||||
|             case Gdk.BUTTON_PRIMARY: |  | ||||||
|                 if(isDouble) |  | ||||||
|                     this.window.toggleFullscreen(); |  | ||||||
|                 break; |  | ||||||
|             case Gdk.BUTTON_SECONDARY: |  | ||||||
|                 this.player.toggle_play(); |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerEnter(controller, x, y) |  | ||||||
|     { |  | ||||||
|         this.setHideCursorTimeout(); |  | ||||||
|         if(this.window.isFullscreen) |  | ||||||
|             this.setHideControlsTimeout(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerLeave(controller) |  | ||||||
|     { |  | ||||||
|         this.clearTimeout('hideCursor'); |  | ||||||
|         this.clearTimeout('hideControls'); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerMotion(controller, posX, posY) |  | ||||||
|     { |  | ||||||
|         /* GTK4 sometimes generates motions with same coords */ |  | ||||||
|         if(this.posX === posX && this.posY === posY) |  | ||||||
|             return; |  | ||||||
|  |  | ||||||
|         /* Do not show cursor on small movements */ |  | ||||||
|         if( |  | ||||||
|             Math.abs(this.posX - posX) >= 0.5 |  | ||||||
|             || Math.abs(this.posY - posY) >= 0.5 |  | ||||||
|         ) { |  | ||||||
|             this.player.widget.set_cursor(this.defaultCursor); |  | ||||||
|             this.setHideCursorTimeout(); |  | ||||||
|  |  | ||||||
|             if(this.window.isFullscreen) { |  | ||||||
|                 if(!this.interface.revealerTop.get_reveal_child()) { |  | ||||||
|                     this.setUpdateTimeInterval(); |  | ||||||
|                     this.interface.revealControls(true); |  | ||||||
|                 } |  | ||||||
|                 this.setHideControlsTimeout(); |  | ||||||
|             } |  | ||||||
|             else if(this.hideControlsTimeout) { |  | ||||||
|                 this.clearTimeout('hideControls'); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.posX = posX; |  | ||||||
|         this.posY = posY; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onPlayerDragUpdate(gesture, offsetX, offsetY) |  | ||||||
|     { |  | ||||||
|         if(!this.dragAllowed || this.active_window.isFullscreen) |  | ||||||
|             return; |  | ||||||
|  |  | ||||||
|         let { gtk_double_click_distance } = this.player.widget.get_settings(); |  | ||||||
|  |  | ||||||
|         if ( |  | ||||||
|             Math.abs(offsetX) > gtk_double_click_distance |  | ||||||
|             || Math.abs(offsetY) > gtk_double_click_distance |  | ||||||
|         ) { |  | ||||||
|             let [isActive, startX, startY] = gesture.get_start_point(); |  | ||||||
|             if(!isActive) return; |  | ||||||
|  |  | ||||||
|             this.active_window.get_surface().begin_move( |  | ||||||
|                 gesture.get_device(), |  | ||||||
|                 gesture.get_current_button(), |  | ||||||
|                 startX, |  | ||||||
|                 startY, |  | ||||||
|                 gesture.get_current_event_time() |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             gesture.reset(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onWindowCloseRequest() |  | ||||||
|     { |  | ||||||
|         this.interface.emit('destroy'); |  | ||||||
|         this.quit(); |  | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -103,17 +103,13 @@ class ClapperPopoverButton extends IconButton | |||||||
|             orientation: Gtk.Orientation.VERTICAL, |             orientation: Gtk.Orientation.VERTICAL, | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         this.popover.set_parent(this); |  | ||||||
|         this.popover.set_child(this.popoverBox); |         this.popover.set_child(this.popoverBox); | ||||||
|         this.popover.set_offset(0, -this.margin_top); |         this.popover.set_offset(0, -this.margin_top); | ||||||
|  |  | ||||||
|         if(this.isFullscreen) |         if(this.isFullscreen) | ||||||
|             this.popover.add_css_class('osd'); |             this.popover.add_css_class('osd'); | ||||||
|  |  | ||||||
|         this.changeStateSignal = this.popover.connect('closed', () => |         this.popover.connect('closed', this._onClosed.bind(this)); | ||||||
|             this.unset_state_flags(Gtk.StateFlags.CHECKED) |  | ||||||
|         ); |  | ||||||
|         this.destroySignal = this.connect('destroy', this._onDestroy.bind(this)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setFullscreenMode(isFullscreen) |     setFullscreenMode(isFullscreen) | ||||||
| @@ -136,16 +132,19 @@ class ClapperPopoverButton extends IconButton | |||||||
|     vfunc_clicked() |     vfunc_clicked() | ||||||
|     { |     { | ||||||
|         this.set_state_flags(Gtk.StateFlags.CHECKED, false); |         this.set_state_flags(Gtk.StateFlags.CHECKED, false); | ||||||
|  |  | ||||||
|  |         this.popover.set_parent(this); | ||||||
|         this.popover.popup(); |         this.popover.popup(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onDestroy() |     _onClosed() | ||||||
|     { |     { | ||||||
|         this.disconnect(this.destroySignal); |         let root = this.get_root(); | ||||||
|  |         let clapperWidget = root.get_child(); | ||||||
|  |  | ||||||
|  |         clapperWidget.player.widget.grab_focus(); | ||||||
|  |  | ||||||
|         this.popover.disconnect(this.changeStateSignal); |  | ||||||
|         this.popover.unparent(); |         this.popover.unparent(); | ||||||
|         this.popoverBox.emit('destroy'); |         this.unset_state_flags(Gtk.StateFlags.CHECKED); | ||||||
|         this.popover.emit('destroy'); |  | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
							
								
								
									
										243
									
								
								clapper_src/controls.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										243
									
								
								clapper_src/controls.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,25 +1,15 @@ | |||||||
| const { GObject, Gdk, Gtk } = imports.gi; | const { GObject, Gtk } = imports.gi; | ||||||
| const Buttons = imports.clapper_src.buttons; | const Buttons = imports.clapper_src.buttons; | ||||||
| const Debug = imports.clapper_src.debug; | const Debug = imports.clapper_src.debug; | ||||||
|  | const Misc = imports.clapper_src.misc; | ||||||
|  |  | ||||||
| const CONTROLS_MARGIN = 4; | const CONTROLS_MARGIN = 4; | ||||||
| const CONTROLS_SPACING = 4; | const CONTROLS_SPACING = 4; | ||||||
|  |  | ||||||
| let { debug } = Debug; | let { debug } = Debug; | ||||||
|  |  | ||||||
| var Controls = GObject.registerClass({ | var Controls = GObject.registerClass( | ||||||
|     Signals: { | class ClapperControls extends Gtk.Box | ||||||
|         'position-seeking-changed': { |  | ||||||
|             param_types: [GObject.TYPE_BOOLEAN] |  | ||||||
|         }, |  | ||||||
|         'track-change-requested': { |  | ||||||
|             param_types: [GObject.TYPE_STRING, GObject.TYPE_INT] |  | ||||||
|         }, |  | ||||||
|         'visualization-change-requested': { |  | ||||||
|             param_types: [GObject.TYPE_STRING] |  | ||||||
|         }, |  | ||||||
|     } |  | ||||||
| }, class ClapperControls extends Gtk.Box |  | ||||||
| { | { | ||||||
|     _init() |     _init() | ||||||
|     { |     { | ||||||
| @@ -29,8 +19,14 @@ var Controls = GObject.registerClass({ | |||||||
|             margin_end: CONTROLS_MARGIN, |             margin_end: CONTROLS_MARGIN, | ||||||
|             spacing: CONTROLS_SPACING, |             spacing: CONTROLS_SPACING, | ||||||
|             valign: Gtk.Align.END, |             valign: Gtk.Align.END, | ||||||
|  |             can_focus: false, | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         this.currentVolume = 0; | ||||||
|  |         this.currentPosition = 0; | ||||||
|  |         this.currentDuration = 0; | ||||||
|  |         this.isPositionSeeking = false; | ||||||
|  |  | ||||||
|         this.durationFormated = '00:00:00'; |         this.durationFormated = '00:00:00'; | ||||||
|         this.elapsedInitial = '00:00:00/00:00:00'; |         this.elapsedInitial = '00:00:00/00:00:00'; | ||||||
|         this.buttonsArr = []; |         this.buttonsArr = []; | ||||||
| @@ -57,10 +53,11 @@ var Controls = GObject.registerClass({ | |||||||
|         this.unfullscreenButton = this.addButton( |         this.unfullscreenButton = this.addButton( | ||||||
|             'view-restore-symbolic', |             'view-restore-symbolic', | ||||||
|         ); |         ); | ||||||
|  |         this.unfullscreenButton.connect('clicked', this._onUnfullscreenClicked.bind(this)); | ||||||
|         this.unfullscreenButton.set_visible(false); |         this.unfullscreenButton.set_visible(false); | ||||||
|  |  | ||||||
|         this.add_css_class('playercontrols'); |         this.add_css_class('playercontrols'); | ||||||
|         this.destroySignal = this.connect('destroy', this._onDestroy.bind(this)); |         this.realizeSignal = this.connect('realize', this._onRealize.bind(this)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setFullscreenMode(isFullscreen) |     setFullscreenMode(isFullscreen) | ||||||
| @@ -69,6 +66,7 @@ var Controls = GObject.registerClass({ | |||||||
|             button.setFullscreenMode(isFullscreen); |             button.setFullscreenMode(isFullscreen); | ||||||
|  |  | ||||||
|         this.unfullscreenButton.set_visible(isFullscreen); |         this.unfullscreenButton.set_visible(isFullscreen); | ||||||
|  |         this.set_can_focus(isFullscreen); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setLiveMode(isLive, isSeekable) |     setLiveMode(isLive, isSeekable) | ||||||
| @@ -79,6 +77,16 @@ var Controls = GObject.registerClass({ | |||||||
|         this.positionScale.visible = isSeekable; |         this.positionScale.visible = isSeekable; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     updateElapsedLabel(value) | ||||||
|  |     { | ||||||
|  |         value = value || 0; | ||||||
|  |  | ||||||
|  |         let elapsed = Misc.getFormatedTime(value) | ||||||
|  |             + '/' + this.durationFormated; | ||||||
|  |  | ||||||
|  |         this.elapsedButton.set_label(elapsed); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     addButton(buttonIcon) |     addButton(buttonIcon) | ||||||
|     { |     { | ||||||
|         let button = (buttonIcon instanceof Gtk.Button) |         let button = (buttonIcon instanceof Gtk.Button) | ||||||
| @@ -135,7 +143,7 @@ var Controls = GObject.registerClass({ | |||||||
|                 }); |                 }); | ||||||
|                 checkButton.connect( |                 checkButton.connect( | ||||||
|                     'toggled', |                     'toggled', | ||||||
|                     this._onCheckButtonToggled.bind(this, checkButton) |                     this._onCheckButtonToggled.bind(this) | ||||||
|                 ); |                 ); | ||||||
|                 box.append(checkButton); |                 box.append(checkButton); | ||||||
|             } |             } | ||||||
| @@ -160,23 +168,69 @@ var Controls = GObject.registerClass({ | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     handleScaleIncrement(type, isUp) |     _handleTrackChange(checkButton) | ||||||
|     { |     { | ||||||
|         if(type === 'volume' && !this.volumeButton.visible) |         let clapperWidget = this.get_ancestor(Gtk.Grid); | ||||||
|  |  | ||||||
|  |         /* Reenabling audio is slow (as expected), | ||||||
|  |          * so it is better to toggle mute instead */ | ||||||
|  |         if(checkButton.type === 'audio') { | ||||||
|  |             if(checkButton.activeId < 0) | ||||||
|  |                 return clapperWidget.player.set_mute(true); | ||||||
|  |  | ||||||
|  |             if(clapperWidget.player.get_mute()) | ||||||
|  |                 clapperWidget.player.set_mute(false); | ||||||
|  |  | ||||||
|  |             return clapperWidget.player[ | ||||||
|  |                 `set_${checkButton.type}_track` | ||||||
|  |             ](checkButton.activeId); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(checkButton.activeId < 0) { | ||||||
|  |             /* Disabling video leaves last frame frozen, | ||||||
|  |              * so we hide it by making it transparent */ | ||||||
|  |             if(checkButton.type === 'video') | ||||||
|  |                 clapperWidget.player.widget.set_opacity(0); | ||||||
|  |  | ||||||
|  |             return clapperWidget.player[ | ||||||
|  |                 `set_${checkButton.type}_track_enabled` | ||||||
|  |             ](false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let setTrack = `set_${checkButton.type}_track`; | ||||||
|  |  | ||||||
|  |         clapperWidget.player[setTrack](checkButton.activeId); | ||||||
|  |         clapperWidget.player[`${setTrack}_enabled`](true); | ||||||
|  |  | ||||||
|  |         if(checkButton.type === 'video' && !clapperWidget.player.widget.opacity) | ||||||
|  |             clapperWidget.player.widget.set_opacity(1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _handleVisualizationChange(checkButton) | ||||||
|  |     { | ||||||
|  |         let clapperWidget = this.get_ancestor(Gtk.Grid); | ||||||
|  |         let isEnabled = clapperWidget.player.get_visualization_enabled(); | ||||||
|  |  | ||||||
|  |         if(!checkButton.activeId) { | ||||||
|  |             if(isEnabled) { | ||||||
|  |                 clapperWidget.player.set_visualization_enabled(false); | ||||||
|  |                 debug('disabled visualizations'); | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let currVis = clapperWidget.player.get_current_visualization(); | ||||||
|  |  | ||||||
|  |         if(currVis === checkButton.activeId) | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         let value = this[`${type}Scale`].get_value(); |         debug(`set visualization: ${checkButton.activeId}`); | ||||||
|         let maxValue = this[`${type}Adjustment`].get_upper(); |         clapperWidget.player.set_visualization(checkButton.activeId); | ||||||
|         let increment = this[`${type}Adjustment`].get_page_increment(); |  | ||||||
|  |  | ||||||
|         value += (isUp) ? increment : -increment; |         if(!isEnabled) { | ||||||
|         value = (value < 0) |             clapperWidget.player.set_visualization_enabled(true); | ||||||
|             ? 0 |             debug('enabled visualizations'); | ||||||
|             : (value > maxValue) |         } | ||||||
|             ? maxValue |  | ||||||
|             : value; |  | ||||||
|  |  | ||||||
|         this[`${type}Scale`].set_value(value); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _addTogglePlayButton() |     _addTogglePlayButton() | ||||||
| @@ -186,6 +240,9 @@ var Controls = GObject.registerClass({ | |||||||
|             'media-playback-pause-symbolic' |             'media-playback-pause-symbolic' | ||||||
|         ); |         ); | ||||||
|         this.togglePlayButton.add_css_class('playbackicon'); |         this.togglePlayButton.add_css_class('playbackicon'); | ||||||
|  |         this.togglePlayButton.connect( | ||||||
|  |             'clicked', this._onTogglePlayClicked.bind(this) | ||||||
|  |         ); | ||||||
|         this.addButton(this.togglePlayButton); |         this.addButton(this.togglePlayButton); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -201,9 +258,10 @@ var Controls = GObject.registerClass({ | |||||||
|             can_focus: false, |             can_focus: false, | ||||||
|             visible: false, |             visible: false, | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         this.positionScale.add_css_class('positionscale'); |         this.positionScale.add_css_class('positionscale'); | ||||||
|         this.positionScale.connect('value-changed', this._onPositionScaleValueChanged.bind(this)); |         this.positionScale.connect( | ||||||
|  |             'value-changed', this._onPositionScaleValueChanged.bind(this) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|         /* GTK4 is missing pressed/released signals for GtkRange/GtkScale. |         /* GTK4 is missing pressed/released signals for GtkRange/GtkScale. | ||||||
|          * We cannot add controllers, cause it already has them, so we |          * We cannot add controllers, cause it already has them, so we | ||||||
| @@ -229,14 +287,6 @@ var Controls = GObject.registerClass({ | |||||||
|         this.volumeButton = this.addPopoverButton( |         this.volumeButton = this.addPopoverButton( | ||||||
|             'audio-volume-muted-symbolic' |             'audio-volume-muted-symbolic' | ||||||
|         ); |         ); | ||||||
|         let scrollController = new Gtk.EventControllerScroll(); |  | ||||||
|         scrollController.set_flags( |  | ||||||
|             Gtk.EventControllerScrollFlags.VERTICAL |  | ||||||
|             | Gtk.EventControllerScrollFlags.DISCRETE |  | ||||||
|         ); |  | ||||||
|         scrollController.connect('scroll', this._onScroll.bind(this)); |  | ||||||
|         this.volumeButton.add_controller(scrollController); |  | ||||||
|  |  | ||||||
|         this.volumeScale = new Gtk.Scale({ |         this.volumeScale = new Gtk.Scale({ | ||||||
|             orientation: Gtk.Orientation.VERTICAL, |             orientation: Gtk.Orientation.VERTICAL, | ||||||
|             inverted: true, |             inverted: true, | ||||||
| @@ -245,6 +295,9 @@ var Controls = GObject.registerClass({ | |||||||
|             round_digits: 2, |             round_digits: 2, | ||||||
|             vexpand: true, |             vexpand: true, | ||||||
|         }); |         }); | ||||||
|  |         this.volumeScale.connect( | ||||||
|  |             'value-changed', this._onVolumeScaleValueChanged.bind(this) | ||||||
|  |         ); | ||||||
|         this.volumeScale.add_css_class('volumescale'); |         this.volumeScale.add_css_class('volumescale'); | ||||||
|         this.volumeAdjustment = this.volumeScale.get_adjustment(); |         this.volumeAdjustment = this.volumeScale.get_adjustment(); | ||||||
|  |  | ||||||
| @@ -264,18 +317,28 @@ var Controls = GObject.registerClass({ | |||||||
|         this.volumeButton.popoverBox.append(this.volumeScale); |         this.volumeButton.popoverBox.append(this.volumeScale); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _getFormatedTime(time) |     _onRealize() | ||||||
|     { |     { | ||||||
|         let hours = ('0' + Math.floor(time / 3600)).slice(-2); |         this.disconnect(this.realizeSignal); | ||||||
| 	time -= hours * 3600; |         this.realizeSignal = null; | ||||||
| 	let minutes = ('0' + Math.floor(time / 60)).slice(-2); |  | ||||||
| 	time -= minutes * 60; |  | ||||||
| 	let seconds = ('0' + Math.floor(time)).slice(-2); |  | ||||||
|  |  | ||||||
|         return `${hours}:${minutes}:${seconds}`; |         let { player } = this.get_ancestor(Gtk.Grid); | ||||||
|  |         let scrollController = new Gtk.EventControllerScroll(); | ||||||
|  |         scrollController.set_flags( | ||||||
|  |             Gtk.EventControllerScrollFlags.VERTICAL | ||||||
|  |             | Gtk.EventControllerScrollFlags.DISCRETE | ||||||
|  |         ); | ||||||
|  |         scrollController.connect('scroll', player._onScroll.bind(player)); | ||||||
|  |         this.volumeButton.add_controller(scrollController); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onCheckButtonToggled(self, checkButton) |     _onUnfullscreenClicked(button) | ||||||
|  |     { | ||||||
|  |         let root = button.get_root(); | ||||||
|  |         root.unfullscreen(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onCheckButtonToggled(checkButton) | ||||||
|     { |     { | ||||||
|         if(!checkButton.get_active()) |         if(!checkButton.get_active()) | ||||||
|             return; |             return; | ||||||
| @@ -284,61 +347,73 @@ var Controls = GObject.registerClass({ | |||||||
|             case 'video': |             case 'video': | ||||||
|             case 'audio': |             case 'audio': | ||||||
|             case 'subtitle': |             case 'subtitle': | ||||||
|                 this.emit( |                 this._handleTrackChange(checkButton); | ||||||
|                     'track-change-requested', |  | ||||||
|                     checkButton.type, |  | ||||||
|                     checkButton.activeId |  | ||||||
|                 ); |  | ||||||
|                 break; |                 break; | ||||||
|             case 'visualization': |             case 'visualization': | ||||||
|                 this.emit( |                 this._handleVisualizationChange(checkButton); | ||||||
|                     `${checkButton.type}-change-requested`, |  | ||||||
|                     checkButton.activeId |  | ||||||
|                 ); |  | ||||||
|                 break; |                 break; | ||||||
|             default: |             default: | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onPositionScaleValueChanged() |     _onTogglePlayClicked() | ||||||
|     { |     { | ||||||
|         let elapsed = this._getFormatedTime(this.positionScale.get_value()) |         /* Parent of controls changes, so get ancestor instead */ | ||||||
|             + '/' + this.durationFormated; |         let { player } = this.get_ancestor(Gtk.Grid); | ||||||
|  |         player.toggle_play(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         this.elapsedButton.set_label(elapsed); |     _onPositionScaleValueChanged(scale) | ||||||
|  |     { | ||||||
|  |         let value = Math.round(scale.get_value()); | ||||||
|  |         this.updateElapsedLabel(value); | ||||||
|  |  | ||||||
|  |         if(this.currentPosition === value || this.isPositionSeeking) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         let { player } = this.get_ancestor(Gtk.Grid); | ||||||
|  |         player.seek_seconds(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onVolumeScaleValueChanged(scale) | ||||||
|  |     { | ||||||
|  |         let volume = Number(scale.get_value().toFixed(2)); | ||||||
|  |         let icon = (volume <= 0) | ||||||
|  |             ? 'muted' | ||||||
|  |             : (volume <= 0.33) | ||||||
|  |             ? 'low' | ||||||
|  |             : (volume <= 0.66) | ||||||
|  |             ? 'medium' | ||||||
|  |             : (volume <= 1) | ||||||
|  |             ? 'high' | ||||||
|  |             : 'overamplified'; | ||||||
|  |  | ||||||
|  |         let iconName = `audio-volume-${icon}-symbolic`; | ||||||
|  |         if(this.volumeButton.icon_name !== iconName) | ||||||
|  |         { | ||||||
|  |             debug(`set volume icon: ${icon}`); | ||||||
|  |             this.volumeButton.set_icon_name(iconName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(this.currentVolume === volume) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         let { player } = this.get_ancestor(Gtk.Grid); | ||||||
|  |         player.set_volume(volume); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onPositionScaleDragging(scale) |     _onPositionScaleDragging(scale) | ||||||
|     { |     { | ||||||
|         let isPositionSeeking = scale.has_css_class('dragging'); |         let isPositionSeeking = scale.has_css_class('dragging'); | ||||||
|  |  | ||||||
|         if(this.isPositionSeeking === isPositionSeeking) |         if((this.isPositionSeeking = isPositionSeeking)) | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         this.isPositionSeeking = isPositionSeeking; |         let clapperWidget = this.get_ancestor(Gtk.Grid); | ||||||
|         this.emit('position-seeking-changed', this.isPositionSeeking); |         if(!clapperWidget) return; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onScroll(controller, dx, dy) |         let positionSeconds = Math.round(scale.get_value()); | ||||||
|     { |         clapperWidget.player.seek_seconds(positionSeconds); | ||||||
|         let isVertical = Math.abs(dy) >= Math.abs(dx); |  | ||||||
|         let isIncrease = (isVertical) ? dy < 0 : dx < 0; |  | ||||||
|         let type = (isVertical) ? 'volume' : 'position'; |  | ||||||
|  |  | ||||||
|         this.handleScaleIncrement(type, isIncrease); |  | ||||||
|  |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onDestroy() |  | ||||||
|     { |  | ||||||
|         this.disconnect(this.destroySignal); |  | ||||||
|  |  | ||||||
|         this.visualizationsButton.emit('destroy'); |  | ||||||
|         this.videoTracksButton.emit('destroy'); |  | ||||||
|         this.audioTracksButton.emit('destroy'); |  | ||||||
|         this.subtitleTracksButton.emit('destroy'); |  | ||||||
|         this.volumeButton.emit('destroy'); |  | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| const { GLib, GObject, Gtk, Pango } = imports.gi; | const { GLib, GObject, Gst, Gtk, Pango } = imports.gi; | ||||||
|  |  | ||||||
| var HeaderBar = GObject.registerClass( | var HeaderBar = GObject.registerClass( | ||||||
| class ClapperHeaderBar extends Gtk.HeaderBar | class ClapperHeaderBar extends Gtk.HeaderBar | ||||||
| @@ -14,7 +14,8 @@ class ClapperHeaderBar extends Gtk.HeaderBar | |||||||
|         let openMenuButton = new Gtk.MenuButton({ |         let openMenuButton = new Gtk.MenuButton({ | ||||||
|             icon_name: 'open-menu-symbolic' |             icon_name: 'open-menu-symbolic' | ||||||
|         }); |         }); | ||||||
|         openMenuButton.set_menu_model(models.settingsMenu); |         let settingsPopover = new HeaderBarPopover(models.settingsMenu); | ||||||
|  |         openMenuButton.set_popover(settingsPopover); | ||||||
|         this.pack_end(openMenuButton); |         this.pack_end(openMenuButton); | ||||||
|  |  | ||||||
|         let fullscreenButton = new Gtk.Button({ |         let fullscreenButton = new Gtk.Button({ | ||||||
| @@ -27,9 +28,9 @@ class ClapperHeaderBar extends Gtk.HeaderBar | |||||||
|     updateHeaderBar(mediaInfo) |     updateHeaderBar(mediaInfo) | ||||||
|     { |     { | ||||||
|         let title = mediaInfo.get_title(); |         let title = mediaInfo.get_title(); | ||||||
|         let subtitle = mediaInfo.get_uri() || null; |         let subtitle = mediaInfo.get_uri(); | ||||||
|  |  | ||||||
|         if(subtitle && subtitle.startsWith('file://')) { |         if(Gst.Uri.get_protocol(subtitle) === 'file') { | ||||||
|             subtitle = GLib.path_get_basename( |             subtitle = GLib.path_get_basename( | ||||||
|                 GLib.filename_from_uri(subtitle)[0] |                 GLib.filename_from_uri(subtitle)[0] | ||||||
|             ); |             ); | ||||||
| @@ -84,3 +85,24 @@ class ClapperHeaderBar extends Gtk.HeaderBar | |||||||
|         return box; |         return box; | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | var HeaderBarPopover = GObject.registerClass( | ||||||
|  | class ClapperHeaderBarPopover extends Gtk.PopoverMenu | ||||||
|  | { | ||||||
|  |     _init(model) | ||||||
|  |     { | ||||||
|  |         super._init({ | ||||||
|  |             menu_model: model, | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         this.connect('closed', this._onClosed.bind(this)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onClosed() | ||||||
|  |     { | ||||||
|  |         let root = this.get_root(); | ||||||
|  |         let clapperWidget = root.get_child(); | ||||||
|  |  | ||||||
|  |         clapperWidget.player.widget.grab_focus(); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								clapper_src/misc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								clapper_src/misc.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | const { GstPlayer, Gtk } = imports.gi; | ||||||
|  | const Debug = imports.clapper_src.debug; | ||||||
|  |  | ||||||
|  | let { debug } = Debug; | ||||||
|  | let inhibitCookie; | ||||||
|  |  | ||||||
|  | function inhibitForState(state, window) | ||||||
|  | { | ||||||
|  |     let isInhibited = false; | ||||||
|  |     let flags = Gtk.ApplicationInhibitFlags.SUSPEND | ||||||
|  |         | Gtk.ApplicationInhibitFlags.IDLE; | ||||||
|  |  | ||||||
|  |     if(state === GstPlayer.PlayerState.PLAYING) { | ||||||
|  |         if(inhibitCookie) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         let app = window.get_application(); | ||||||
|  |  | ||||||
|  |         inhibitCookie = app.inhibit( | ||||||
|  |             window, | ||||||
|  |             flags, | ||||||
|  |             'video is playing' | ||||||
|  |         ); | ||||||
|  |         if(!inhibitCookie) | ||||||
|  |             debug(new Error('could not inhibit session!')); | ||||||
|  |  | ||||||
|  |         isInhibited = (inhibitCookie > 0); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         //if(!inhibitCookie) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         /* Uninhibit seems to be broken as of GTK 3.99.2 | ||||||
|  |         this.uninhibit(inhibitCookie); | ||||||
|  |         inhibitCookie = null; | ||||||
|  |         */ | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     debug(`set prevent suspend to: ${isInhibited}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getFormatedTime(time) | ||||||
|  | { | ||||||
|  |     let hours = ('0' + Math.floor(time / 3600)).slice(-2); | ||||||
|  |     time -= hours * 3600; | ||||||
|  |     let minutes = ('0' + Math.floor(time / 60)).slice(-2); | ||||||
|  |     time -= minutes * 60; | ||||||
|  |     let seconds = ('0' + Math.floor(time)).slice(-2); | ||||||
|  |  | ||||||
|  |     return `${hours}:${minutes}:${seconds}`; | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| const { Gio, GObject, Gst, GstPlayer, Gtk } = imports.gi; | const { Gdk, Gio, GLib, GObject, Gst, GstPlayer, Gtk } = imports.gi; | ||||||
| const ByteArray = imports.byteArray; | const ByteArray = imports.byteArray; | ||||||
| const Debug = imports.clapper_src.debug; | const Debug = imports.clapper_src.debug; | ||||||
|  |  | ||||||
| @@ -44,15 +44,13 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|             video_renderer: renderer |             video_renderer: renderer | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         /* Assign elements to player for later access */ |  | ||||||
|         this.gtkglsink = gtkglsink; |  | ||||||
|         this.glsinkbin = glsinkbin; |  | ||||||
|         this.dispatcher = dispatcher; |  | ||||||
|         this.renderer = renderer; |  | ||||||
|  |  | ||||||
|         this.gstRegistry = Gst.Registry.get(); |  | ||||||
|         this.is_local_file = false; |         this.is_local_file = false; | ||||||
|         this.seek_done = false; |         this.seek_done = true; | ||||||
|  |         this.dragAllowed = false; | ||||||
|  |  | ||||||
|  |         this.posX = 0; | ||||||
|  |         this.posY = 0; | ||||||
|  |         this.keyPressCount = 0; | ||||||
|  |  | ||||||
|         this._playerSignals = []; |         this._playerSignals = []; | ||||||
|         this._widgetSignals = []; |         this._widgetSignals = []; | ||||||
| @@ -73,37 +71,54 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|         this.set_plugin_rank('vah264dec', 300); |         this.set_plugin_rank('vah264dec', 300); | ||||||
|  |  | ||||||
|         this.widget = gtkglsink.widget; |         this.widget = gtkglsink.widget; | ||||||
|  |         this.widget.vexpand = true; | ||||||
|  |         this.widget.hexpand = true; | ||||||
|  |  | ||||||
|         this.state = GstPlayer.PlayerState.STOPPED; |         this.state = GstPlayer.PlayerState.STOPPED; | ||||||
|         this.visualization_enabled = false; |         this.visualization_enabled = false; | ||||||
|         this.fast_seeking = opts.fast_seeking || false; |         this.fast_seeking = opts.fast_seeking || false; | ||||||
|  |  | ||||||
|         this._playlist = []; |         this._playlist = []; | ||||||
|         this._trackId = 0; |         this._trackId = 0; | ||||||
|         this.playlist_ext = opts.playlist_ext || 'claps'; |  | ||||||
|  |  | ||||||
|         this.keyController = new Gtk.EventControllerKey(); |         this._hideCursorTimeout = null; | ||||||
|         this.motionController = new Gtk.EventControllerMotion(); |         this._hideControlsTimeout = null; | ||||||
|         this.scrollController = new Gtk.EventControllerScroll(); |         this._updateTimeTimeout = null; | ||||||
|         this.clickGesture = new Gtk.GestureClick(); |  | ||||||
|         this.dragGesture = new Gtk.GestureDrag(); |  | ||||||
|  |  | ||||||
|         this.scrollController.set_flags( |         this.cursorInPlayer = false; | ||||||
|             Gtk.EventControllerScrollFlags.BOTH_AXES |  | ||||||
|         ); |  | ||||||
|         this.clickGesture.set_button(0); |  | ||||||
|  |  | ||||||
|         this.widget.add_controller(this.keyController); |         let clickGesture = new Gtk.GestureClick(); | ||||||
|         this.widget.add_controller(this.motionController); |         clickGesture.set_button(0); | ||||||
|         this.widget.add_controller(this.scrollController); |         clickGesture.connect('pressed', this._onWidgetPressed.bind(this)); | ||||||
|         this.widget.add_controller(this.clickGesture); |         this.widget.add_controller(clickGesture); | ||||||
|         this.widget.add_controller(this.dragGesture); |  | ||||||
|  |  | ||||||
|         this.connect('state-changed', this._onStateChanged.bind(this)); |         let dragGesture = new Gtk.GestureDrag(); | ||||||
|         this.connect('uri-loaded', this._onUriLoaded.bind(this)); |         dragGesture.connect('drag-update', this._onWidgetDragUpdate.bind(this)); | ||||||
|         this.connect('end-of-stream', this._onStreamEnded.bind(this)); |         this.widget.add_controller(dragGesture); | ||||||
|         this.connect('warning', this._onPlayerWarning.bind(this)); |  | ||||||
|         this.connect('error', this._onPlayerError.bind(this)); |         let keyController = new Gtk.EventControllerKey(); | ||||||
|         this.connectWidget('destroy', this._onWidgetDestroy.bind(this)); |         keyController.connect('key-pressed', this._onWidgetKeyPressed.bind(this)); | ||||||
|  |         keyController.connect('key-released', this._onWidgetKeyReleased.bind(this)); | ||||||
|  |         this.widget.add_controller(keyController); | ||||||
|  |  | ||||||
|  |         let scrollController = new Gtk.EventControllerScroll(); | ||||||
|  |         scrollController.set_flags(Gtk.EventControllerScrollFlags.BOTH_AXES); | ||||||
|  |         scrollController.connect('scroll', this._onScroll.bind(this)); | ||||||
|  |         this.widget.add_controller(scrollController); | ||||||
|  |  | ||||||
|  |         let motionController = new Gtk.EventControllerMotion(); | ||||||
|  |         motionController.connect('enter', this._onWidgetEnter.bind(this)); | ||||||
|  |         motionController.connect('leave', this._onWidgetLeave.bind(this)); | ||||||
|  |         motionController.connect('motion', this._onWidgetMotion.bind(this)); | ||||||
|  |         this.widget.add_controller(motionController); | ||||||
|  |  | ||||||
|  |         this.selfConnect('state-changed', this._onStateChanged.bind(this)); | ||||||
|  |         this.selfConnect('uri-loaded', this._onUriLoaded.bind(this)); | ||||||
|  |         this.selfConnect('end-of-stream', this._onStreamEnded.bind(this)); | ||||||
|  |         this.selfConnect('warning', this._onPlayerWarning.bind(this)); | ||||||
|  |         this.selfConnect('error', this._onPlayerError.bind(this)); | ||||||
|  |  | ||||||
|  |         this._realizeSignal = this.widget.connect('realize', this._onWidgetRealize.bind(this)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     set_media(source) |     set_media(source) | ||||||
| @@ -133,7 +148,7 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|             return this.set_media(this._playlist[this._trackId]); |             return this.set_media(this._playlist[this._trackId]); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if(file.get_path().endsWith(`.${this.playlist_ext}`)) |         if(file.get_path().endsWith('.claps')) | ||||||
|             return this.load_playlist_file(file); |             return this.load_playlist_file(file); | ||||||
|  |  | ||||||
|         this.is_local_file = true; |         this.is_local_file = true; | ||||||
| @@ -200,6 +215,12 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|  |  | ||||||
|     seek(position) |     seek(position) | ||||||
|     { |     { | ||||||
|  |         if(this.state === GstPlayer.PlayerState.STOPPED) | ||||||
|  |             this.pause(); | ||||||
|  |  | ||||||
|  |         if(position < 0) | ||||||
|  |             position = 0; | ||||||
|  |  | ||||||
|         this.seek_done = false; |         this.seek_done = false; | ||||||
|         debug(`player is seeking to position: ${position}`); |         debug(`player is seeking to position: ${position}`); | ||||||
|  |  | ||||||
| @@ -219,6 +240,34 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|         this.seek(position * 1000000000); |         this.seek(position * 1000000000); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     set_volume(volume) | ||||||
|  |     { | ||||||
|  |         if(volume < 0) | ||||||
|  |             volume = 0; | ||||||
|  |         else if(volume > 2) | ||||||
|  |             volume = 2; | ||||||
|  |  | ||||||
|  |         super.set_volume(volume); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     adjust_position(isIncrease) | ||||||
|  |     { | ||||||
|  |         let { controls } = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |  | ||||||
|  |         let value = (isIncrease) ? 10 : -10; | ||||||
|  |         let positionSeconds = controls.positionScale.get_value() + value; | ||||||
|  |         controls.positionScale.set_value(positionSeconds); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     adjust_volume(isIncrease) | ||||||
|  |     { | ||||||
|  |         let { controls } = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |  | ||||||
|  |         let value = (isIncrease) ? 0.05 : -0.05; | ||||||
|  |         let volume = controls.volumeScale.get_value() + value; | ||||||
|  |         controls.volumeScale.set_value(volume); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     toggle_play() |     toggle_play() | ||||||
|     { |     { | ||||||
|         let action = (this.state === GstPlayer.PlayerState.PLAYING) |         let action = (this.state === GstPlayer.PlayerState.PLAYING) | ||||||
| @@ -238,7 +287,8 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|     { |     { | ||||||
|         debug(`changing rank of plugin: ${name}`); |         debug(`changing rank of plugin: ${name}`); | ||||||
|  |  | ||||||
|         let feature = this.gstRegistry.lookup_feature(name); |         let gstRegistry = Gst.Registry.get(); | ||||||
|  |         let feature = gstRegistry.lookup_feature(name); | ||||||
|         if(!feature) |         if(!feature) | ||||||
|             return debug(`plugin unavailable: ${name}`); |             return debug(`plugin unavailable: ${name}`); | ||||||
|  |  | ||||||
| @@ -248,20 +298,72 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|         debug(`changed rank: ${oldRank} -> ${rank} for ${name}`); |         debug(`changed rank: ${oldRank} -> ${rank} for ${name}`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     connect(signal, fn) |     selfConnect(signal, fn) | ||||||
|     { |     { | ||||||
|         let connection = super.connect(signal, fn); |         this._playerSignals.push( | ||||||
|         this._playerSignals.push(connection); |             super.connect(signal, fn) | ||||||
|  |         ); | ||||||
|         return connection; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     connectWidget(signal, fn) |     _setHideCursorTimeout() | ||||||
|     { |     { | ||||||
|         let connection = this.widget.connect(signal, fn); |         this._clearTimeout('hideCursor'); | ||||||
|         this._widgetSignals.push(connection); |         this._hideCursorTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { | ||||||
|  |             this._hideCursorTimeout = null; | ||||||
|  |  | ||||||
|         return connection; |             if(this.cursorInPlayer) { | ||||||
|  |                 let blankCursor = Gdk.Cursor.new_from_name('none', null); | ||||||
|  |                 this.widget.set_cursor(blankCursor); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return GLib.SOURCE_REMOVE; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _setHideControlsTimeout() | ||||||
|  |     { | ||||||
|  |         this._clearTimeout('hideControls'); | ||||||
|  |         this._hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => { | ||||||
|  |             this._hideControlsTimeout = null; | ||||||
|  |  | ||||||
|  |             if(this.cursorInPlayer) { | ||||||
|  |                 let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |                 if(clapperWidget.fullscreenMode) { | ||||||
|  |                     this._clearTimeout('updateTime'); | ||||||
|  |                     clapperWidget.revealControls(false); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return GLib.SOURCE_REMOVE; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _setUpdateTimeInterval() | ||||||
|  |     { | ||||||
|  |         this._clearTimeout('updateTime'); | ||||||
|  |         let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |         let nextUpdate = clapperWidget.updateTime(); | ||||||
|  |  | ||||||
|  |         this._updateTimeTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, nextUpdate, () => { | ||||||
|  |             this._updateTimeTimeout = null; | ||||||
|  |  | ||||||
|  |             if(clapperWidget.fullscreenMode) | ||||||
|  |                 this._setUpdateTimeInterval(); | ||||||
|  |  | ||||||
|  |             return GLib.SOURCE_REMOVE; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _clearTimeout(name) | ||||||
|  |     { | ||||||
|  |         if(!this[`_${name}Timeout`]) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         GLib.source_remove(this[`_${name}Timeout`]); | ||||||
|  |         this[`_${name}Timeout`] = null; | ||||||
|  |  | ||||||
|  |         if(name === 'updateTime') | ||||||
|  |             debug('cleared update time interval'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onStateChanged(player, state) |     _onStateChanged(player, state) | ||||||
| @@ -272,6 +374,11 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|             this.seek_done = true; |             this.seek_done = true; | ||||||
|             debug('seeking finished'); |             debug('seeking finished'); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |         if(!clapperWidget) return; | ||||||
|  |  | ||||||
|  |         clapperWidget._onPlayerStateChanged(player, state); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onStreamEnded(player) |     _onStreamEnded(player) | ||||||
| @@ -300,12 +407,227 @@ class ClapperPlayer extends GstPlayer.Player | |||||||
|         debug(error); |         debug(error); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     _onWidgetDestroy() |     _onWidgetRealize() | ||||||
|  |     { | ||||||
|  |         this.widget.disconnect(this._realizeSignal); | ||||||
|  |         this._realizeSignal = null; | ||||||
|  |  | ||||||
|  |         let root = this.widget.get_root(); | ||||||
|  |         if(!root) return; | ||||||
|  |  | ||||||
|  |         root.connect('close-request', this._onCloseRequest.bind(this)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetKeyPressed(controller, keyval, keycode, state) | ||||||
|  |     { | ||||||
|  |         this.keyPressCount++; | ||||||
|  |  | ||||||
|  |         let bool = false; | ||||||
|  |         let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |  | ||||||
|  |         switch(keyval) { | ||||||
|  |             case Gdk.KEY_Up: | ||||||
|  |                 bool = true; | ||||||
|  |             case Gdk.KEY_Down: | ||||||
|  |                 this.adjust_volume(bool); | ||||||
|  |                 break; | ||||||
|  |             case Gdk.KEY_Right: | ||||||
|  |                 bool = true; | ||||||
|  |             case Gdk.KEY_Left: | ||||||
|  |                 clapperWidget.controls.isPositionSeeking = true; | ||||||
|  |                 this._clearTimeout('hideControls'); | ||||||
|  |                 if(this.keyPressCount > 1) { | ||||||
|  |                     clapperWidget.revealerBottom.set_can_focus(false); | ||||||
|  |                     clapperWidget.revealerBottom.revealChild(true); | ||||||
|  |                 } | ||||||
|  |                 this.adjust_position(bool); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetKeyReleased(controller, keyval, keycode, state) | ||||||
|  |     { | ||||||
|  |         this.keyPressCount = 0; | ||||||
|  |  | ||||||
|  |         let value; | ||||||
|  |         let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |  | ||||||
|  |         switch(keyval) { | ||||||
|  |             case Gdk.KEY_space: | ||||||
|  |                 this.toggle_play(); | ||||||
|  |                 break; | ||||||
|  |             case Gdk.KEY_Return: | ||||||
|  |                 if(clapperWidget.fullscreenMode) { | ||||||
|  |                     clapperWidget.revealControls(true); | ||||||
|  |                     this._setHideControlsTimeout(); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case Gdk.KEY_Right: | ||||||
|  |             case Gdk.KEY_Left: | ||||||
|  |                 value = clapperWidget.controls.positionScale.get_value(); | ||||||
|  |                 this.seek_seconds(value); | ||||||
|  |                 this._setHideControlsTimeout(); | ||||||
|  |                 clapperWidget.controls.isPositionSeeking = false; | ||||||
|  |                 break; | ||||||
|  |             case Gdk.KEY_F11: | ||||||
|  |                 clapperWidget.toggleFullscreen(); | ||||||
|  |                 break; | ||||||
|  |             case Gdk.KEY_Escape: | ||||||
|  |                 if(clapperWidget.fullscreenMode) { | ||||||
|  |                     let root = this.widget.get_root(); | ||||||
|  |                     root.unfullscreen(); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case Gdk.KEY_q: | ||||||
|  |             case Gdk.KEY_Q: | ||||||
|  |                 let root = this.widget.get_root(); | ||||||
|  |                 root.emit('close-request'); | ||||||
|  |                 root.destroy(); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetPressed(gesture, nPress, x, y) | ||||||
|  |     { | ||||||
|  |         let button = gesture.get_current_button(); | ||||||
|  |         let isDouble = (nPress % 2 == 0); | ||||||
|  |         this.dragAllowed = !isDouble; | ||||||
|  |  | ||||||
|  |         switch(button) { | ||||||
|  |             case Gdk.BUTTON_PRIMARY: | ||||||
|  |                 if(isDouble) { | ||||||
|  |                     let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |                     clapperWidget.toggleFullscreen(); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case Gdk.BUTTON_SECONDARY: | ||||||
|  |                 this.toggle_play(); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetEnter(controller, x, y) | ||||||
|  |     { | ||||||
|  |         this.cursorInPlayer = true; | ||||||
|  |  | ||||||
|  |         this._setHideCursorTimeout(); | ||||||
|  |  | ||||||
|  |         let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |         if(clapperWidget.fullscreenMode) | ||||||
|  |             this._setHideControlsTimeout(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetLeave(controller) | ||||||
|  |     { | ||||||
|  |         this.cursorInPlayer = false; | ||||||
|  |  | ||||||
|  |         this._clearTimeout('hideCursor'); | ||||||
|  |         this._clearTimeout('hideControls'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetMotion(controller, posX, posY) | ||||||
|  |     { | ||||||
|  |         this.cursorInPlayer = true; | ||||||
|  |  | ||||||
|  |         /* GTK4 sometimes generates motions with same coords */ | ||||||
|  |         if(this.posX === posX && this.posY === posY) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         /* Do not show cursor on small movements */ | ||||||
|  |         if( | ||||||
|  |             Math.abs(this.posX - posX) >= 0.5 | ||||||
|  |             || Math.abs(this.posY - posY) >= 0.5 | ||||||
|  |         ) { | ||||||
|  |             let defaultCursor = Gdk.Cursor.new_from_name('default', null); | ||||||
|  |             this.widget.set_cursor(defaultCursor); | ||||||
|  |             this._setHideCursorTimeout(); | ||||||
|  |  | ||||||
|  |             let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |  | ||||||
|  |             if(clapperWidget.fullscreenMode) { | ||||||
|  |                 if(!this._updateTimeTimeout) | ||||||
|  |                     this._setUpdateTimeInterval(); | ||||||
|  |  | ||||||
|  |                 if(!clapperWidget.revealerTop.get_reveal_child()) { | ||||||
|  |                     /* Do not grab controls key focus on mouse movement */ | ||||||
|  |                     clapperWidget.revealerBottom.set_can_focus(false); | ||||||
|  |                     clapperWidget.revealControls(true); | ||||||
|  |                 } | ||||||
|  |                 this._setHideControlsTimeout(); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 if(this._hideControlsTimeout) | ||||||
|  |                     this._clearTimeout('hideControls'); | ||||||
|  |                 if(this._updateTimeTimeout) | ||||||
|  |                     this._clearTimeout('updateTime'); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.posX = posX; | ||||||
|  |         this.posY = posY; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onWidgetDragUpdate(gesture, offsetX, offsetY) | ||||||
|  |     { | ||||||
|  |         if(!this.dragAllowed) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         let clapperWidget = this.widget.get_ancestor(Gtk.Grid); | ||||||
|  |         if(clapperWidget.fullscreenMode) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         let { gtk_double_click_distance } = this.widget.get_settings(); | ||||||
|  |  | ||||||
|  |         if ( | ||||||
|  |             Math.abs(offsetX) > gtk_double_click_distance | ||||||
|  |             || Math.abs(offsetY) > gtk_double_click_distance | ||||||
|  |         ) { | ||||||
|  |             let [isActive, startX, startY] = gesture.get_start_point(); | ||||||
|  |             if(!isActive) return; | ||||||
|  |  | ||||||
|  |             let root = this.widget.get_root(); | ||||||
|  |             if(!root) return; | ||||||
|  |  | ||||||
|  |             root.get_surface().begin_move( | ||||||
|  |                 gesture.get_device(), | ||||||
|  |                 gesture.get_current_button(), | ||||||
|  |                 startX, | ||||||
|  |                 startY, | ||||||
|  |                 gesture.get_current_event_time() | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             gesture.reset(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onScroll(controller, dx, dy) | ||||||
|  |     { | ||||||
|  |         let isHorizontal = Math.abs(dx) >= Math.abs(dy); | ||||||
|  |         let isIncrease = (isHorizontal) ? dx < 0 : dy < 0; | ||||||
|  |  | ||||||
|  |         if(isHorizontal) | ||||||
|  |             this.adjust_position(isIncrease); | ||||||
|  |         else | ||||||
|  |             this.adjust_volume(isIncrease); | ||||||
|  |  | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _onCloseRequest(window) | ||||||
|     { |     { | ||||||
|         while(this._widgetSignals.length) |         while(this._widgetSignals.length) | ||||||
|             this.widget.disconnect(this._widgetSignals.pop()); |             this.widget.disconnect(this._widgetSignals.pop()); | ||||||
|  |  | ||||||
|         while(this._playerSignals.length) |         while(this._playerSignals.length) | ||||||
|             this.disconnect(this._playerSignals.pop()); |             this.disconnect(this._playerSignals.pop()); | ||||||
|  |  | ||||||
|  |         if(this.state !== GstPlayer.PlayerState.STOPPED) | ||||||
|  |             this.stop(); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| const { Gdk, GLib, GObject, Gtk, Pango } = imports.gi; | const { GLib, GObject, Gtk, Pango } = imports.gi; | ||||||
| const Debug = imports.clapper_src.debug; | const Debug = imports.clapper_src.debug; | ||||||
|  |  | ||||||
| const REVEAL_TIME = 800; | const REVEAL_TIME = 800; | ||||||
| @@ -14,6 +14,7 @@ class ClapperCustomRevealer extends Gtk.Revealer | |||||||
|  |  | ||||||
|         let defaults = { |         let defaults = { | ||||||
|             visible: false, |             visible: false, | ||||||
|  |             can_focus: false, | ||||||
|         }; |         }; | ||||||
|         Object.assign(opts, defaults); |         Object.assign(opts, defaults); | ||||||
|  |  | ||||||
| @@ -31,6 +32,9 @@ class ClapperCustomRevealer extends Gtk.Revealer | |||||||
|         else |         else | ||||||
|             this._setHideTimeout(); |             this._setHideTimeout(); | ||||||
|  |  | ||||||
|  |         /* Restore focusability after we are done */ | ||||||
|  |         if(!isReveal) this.set_can_focus(true); | ||||||
|  |  | ||||||
|         this._timedReveal(isReveal, REVEAL_TIME); |         this._timedReveal(isReveal, REVEAL_TIME); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -94,18 +98,6 @@ class ClapperRevealerTop extends CustomRevealer | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         this.revealerName = 'top'; |         this.revealerName = 'top'; | ||||||
| /* |  | ||||||
|         this.set_events( |  | ||||||
|             Gdk.EventMask.BUTTON_PRESS_MASK |  | ||||||
|             | Gdk.EventMask.BUTTON_RELEASE_MASK |  | ||||||
|             | Gdk.EventMask.TOUCH_MASK |  | ||||||
|             | Gdk.EventMask.SCROLL_MASK |  | ||||||
|             | Gdk.EventMask.TOUCHPAD_GESTURE_MASK |  | ||||||
|             | Gdk.EventMask.POINTER_MOTION_MASK |  | ||||||
|             | Gdk.EventMask.ENTER_NOTIFY_MASK |  | ||||||
|             | Gdk.EventMask.LEAVE_NOTIFY_MASK |  | ||||||
|         ); |  | ||||||
| */ |  | ||||||
|         let initTime = GLib.DateTime.new_now_local().format('%X'); |         let initTime = GLib.DateTime.new_now_local().format('%X'); | ||||||
|         this.timeFormat = (initTime.length > 8) |         this.timeFormat = (initTime.length > 8) | ||||||
|             ? '%I:%M %p' |             ? '%I:%M %p' | ||||||
| @@ -198,7 +190,7 @@ class ClapperRevealerBottom extends CustomRevealer | |||||||
|     set_visible(isVisible) |     set_visible(isVisible) | ||||||
|     { |     { | ||||||
|         let isChange = super.set_visible(isVisible); |         let isChange = super.set_visible(isVisible); | ||||||
|         if(!isChange) return; |         if(!isChange || !this.can_focus) return; | ||||||
|  |  | ||||||
|         let parent = this.get_parent(); |         let parent = this.get_parent(); | ||||||
|         let playerWidget = parent.get_first_child(); |         let playerWidget = parent.get_first_child(); | ||||||
|   | |||||||
| @@ -1,12 +1,14 @@ | |||||||
| const { Gdk, GLib, GObject, Gtk, Gst, GstPlayer, Pango } = imports.gi; | const { Gdk, GLib, GObject, Gtk, 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; | ||||||
|  | const Misc = imports.clapper_src.misc; | ||||||
|  | const { Player } = imports.clapper_src.player; | ||||||
| const Revealers = imports.clapper_src.revealers; | const Revealers = imports.clapper_src.revealers; | ||||||
| 
 | 
 | ||||||
| let { debug } = Debug; | let { debug } = Debug; | ||||||
| 
 | 
 | ||||||
| var Interface = GObject.registerClass( | var Widget = GObject.registerClass( | ||||||
| class ClapperInterface extends Gtk.Grid | class ClapperWidget extends Gtk.Grid | ||||||
| { | { | ||||||
|     _init(opts) |     _init(opts) | ||||||
|     { |     { | ||||||
| @@ -15,19 +17,23 @@ class ClapperInterface extends Gtk.Grid | |||||||
|         super._init(); |         super._init(); | ||||||
| 
 | 
 | ||||||
|         let defaults = { |         let defaults = { | ||||||
|             seekOnDrop: true |             cssPath: `${pkg.datadir}/${pkg.name}/css/styles.css`, | ||||||
|         }; |         }; | ||||||
|         Object.assign(this, defaults, opts); |         Object.assign(this, defaults, opts); | ||||||
| 
 | 
 | ||||||
|  |         let cssProvider = new Gtk.CssProvider(); | ||||||
|  |         cssProvider.load_from_path(this.cssPath); | ||||||
|  |         Gtk.StyleContext.add_provider_for_display( | ||||||
|  |             Gdk.Display.get_default(), | ||||||
|  |             cssProvider, | ||||||
|  |             Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|         this.fullscreenMode = false; |         this.fullscreenMode = false; | ||||||
|         this.isSeekable = false; |         this.isSeekable = false; | ||||||
| 
 | 
 | ||||||
|         this.lastVolumeValue = null; |  | ||||||
|         this.lastPositionValue = 0; |  | ||||||
|         this.lastRevealerEventTime = 0; |         this.lastRevealerEventTime = 0; | ||||||
|         this.needsTracksUpdate = true; |         this.needsTracksUpdate = true; | ||||||
|         this.headerBar = null; |  | ||||||
|         this.defaultTitle = null; |  | ||||||
|         this.mediaInfoSignal = null; |         this.mediaInfoSignal = null; | ||||||
| 
 | 
 | ||||||
|         this.videoBox = new Gtk.Box(); |         this.videoBox = new Gtk.Box(); | ||||||
| @@ -41,54 +47,21 @@ class ClapperInterface extends Gtk.Grid | |||||||
|         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.destroySignal = this.connect('destroy', this._onDestroy.bind(this)); |         this.mapSignal = this.connect('map', this._onMap.bind(this)); | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     addPlayer(player) |         this.player = new Player(); | ||||||
|     { |         this.player.widget.width_request = 960; | ||||||
|         this._player = player; |         this.player.widget.height_request = 540; | ||||||
|         this._player.widget.vexpand = true; |  | ||||||
|         this._player.widget.hexpand = true; |  | ||||||
| 
 | 
 | ||||||
|         this._player.connect('state-changed', this._onPlayerStateChanged.bind(this)); |         this.player.selfConnect('position-updated', this._onPlayerPositionUpdated.bind(this)); | ||||||
|         this._player.connect('volume-changed', this._onPlayerVolumeChanged.bind(this)); |         this.player.selfConnect('duration-changed', this._onPlayerDurationChanged.bind(this)); | ||||||
|         this._player.connect('duration-changed', this._onPlayerDurationChanged.bind(this)); |         this.player.selfConnect('volume-changed', this._onPlayerVolumeChanged.bind(this)); | ||||||
|         this._player.connect('position-updated', this._onPlayerPositionUpdated.bind(this)); |  | ||||||
| 
 | 
 | ||||||
|         this._player.scrollController.connect( |         this.overlay.set_child(this.player.widget); | ||||||
|             'scroll', (ctl, dx, dy) => this.controls._onScroll(ctl, dx, dy) |  | ||||||
|         ); |  | ||||||
|         this.controls.togglePlayButton.connect( |  | ||||||
|             'clicked', this._onControlsTogglePlayClicked.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.scaleSig = this.controls.positionScale.connect( |  | ||||||
|             'value-changed', this._onControlsPositionChanged.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.controls.volumeScale.connect( |  | ||||||
|             'value-changed', this._onControlsVolumeChanged.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.controls.connect( |  | ||||||
|             'position-seeking-changed', this._onPositionSeekingChanged.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.controls.connect( |  | ||||||
|             'track-change-requested', this._onTrackChangeRequested.bind(this) |  | ||||||
|         ); |  | ||||||
|         this.controls.connect( |  | ||||||
|             'visualization-change-requested', this._onVisualizationChangeRequested.bind(this) |  | ||||||
|         ); |  | ||||||
|         //this.revealerTop.connect('event-after', (self, event) => this._player.widget.event(event));
 |  | ||||||
| 
 |  | ||||||
|         this.overlay.set_child(this._player.widget); |  | ||||||
|         this.overlay.add_overlay(this.revealerTop); |         this.overlay.add_overlay(this.revealerTop); | ||||||
|         this.overlay.add_overlay(this.revealerBottom); |         this.overlay.add_overlay(this.revealerBottom); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     addHeaderBar(headerBar, defaultTitle) |  | ||||||
|     { |  | ||||||
|         this.headerBar = headerBar; |  | ||||||
|         this.defaultTitle = defaultTitle || null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     revealControls(isReveal) |     revealControls(isReveal) | ||||||
|     { |     { | ||||||
|         for(let pos of ['Top', 'Bottom']) |         for(let pos of ['Top', 'Bottom']) | ||||||
| @@ -101,30 +74,18 @@ class ClapperInterface extends Gtk.Grid | |||||||
|             this[`revealer${pos}`].showChild(isShow); |             this[`revealer${pos}`].showChild(isShow); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     setFullscreenMode(isFullscreen) |     toggleFullscreen() | ||||||
|     { |     { | ||||||
|         if(this.fullscreenMode === isFullscreen) |         let root = this.get_root(); | ||||||
|             return; |         if(!root) return; | ||||||
| 
 | 
 | ||||||
|         if(isFullscreen) { |         let un = (this.fullscreenMode) ? 'un' : ''; | ||||||
|             this.remove(this.controls); |         root[`${un}fullscreen`](); | ||||||
|             this.revealerBottom.append(this.controls); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             this.revealerBottom.remove(this.controls); |  | ||||||
|             this.attach(this.controls, 0, 1, 1, 1); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.controls.setFullscreenMode(isFullscreen); |  | ||||||
|         this.showControls(isFullscreen); |  | ||||||
| 
 |  | ||||||
|         this.fullscreenMode = isFullscreen; |  | ||||||
|         debug(`interface in fullscreen mode: ${isFullscreen}`); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onMediaInfoUpdated(player, mediaInfo) |     _onMediaInfoUpdated(player, mediaInfo) | ||||||
|     { |     { | ||||||
|         this._player.disconnect(this.mediaInfoSignal); |         player.disconnect(this.mediaInfoSignal); | ||||||
| 
 | 
 | ||||||
|         /* Set titlebar media title and path */ |         /* Set titlebar media title and path */ | ||||||
|         this.updateTitles(mediaInfo); |         this.updateTitles(mediaInfo); | ||||||
| @@ -193,7 +154,7 @@ class ClapperInterface extends Gtk.Grid | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for(let type of ['video', 'audio', 'subtitle']) { |         for(let type of ['video', 'audio', 'subtitle']) { | ||||||
|             let currStream = this._player[`get_current_${type}_track`](); |             let currStream = player[`get_current_${type}_track`](); | ||||||
|             let activeId = (currStream) ? currStream.get_index() : -1; |             let activeId = (currStream) ? currStream.get_index() : -1; | ||||||
| 
 | 
 | ||||||
|             if(currStream && type !== 'subtitle') { |             if(currStream && type !== 'subtitle') { | ||||||
| @@ -227,17 +188,27 @@ class ClapperInterface extends Gtk.Grid | |||||||
| 
 | 
 | ||||||
|     updateTitles(mediaInfo) |     updateTitles(mediaInfo) | ||||||
|     { |     { | ||||||
|         if(this.headerBar) |         let root = this.get_root(); | ||||||
|             this.headerBar.updateHeaderBar(mediaInfo); |         if(!root) return; | ||||||
| 
 | 
 | ||||||
|         this.revealerTop.setMediaTitle(this.headerBar.titleLabel.label); |         let title; | ||||||
|  |         let headerbar = root.get_titlebar(); | ||||||
|  | 
 | ||||||
|  |         if(headerbar && headerbar.updateHeaderBar) { | ||||||
|  |             headerbar.updateHeaderBar(mediaInfo); | ||||||
|  |             title = headerbar.titleLabel.label; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             title = mediaInfo.get_title() || mediaInfo.get_uri(); | ||||||
|  | 
 | ||||||
|  |         this.revealerTop.setMediaTitle(title); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     updateTime() |     updateTime() | ||||||
|     { |     { | ||||||
|         let currTime = GLib.DateTime.new_now_local(); |         let currTime = GLib.DateTime.new_now_local(); | ||||||
|         let endTime = currTime.add_seconds( |         let endTime = currTime.add_seconds( | ||||||
|             this.controls.positionAdjustment.get_upper() - this.lastPositionValue |             this.controls.positionAdjustment.get_upper() - this.controls.currentPosition | ||||||
|         ); |         ); | ||||||
|         let nextUpdate = this.revealerTop.setTimes(currTime, endTime); |         let nextUpdate = this.revealerTop.setTimes(currTime, endTime); | ||||||
| 
 | 
 | ||||||
| @@ -283,78 +254,20 @@ class ClapperInterface extends Gtk.Grid | |||||||
|         debug(`show visualizations button: ${isShow}`); |         debug(`show visualizations button: ${isShow}`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onTrackChangeRequested(self, type, activeId) |  | ||||||
|     { |  | ||||||
|         /* 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); |  | ||||||
| 
 |  | ||||||
|             if(this._player.get_mute()) |  | ||||||
|                 this._player.set_mute(false); |  | ||||||
| 
 |  | ||||||
|             return this._player[`set_${type}_track`](activeId); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(activeId < 0) { |  | ||||||
|             /* Disabling video leaves last frame frozen, |  | ||||||
|              * so we hide it by making it transparent */ |  | ||||||
|             if(type === 'video') |  | ||||||
|                 this._player.widget.set_opacity(0); |  | ||||||
| 
 |  | ||||||
|             return this._player[`set_${type}_track_enabled`](false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this._player[`set_${type}_track`](activeId); |  | ||||||
|         this._player[`set_${type}_track_enabled`](true); |  | ||||||
| 
 |  | ||||||
|         if(type === 'video' && !this._player.widget.opacity) { |  | ||||||
|             this._player.widget.set_opacity(1); |  | ||||||
|             this._player.renderer.expose(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     _onVisualizationChangeRequested(self, visName) |  | ||||||
|     { |  | ||||||
|         let isEnabled = this._player.get_visualization_enabled(); |  | ||||||
| 
 |  | ||||||
|         if(!visName) { |  | ||||||
|             if(isEnabled) { |  | ||||||
|                 this._player.set_visualization_enabled(false); |  | ||||||
|                 debug('disabled visualizations'); |  | ||||||
|             } |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let currVis = this._player.get_current_visualization(); |  | ||||||
| 
 |  | ||||||
|         if(currVis === visName) |  | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         debug(`set visualization: ${visName}`); |  | ||||||
|         this._player.set_visualization(visName); |  | ||||||
| 
 |  | ||||||
|         if(!isEnabled) { |  | ||||||
|             this._player.set_visualization_enabled(true); |  | ||||||
|             debug('enabled visualizations'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     _onPlayerStateChanged(player, state) |     _onPlayerStateChanged(player, state) | ||||||
|     { |     { | ||||||
|         switch(state) { |         switch(state) { | ||||||
|             case GstPlayer.PlayerState.BUFFERING: |             case GstPlayer.PlayerState.BUFFERING: | ||||||
|                 if(!this._player.is_local_file) |                 if(!player.is_local_file) | ||||||
|                     this.needsTracksUpdate = true; |                     this.needsTracksUpdate = true; | ||||||
|                 break; |                 break; | ||||||
|             case GstPlayer.PlayerState.STOPPED: |             case GstPlayer.PlayerState.STOPPED: | ||||||
|                 this.lastPositionValue = 0; |                 this.controls.currentPosition = 0; | ||||||
|                 this.controls.positionAdjustment.set_value(0); |                 this.controls.positionScale.set_value(0); | ||||||
|                 this.controls.togglePlayButton.setPrimaryIcon(); |                 this.controls.togglePlayButton.setPrimaryIcon(); | ||||||
|                 this.needsTracksUpdate = true; |                 this.needsTracksUpdate = true; | ||||||
|                 if(this.mediaInfoSignal) { |                 if(this.mediaInfoSignal) { | ||||||
|                     this._player.disconnect(this.mediaInfoSignal); |                     player.disconnect(this.mediaInfoSignal); | ||||||
|                     this.mediaInfoSignal = null; |                     this.mediaInfoSignal = null; | ||||||
|                 } |                 } | ||||||
|             case GstPlayer.PlayerState.PAUSED: |             case GstPlayer.PlayerState.PAUSED: | ||||||
| @@ -364,7 +277,7 @@ class ClapperInterface extends Gtk.Grid | |||||||
|                 this.controls.togglePlayButton.setSecondaryIcon(); |                 this.controls.togglePlayButton.setSecondaryIcon(); | ||||||
|                 if(this.needsTracksUpdate && !this.mediaInfoSignal) { |                 if(this.needsTracksUpdate && !this.mediaInfoSignal) { | ||||||
|                     this.needsTracksUpdate = false; |                     this.needsTracksUpdate = false; | ||||||
|                     this.mediaInfoSignal = this._player.connect( |                     this.mediaInfoSignal = player.connect( | ||||||
|                         'media_info_updated', this._onMediaInfoUpdated.bind(this) |                         'media_info_updated', this._onMediaInfoUpdated.bind(this) | ||||||
|                     ); |                     ); | ||||||
|                 } |                 } | ||||||
| @@ -372,127 +285,100 @@ class ClapperInterface extends Gtk.Grid | |||||||
|             default: |             default: | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if(state === GstPlayer.PlayerState.BUFFERING) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         let window = this.get_root(); | ||||||
|  |         Misc.inhibitForState(state, window); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onPlayerDurationChanged(player) |     _onPlayerDurationChanged(player) | ||||||
|     { |     { | ||||||
|         let duration = this._player.get_duration() / 1000000000; |         let duration = Math.floor(player.get_duration() / 1000000000); | ||||||
|  | 
 | ||||||
|  |         /* Sometimes GstPlayer might re-emit | ||||||
|  |          * duration changed during playback */ | ||||||
|  |         if(this.controls.currentDuration === duration) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|         let increment = (duration < 1) |         let increment = (duration < 1) | ||||||
|             ? 0 |             ? 0 | ||||||
|             : (duration < 100) |             : (duration < 100) | ||||||
|             ? 1 |             ? 1 | ||||||
|             : duration / 100; |             : duration / 100; | ||||||
| 
 | 
 | ||||||
|         this.controls.positionAdjustment.set_upper(Math.floor(duration)); |         this.controls.positionAdjustment.set_upper(duration); | ||||||
|         this.controls.positionAdjustment.set_step_increment(increment); |         this.controls.positionAdjustment.set_step_increment(increment); | ||||||
|         this.controls.positionAdjustment.set_page_increment(increment); |         this.controls.positionAdjustment.set_page_increment(increment); | ||||||
| 
 | 
 | ||||||
|         this.controls.durationFormated = this.controls._getFormatedTime(duration); |         this.controls.currentDuration = duration; | ||||||
|         this.controls._onPositionScaleValueChanged(); |         this.controls.durationFormated = Misc.getFormatedTime(duration); | ||||||
|  |         this.controls.updateElapsedLabel(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onPlayerPositionUpdated(player, position) |     _onPlayerPositionUpdated(player, position) | ||||||
|     { |     { | ||||||
|         if( |         if( | ||||||
|             !this.isSeekable |             !this.isSeekable | ||||||
|             || !this._player.seek_done |             || !player.seek_done | ||||||
|             || this.controls.isPositionSeeking |             || this.controls.isPositionSeeking | ||||||
|             || this._player.state === GstPlayer.PlayerState.BUFFERING |             || player.state === GstPlayer.PlayerState.BUFFERING | ||||||
|         ) |         ) | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|         let positionSeconds = Math.round(position / 1000000000); |         let positionSeconds = Math.round(position / 1000000000); | ||||||
| 
 |         if(positionSeconds === this.controls.currentPosition) | ||||||
|         if(positionSeconds === this.lastPositionValue) |  | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|         this.lastPositionValue = positionSeconds; |         this.controls.currentPosition = positionSeconds; | ||||||
|         this.controls.positionScale.set_value(positionSeconds); |         this.controls.positionScale.set_value(positionSeconds); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onPlayerVolumeChanged() |     _onPlayerVolumeChanged(player) | ||||||
|     { |     { | ||||||
|         let volume = Number(this._player.get_volume().toFixed(2)); |         let volume = Number(player.get_volume().toFixed(2)); | ||||||
| 
 |         if(volume === this.currentVolume) | ||||||
|         if(volume === this.lastVolumeValue) |  | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|         this.lastVolumeValue = volume; |         this.controls.currentVolume = volume; | ||||||
|         this.controls.volumeScale.set_value(volume); |         this.controls.volumeScale.set_value(volume); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onPositionSeekingChanged(self, isPositionSeeking) |     _onStateNotify(toplevel) | ||||||
|     { |     { | ||||||
|         if(isPositionSeeking || !this.seekOnDrop) |         let isFullscreen = Boolean( | ||||||
|  |             toplevel.state & Gdk.ToplevelState.FULLSCREEN | ||||||
|  |         ); | ||||||
|  |         if(this.fullscreenMode === isFullscreen) | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|         this._onControlsPositionChanged(this.controls.positionScale); |         this.fullscreenMode = isFullscreen; | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     _onControlsTogglePlayClicked() |         if(isFullscreen) { | ||||||
|     { |             this.remove(this.controls); | ||||||
|         this._player.toggle_play(); |             this.revealerBottom.append(this.controls); | ||||||
|     } |         } | ||||||
| 
 |         else { | ||||||
|     _onControlsPositionChanged(positionScale) |             this.revealerBottom.remove(this.controls); | ||||||
|     { |             this.attach(this.controls, 0, 1, 1, 1); | ||||||
|         if(this.seekOnDrop && this.controls.isPositionSeeking) |  | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         let positionSeconds = Math.round(positionScale.get_value()); |  | ||||||
| 
 |  | ||||||
|         if(positionSeconds === this.lastPositionValue) |  | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         this.lastPositionValue = positionSeconds; |  | ||||||
|         this._player.seek_seconds(positionSeconds); |  | ||||||
| 
 |  | ||||||
|         /* Needed to enable preview after playback is stopped */ |  | ||||||
|         if(this._player.state === GstPlayer.PlayerState.STOPPED) |  | ||||||
|             this._player.pause(); |  | ||||||
| 
 |  | ||||||
|         if(this.fullscreenMode) |  | ||||||
|             this.updateTime(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     _onControlsVolumeChanged(volumeScale) |  | ||||||
|     { |  | ||||||
|         let volume = Number(volumeScale.get_value().toFixed(2)); |  | ||||||
| 
 |  | ||||||
|         let icon = (volume <= 0) |  | ||||||
|             ? 'muted' |  | ||||||
|             : (volume <= 0.33) |  | ||||||
|             ? 'low' |  | ||||||
|             : (volume <= 0.66) |  | ||||||
|             ? 'medium' |  | ||||||
|             : (volume <= 1) |  | ||||||
|             ? 'high' |  | ||||||
|             : 'overamplified'; |  | ||||||
| 
 |  | ||||||
|         let iconName = `audio-volume-${icon}-symbolic`; |  | ||||||
|         if(this.controls.volumeButton.icon_name !== iconName) |  | ||||||
|         { |  | ||||||
|             debug(`set volume icon: ${icon}`); |  | ||||||
|             this.controls.volumeButton.set_icon_name(iconName); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(volume === this.lastVolumeValue) |         this.controls.setFullscreenMode(isFullscreen); | ||||||
|             return; |         this.showControls(isFullscreen); | ||||||
|  |         this.player.widget.grab_focus(); | ||||||
| 
 | 
 | ||||||
|         this.lastVolumeValue = volume; |         debug(`interface in fullscreen mode: ${isFullscreen}`); | ||||||
|         this._player.set_volume(volume); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     _onDestroy() |     _onMap() | ||||||
|     { |     { | ||||||
|         this.disconnect(this.destroySignal); |         this.disconnect(this.mapSignal); | ||||||
| 
 | 
 | ||||||
|         if( |         let root = this.get_root(); | ||||||
|             this._player |         if(!root) return; | ||||||
|             && this._player.state !== GstPlayer.PlayerState.STOPPED |  | ||||||
|         ) |  | ||||||
|             this._player.stop(); |  | ||||||
| 
 | 
 | ||||||
|         this.controls.emit('destroy'); |         let surface = root.get_surface(); | ||||||
|  |         surface.connect('notify::state', this._onStateNotify.bind(this)); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
| @@ -1,49 +1,18 @@ | |||||||
| const { Gdk, GObject, Gtk } = imports.gi; | const { Gdk, GObject, Gtk } = imports.gi; | ||||||
|  |  | ||||||
| var Window = GObject.registerClass({ | var Window = GObject.registerClass( | ||||||
|     Signals: { | class ClapperWindow extends Gtk.ApplicationWindow | ||||||
|         'fullscreen-changed': { |  | ||||||
|             param_types: [GObject.TYPE_BOOLEAN] |  | ||||||
|         }, |  | ||||||
|     } |  | ||||||
| }, class ClapperWindow extends Gtk.ApplicationWindow |  | ||||||
| { | { | ||||||
|     _init(application, title) |     _init(application, title) | ||||||
|     { |     { | ||||||
|         super._init({ |         super._init({ | ||||||
|             application: application, |             application: application, | ||||||
|             title: title || 'Clapper', |             title: title, | ||||||
|             resizable: true, |  | ||||||
|             destroy_with_parent: true, |  | ||||||
|         }); |         }); | ||||||
|         this.isFullscreen = false; |  | ||||||
|         this.mapSignal = this.connect('map', this._onMap.bind(this)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     toggleFullscreen() |     updateTitlebar(mediaInfo) | ||||||
|     { |     { | ||||||
|         let un = (this.isFullscreen) ? 'un' : ''; |          | ||||||
|         this[`${un}fullscreen`](); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onStateNotify(toplevel) |  | ||||||
|     { |  | ||||||
|         let isFullscreen = Boolean( |  | ||||||
|             toplevel.state & Gdk.ToplevelState.FULLSCREEN |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         if(this.isFullscreen === isFullscreen) |  | ||||||
|             return; |  | ||||||
|  |  | ||||||
|         this.isFullscreen = isFullscreen; |  | ||||||
|         this.emit('fullscreen-changed', this.isFullscreen); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _onMap() |  | ||||||
|     { |  | ||||||
|         this.disconnect(this.mapSignal); |  | ||||||
|  |  | ||||||
|         let surface = this.get_surface(); |  | ||||||
|         surface.connect('notify::state', this._onStateNotify.bind(this)); |  | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ imports.searchPath.unshift('@importspath@'); | |||||||
| const ClapperSrc = imports.clapper_src; | const ClapperSrc = imports.clapper_src; | ||||||
|  |  | ||||||
| var { App } = ClapperSrc.app; | var { App } = ClapperSrc.app; | ||||||
| var { Interface } = ClapperSrc.interface; | var { Widget } = ClapperSrc.widget; | ||||||
| var { Player } = ClapperSrc.player; |  | ||||||
|  |  | ||||||
| imports.searchPath.shift(); | imports.searchPath.shift(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user