From 12591e106fc68b8bc7017092f8f7bf7b760dc474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Wed, 17 Feb 2021 16:31:37 +0100 Subject: [PATCH] Move event controllers to widget and add them to top revealer --- src/buttons.js | 4 +- src/controls.js | 12 +- src/headerbar.js | 2 +- src/player.js | 291 +------------------------------------- src/widget.js | 358 +++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 343 insertions(+), 324 deletions(-) diff --git a/src/buttons.js b/src/buttons.js index 9a48d78c..73b1c04b 100644 --- a/src/buttons.js +++ b/src/buttons.js @@ -44,8 +44,8 @@ class ClapperCustomButton extends Gtk.Button if(!this.isFullscreen) return; - const { player } = this.get_ancestor(Gtk.Grid); - player._setHideControlsTimeout(); + const clapperWidget = this.get_ancestor(Gtk.Grid); + clapperWidget._setHideControlsTimeout(); } }); diff --git a/src/controls.js b/src/controls.js index 447ac2fa..33300958 100644 --- a/src/controls.js +++ b/src/controls.js @@ -478,13 +478,13 @@ class ClapperControls extends Gtk.Box this.disconnect(this.realizeSignal); this.realizeSignal = null; - const { player } = this.get_ancestor(Gtk.Grid); + const clapperWidget = this.get_ancestor(Gtk.Grid); const scrollController = new Gtk.EventControllerScroll(); scrollController.set_flags( Gtk.EventControllerScrollFlags.VERTICAL | Gtk.EventControllerScrollFlags.DISCRETE ); - scrollController.connect('scroll', player._onScroll.bind(player)); + scrollController.connect('scroll', clapperWidget._onScroll.bind(clapperWidget)); this.volumeButton.add_controller(scrollController); const initialVolume = (settings.get_string('volume-initial') === 'custom') @@ -541,8 +541,8 @@ class ClapperControls extends Gtk.Box _onPositionScaleScroll(controller, dx, dy) { - const { player } = this.get_ancestor(Gtk.Grid); - player._onScroll(controller, dx || dy, 0); + const clapperWidget = this.get_ancestor(Gtk.Grid); + clapperWidget._onScroll(controller, dx || dy, 0); } _onPositionScaleValueChanged(scale) @@ -617,8 +617,8 @@ class ClapperControls extends Gtk.Box /* Only happens when navigating through controls panel */ _onControlsKeyPressed(controller, keyval, keycode, state) { - const { player } = this.get_ancestor(Gtk.Grid); - player._setHideControlsTimeout(); + const clapperWidget = this.get_ancestor(Gtk.Grid); + clapperWidget._setHideControlsTimeout(); } _onControlsKeyReleased(controller, keyval, keycode, state) diff --git a/src/headerbar.js b/src/headerbar.js index 708fbc87..4d8cfd8d 100644 --- a/src/headerbar.js +++ b/src/headerbar.js @@ -22,7 +22,7 @@ class ClapperHeaderBar extends HeaderBarBase clapperWidget.controlsRevealer.toggleReveal(); /* Reset timer to not disappear during click */ - clapperWidget.player._setHideControlsTimeout(); + clapperWidget._setHideControlsTimeout(); } _onFullscreenButtonClicked() diff --git a/src/player.js b/src/player.js index 2ebb68d1..ee7d7a16 100644 --- a/src/player.js +++ b/src/player.js @@ -14,10 +14,7 @@ class ClapperPlayer extends PlayerBase { super._init(); - this.cursorInPlayer = false; this.seek_done = true; - this.dragAllowed = false; - this.isWidgetDragging = false; this.doneStartup = false; this.needsFastSeekRestore = false; @@ -25,54 +22,15 @@ class ClapperPlayer extends PlayerBase this.quitOnStop = false; this.needsTocUpdate = true; - this.posX = 0; - this.posY = 0; this.keyPressCount = 0; this._maxVolume = Misc.getLinearValue(Misc.maxVolume); - this._hideControlsTimeout = null; - this._updateTimeTimeout = null; - - const clickGesture = new Gtk.GestureClick(); - clickGesture.set_button(0); - clickGesture.connect('pressed', this._onWidgetPressed.bind(this)); - this.widget.add_controller(clickGesture); - - const dragGesture = new Gtk.GestureDrag(); - dragGesture.connect('drag-update', this._onWidgetDragUpdate.bind(this)); - this.widget.add_controller(dragGesture); - - const swipeGesture = new Gtk.GestureSwipe({ - touch_only: true, - }); - swipeGesture.connect('swipe', this._onWidgetSwipe.bind(this)); - swipeGesture.connect('update', this._onWidgetSwipeUpdate.bind(this)); - this.widget.add_controller(swipeGesture); - const keyController = new Gtk.EventControllerKey(); keyController.connect('key-pressed', this._onWidgetKeyPressed.bind(this)); keyController.connect('key-released', this._onWidgetKeyReleased.bind(this)); this.widget.add_controller(keyController); - const scrollController = new Gtk.EventControllerScroll(); - scrollController.set_flags(Gtk.EventControllerScrollFlags.BOTH_AXES); - scrollController.connect('scroll', this._onScroll.bind(this)); - this.widget.add_controller(scrollController); - - const 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); - - const dropTarget = new Gtk.DropTarget({ - actions: Gdk.DragAction.COPY, - }); - dropTarget.set_gtypes([GObject.TYPE_STRING]); - dropTarget.connect('drop', this._onDataDrop.bind(this)); - this.widget.add_controller(dropTarget); - this.connect('state-changed', this._onStateChanged.bind(this)); this.connect('uri-loaded', this._onUriLoaded.bind(this)); this.connect('end-of-stream', this._onStreamEnded.bind(this)); @@ -300,76 +258,6 @@ class ClapperPlayer extends PlayerBase } } - getIsSwipeOk(velocity, otherVelocity) - { - if(!velocity) - return false; - - const absVel = Math.abs(velocity); - - if(absVel < 20 || Math.abs(otherVelocity) * 1.5 >= absVel) - return false; - - const clapperWidget = this.widget.get_ancestor(Gtk.Grid); - - return clapperWidget.isFullscreenMode; - } - - _setHideControlsTimeout() - { - this._clearTimeout('hideControls'); - this._hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => { - this._hideControlsTimeout = null; - - if(this.cursorInPlayer) { - const clapperWidget = this.widget.get_ancestor(Gtk.Grid); - const blankCursor = Gdk.Cursor.new_from_name('none', null); - - this.widget.set_cursor(blankCursor); - clapperWidget.revealerTop.set_cursor(blankCursor); - - if(clapperWidget.isFullscreenMode) - this._clearTimeout('updateTime'); - - clapperWidget.revealControls(false); - } - - return GLib.SOURCE_REMOVE; - }); - } - - _setUpdateTimeInterval() - { - this._clearTimeout('updateTime'); - - const clapperWidget = this.widget.get_ancestor(Gtk.Grid); - const nextUpdate = clapperWidget.updateTime(); - - if(nextUpdate === null) - return; - - this._updateTimeTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, nextUpdate, () => { - this._updateTimeTimeout = null; - - if(clapperWidget.isFullscreenMode) - 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'); - } - _performCloseCleanup(window) { window.disconnect(this.closeRequestSignal); @@ -546,7 +434,7 @@ class ClapperPlayer extends PlayerBase bool = true; case Gdk.KEY_Left: this.adjust_position(bool); - this._clearTimeout('hideControls'); + clapperWidget._clearTimeout('hideControls'); if(this.keyPressCount > 1) { clapperWidget.revealerBottom.set_can_focus(false); clapperWidget.revealerBottom.revealChild(true); @@ -572,7 +460,7 @@ class ClapperPlayer extends PlayerBase case Gdk.KEY_Return: if(clapperWidget.isFullscreenMode) { clapperWidget.revealControls(true); - this._setHideControlsTimeout(); + clapperWidget._setHideControlsTimeout(); } break; case Gdk.KEY_Right: @@ -581,7 +469,7 @@ class ClapperPlayer extends PlayerBase clapperWidget.controls.positionScale.get_value() ); this.seek_seconds(value); - this._setHideControlsTimeout(); + clapperWidget._setHideControlsTimeout(); break; case Gdk.KEY_F11: case Gdk.KEY_f: @@ -604,179 +492,6 @@ class ClapperPlayer extends PlayerBase } } - _onWidgetPressed(gesture, nPress, x, y) - { - const button = gesture.get_current_button(); - const isDouble = (nPress % 2 == 0); - this.dragAllowed = !isDouble; - - switch(button) { - case Gdk.BUTTON_PRIMARY: - if(isDouble) { - const 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.isWidgetDragging = false; - - this._setHideControlsTimeout(); - } - - _onWidgetLeave(controller) - { - this.cursorInPlayer = false; - - 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 - ) { - const clapperWidget = this.widget.get_ancestor(Gtk.Grid); - const defaultCursor = Gdk.Cursor.new_from_name('default', null); - - this.widget.set_cursor(defaultCursor); - clapperWidget.revealerTop.set_cursor(defaultCursor); - - this._setHideControlsTimeout(); - - if(clapperWidget.isFullscreenMode) { - if(!this._updateTimeTimeout) - this._setUpdateTimeInterval(); - } - else if(this._updateTimeTimeout) - this._clearTimeout('updateTime'); - - 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.posX = posX; - this.posY = posY; - } - - _onWidgetDragUpdate(gesture, offsetX, offsetY) - { - if(!this.dragAllowed) - return; - - const clapperWidget = this.widget.get_ancestor(Gtk.Grid); - if(clapperWidget.isFullscreenMode) - return; - - const { gtk_double_click_distance } = this.widget.get_settings(); - - if ( - Math.abs(offsetX) > gtk_double_click_distance - || Math.abs(offsetY) > gtk_double_click_distance - ) { - const [isActive, startX, startY] = gesture.get_start_point(); - if(!isActive) return; - - const native = this.widget.get_native(); - if(!native) return; - - let [isShared, winX, winY] = this.widget.translate_coordinates( - native, startX, startY - ); - if(!isShared) return; - - const [nativeX, nativeY] = native.get_surface_transform(); - winX += nativeX; - winY += nativeY; - - this.isWidgetDragging = true; - native.get_surface().begin_move( - gesture.get_device(), - gesture.get_current_button(), - winX, - winY, - gesture.get_current_event_time() - ); - - gesture.reset(); - } - } - - _onWidgetSwipe(gesture, velocityX, velocityY) - { - if(!this.getIsSwipeOk(velocityX, velocityY)) - return; - - this._onScroll(gesture, -velocityX, 0); - } - - _onWidgetSwipeUpdate(gesture, sequence) - { - const [isCalc, velocityX, velocityY] = gesture.get_velocity(); - if(!isCalc) return; - - if(!this.getIsSwipeOk(velocityY, velocityX)) - return; - - const isIncrease = velocityY < 0; - - this.adjust_volume(isIncrease, 0.01); - } - - _onScroll(controller, dx, dy) - { - const isHorizontal = (Math.abs(dx) >= Math.abs(dy)); - const isIncrease = (isHorizontal) ? dx < 0 : dy < 0; - - if(isHorizontal) { - this.adjust_position(isIncrease); - const { controls } = this.widget.get_ancestor(Gtk.Grid); - const value = Math.round(controls.positionScale.get_value()); - this.seek_seconds(value); - } - else - this.adjust_volume(isIncrease); - - return true; - } - - _onDataDrop(dropTarget, value, x, y) - { - const playlist = value.split(/\r?\n/).filter(uri => { - return Gst.uri_is_valid(uri); - }); - - if(!playlist.length) - return false; - - this.set_playlist(playlist); - - const { application } = this.widget.get_root(); - application.activate(); - - return true; - } - _onCloseRequest(window) { this._performCloseCleanup(window); diff --git a/src/widget.js b/src/widget.js index 23dcef7c..4c21e922 100644 --- a/src/widget.js +++ b/src/widget.js @@ -1,4 +1,4 @@ -const { Gdk, GLib, GObject, GstClapper, Gtk } = imports.gi; +const { Gdk, GLib, GObject, Gst, GstClapper, Gtk } = imports.gi; const { Controls } = imports.src.controls; const Debug = imports.src.debug; const Dialogs = imports.src.dialogs; @@ -20,6 +20,9 @@ class ClapperWidget extends Gtk.Grid * separately as a pre-made GTK widget */ Misc.loadCustomCss(); + this.posX = 0; + this.posY = 0; + this.windowSize = JSON.parse(settings.get_string('window-size')); this.layoutWidth = 0; @@ -27,6 +30,13 @@ class ClapperWidget extends Gtk.Grid this.isSeekable = false; this.isMobileMonitor = false; + this.dragAllowed = false; + this.cursorInPlayer = false; + this.isWidgetDragging = false; + + this._hideControlsTimeout = null; + this._updateTimeTimeout = null; + this.needsTracksUpdate = true; this.overlay = new Gtk.Overlay(); @@ -49,6 +59,8 @@ class ClapperWidget extends Gtk.Grid this.mapSignal = this.connect('map', this._onMap.bind(this)); this.player = new Player(); + const playerWidget = this.player.widget; + this.controls.elapsedButton.scrolledWindow.set_child(this.player.playlistWidget); this.controls.speedAdjustment.bind_property( 'value', this.player, 'rate', GObject.BindingFlags.BIDIRECTIONAL @@ -60,27 +72,34 @@ class ClapperWidget extends Gtk.Grid /* FIXME: re-enable once ported to new GstPlayer API with messages bus */ //this.player.connect('volume-changed', this._onPlayerVolumeChanged.bind(this)); - this.overlay.set_child(this.player.widget); + this.overlay.set_child(playerWidget); this.overlay.add_overlay(this.revealerTop); this.overlay.add_overlay(this.revealerBottom); - const motionController = new Gtk.EventControllerMotion(); - motionController.connect('leave', this._onLeave.bind(this)); - this.add_controller(motionController); + const clickGesture = this._getClickGesture(); + playerWidget.add_controller(clickGesture); + const clickGestureTop = this._getClickGesture(); + this.revealerTop.add_controller(clickGestureTop); - const topClickGesture = new Gtk.GestureClick(); - topClickGesture.set_button(0); - topClickGesture.connect('pressed', this.player._onWidgetPressed.bind(this.player)); - this.revealerTop.add_controller(topClickGesture); + const dragGesture = this._getDragGesture(); + playerWidget.add_controller(dragGesture); + const dragGestureTop = this._getDragGesture(); + this.revealerTop.add_controller(dragGestureTop); - const topMotionController = new Gtk.EventControllerMotion(); - topMotionController.connect('motion', this.player._onWidgetMotion.bind(this.player)); - this.revealerTop.add_controller(topMotionController); + const scrollController = this._getScrollController(); + playerWidget.add_controller(scrollController); + const scrollControllerTop = this._getScrollController(); + this.revealerTop.add_controller(scrollControllerTop); - const topScrollController = new Gtk.EventControllerScroll(); - topScrollController.set_flags(Gtk.EventControllerScrollFlags.BOTH_AXES); - topScrollController.connect('scroll', this.player._onScroll.bind(this.player)); - this.revealerTop.add_controller(topScrollController); + const motionController = this._getMotionController(); + playerWidget.add_controller(motionController); + const motionControllerTop = this._getMotionController(); + this.revealerTop.add_controller(motionControllerTop); + + const dropTarget = this._getDropTarget(); + playerWidget.add_controller(dropTarget); + const dropTargetTop = this._getDropTarget(); + this.revealerTop.add_controller(dropTargetTop); } revealControls(isReveal) @@ -524,17 +543,6 @@ class ClapperWidget extends Gtk.Grid this.controls._onPlayerResize(width, height); } - _onLeave(controller) - { - if( - this.isFullscreenMode - || this.player.isWidgetDragging - ) - return; - - this.revealerBottom.revealChild(false); - } - _onMap() { this.disconnect(this.mapSignal); @@ -562,4 +570,300 @@ class ClapperWidget extends Gtk.Grid surface.connect('notify::state', this._onStateNotify.bind(this)); surface.connect('layout', this._onLayoutUpdate.bind(this)); } + + _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'); + } + + _setHideControlsTimeout() + { + this._clearTimeout('hideControls'); + this._hideControlsTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => { + this._hideControlsTimeout = null; + + if(this.cursorInPlayer) { + const blankCursor = Gdk.Cursor.new_from_name('none', null); + + this.player.widget.set_cursor(blankCursor); + this.revealerTop.set_cursor(blankCursor); + + if(this.isFullscreenMode) + this._clearTimeout('updateTime'); + + this.revealControls(false); + } + + return GLib.SOURCE_REMOVE; + }); + } + + _setUpdateTimeInterval() + { + this._clearTimeout('updateTime'); + + const nextUpdate = this.updateTime(); + + if(nextUpdate === null) + return; + + this._updateTimeTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, nextUpdate, () => { + this._updateTimeTimeout = null; + + if(this.isFullscreenMode) + this._setUpdateTimeInterval(); + + return GLib.SOURCE_REMOVE; + }); + } + + _getClickGesture() + { + const clickGesture = new Gtk.GestureClick(); + clickGesture.set_button(0); + clickGesture.connect('pressed', this._onPressed.bind(this)); + + return clickGesture; + } + + _getDragGesture() + { + const dragGesture = new Gtk.GestureDrag(); + dragGesture.connect('drag-update', this._onDragUpdate.bind(this)); + + return dragGesture; + } + + _getSwipeGesture() + { + const swipeGesture = new Gtk.GestureSwipe({ + touch_only: true, + }); + swipeGesture.connect('swipe', this._onSwipe.bind(this)); + swipeGesture.connect('update', this._onSwipeUpdate.bind(this)); + + return swipeGesture; + } + + _getScrollController() + { + const scrollController = new Gtk.EventControllerScroll(); + scrollController.set_flags(Gtk.EventControllerScrollFlags.BOTH_AXES); + scrollController.connect('scroll', this._onScroll.bind(this)); + + return scrollController; + } + + _getMotionController() + { + const motionController = new Gtk.EventControllerMotion(); + motionController.connect('enter', this._onEnter.bind(this)); + motionController.connect('leave', this._onLeave.bind(this)); + motionController.connect('motion', this._onMotion.bind(this)); + + return motionController; + } + + _getDropTarget() + { + const dropTarget = new Gtk.DropTarget({ + actions: Gdk.DragAction.COPY, + }); + dropTarget.set_gtypes([GObject.TYPE_STRING]); + dropTarget.connect('drop', this._onDataDrop.bind(this)); + + return dropTarget; + } + + _getIsSwipeOk(velocity, otherVelocity) + { + if(!velocity) + return false; + + const absVel = Math.abs(velocity); + + if(absVel < 20 || Math.abs(otherVelocity) * 1.5 >= absVel) + return false; + + return this.isFullscreenMode; + } + + _onPressed(gesture, nPress, x, y) + { + const button = gesture.get_current_button(); + const isDouble = (nPress % 2 == 0); + this.dragAllowed = !isDouble; + + switch(button) { + case Gdk.BUTTON_PRIMARY: + if(isDouble) + this.toggleFullscreen(); + break; + case Gdk.BUTTON_SECONDARY: + this.player.toggle_play(); + break; + default: + break; + } + } + + _onDragUpdate(gesture, offsetX, offsetY) + { + if(!this.dragAllowed || this.isFullscreenMode) + return; + + const { gtk_double_click_distance } = this.get_settings(); + + if ( + Math.abs(offsetX) > gtk_double_click_distance + || Math.abs(offsetY) > gtk_double_click_distance + ) { + const [isActive, startX, startY] = gesture.get_start_point(); + if(!isActive) return; + + const playerWidget = this.player.widget; + + const native = playerWidget.get_native(); + if(!native) return; + + let [isShared, winX, winY] = playerWidget.translate_coordinates( + native, startX, startY + ); + if(!isShared) return; + + const [nativeX, nativeY] = native.get_surface_transform(); + winX += nativeX; + winY += nativeY; + + this.isWidgetDragging = true; + native.get_surface().begin_move( + gesture.get_device(), + gesture.get_current_button(), + winX, + winY, + gesture.get_current_event_time() + ); + + gesture.reset(); + } + } + + _onSwipe(gesture, velocityX, velocityY) + { + if(!this._getIsSwipeOk(velocityX, velocityY)) + return; + + this._onScroll(gesture, -velocityX, 0); + } + + _onSwipeUpdate(gesture, sequence) + { + const [isCalc, velocityX, velocityY] = gesture.get_velocity(); + if(!isCalc) return; + + if(!this._getIsSwipeOk(velocityY, velocityX)) + return; + + const isIncrease = velocityY < 0; + + this.player.adjust_volume(isIncrease, 0.01); + } + + _onScroll(controller, dx, dy) + { + const isHorizontal = (Math.abs(dx) >= Math.abs(dy)); + const isIncrease = (isHorizontal) ? dx < 0 : dy < 0; + + if(isHorizontal) { + this.player.adjust_position(isIncrease); + const value = Math.round(this.controls.positionScale.get_value()); + this.player.seek_seconds(value); + } + else + this.player.adjust_volume(isIncrease); + + return true; + } + + _onEnter(controller, x, y) + { + this.cursorInPlayer = true; + this.isWidgetDragging = false; + + this._setHideControlsTimeout(); + } + + _onLeave(controller) + { + this.cursorInPlayer = false; + this._setHideControlsTimeout(250); + + if( + this.isFullscreenMode + || this.isWidgetDragging + ) + return; + + //this.revealerBottom.revealChild(false); + } + + _onMotion(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 + ) { + const defaultCursor = Gdk.Cursor.new_from_name('default', null); + + this.player.widget.set_cursor(defaultCursor); + this.revealerTop.set_cursor(defaultCursor); + + this._setHideControlsTimeout(); + + if(this.isFullscreenMode) { + if(!this._updateTimeTimeout) + this._setUpdateTimeInterval(); + } + else if(this._updateTimeTimeout) + this._clearTimeout('updateTime'); + + if(!this.revealerTop.get_reveal_child()) { + /* Do not grab controls key focus on mouse movement */ + this.revealerBottom.set_can_focus(false); + this.revealControls(true); + } + } + + this.posX = posX; + this.posY = posY; + } + + _onDataDrop(dropTarget, value, x, y) + { + const playlist = value.split(/\r?\n/).filter(uri => { + return Gst.uri_is_valid(uri); + }); + + if(!playlist.length) + return false; + + this.player.set_playlist(playlist); + this.root.application.activate(); + + return true; + } });