/ *
* Copyright 2016 Smith AR < audoban @ openmailbox . org >
* Michail Vourlakos < mvourlakos @ gmail . com >
*
* This file is part of Latte - Dock
*
* Latte - Dock is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* Latte - Dock is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http: //www.gnu.org/licenses/>.
* /
import QtQuick 2.1
import QtQuick . Window 2.2
import org . kde . plasma . core 2.0 as PlasmaCore
import org . kde . plasma . plasmoid 2.0
import org . kde . latte . core 0.2 as LatteCore
import org . kde . latte . private . containment 0.1 as LatteContainment
Item {
id: manager
anchors.fill: parent
property QtObject window
property bool isFloatingInClientSide: ! root . behaveAsPlasmaPanel
&& screenEdgeMarginEnabled
&& ! root . floatingInternalGapIsForced
&& ! inSlidingIn
&& ! inSlidingOut
property int animationSpeed: LatteCore . WindowSystem . compositingActive ?
( root . editMode ? 400 : animations . speedFactor . current * 1.62 * animations . duration . large ) : 0
property bool inClientSideScreenEdgeSliding: root . behaveAsDockWithMask && hideThickScreenGap
property bool inNormalState: ( ( animations . needBothAxis . count === 0 ) && ( animations . needLength . count === 0 ) )
|| ( latteView && latteView . visibility . isHidden && ! latteView . visibility . containsMouse && animations . needThickness . count === 0 )
property bool inRelocationAnimation: latteView && latteView . positioner && latteView . positioner . inRelocationAnimation
property bool inSlidingIn: false //necessary because of its init structure
property alias inSlidingOut: slidingAnimationAutoHiddenOut . running
property bool inRelocationHiding: false
readonly property bool isSinkedEventEnabled: ! ( parabolic . isEnabled && ( animations . needBothAxis . count > 0 || animations . needLength . count > 0 ) )
&& myView . isShownFully
property int length: root . isVertical ? Screen.height : Screen . width //screenGeometry.height : screenGeometry.width
property int slidingOutToPos: {
if ( root . behaveAsPlasmaPanel ) {
var edgeMargin = screenEdgeMarginEnabled ? plasmoid.configuration.screenEdgeMargin : 0
root . isHorizontal ? root . height + edgeMargin - 1 : root . width + edgeMargin - 1 ;
} else {
var topOrLeftEdge = ( ( plasmoid . location === PlasmaCore . Types . LeftEdge ) || ( plasmoid . location === PlasmaCore . Types . TopEdge ) ) ;
return ( topOrLeftEdge ? - metrics.mask.thickness.normal : metrics . mask . thickness . normal ) ;
}
}
//! when Latte behaves as Plasma panel
property int thicknessAsPanel: metrics . totals . thickness
property Item layouts: null
property bool updateIsEnabled: autosize . inCalculatedIconSize && ! inSlidingIn && ! inSlidingOut && ! inRelocationHiding
Connections {
target: background . totals
onVisualLengthChanged: updateMaskArea ( ) ;
onVisualThicknessChanged: updateMaskArea ( ) ;
}
Connections {
target: background . shadows
onHeadThicknessChanged: updateMaskArea ( ) ;
}
Connections {
target: latteView ? latteView : null
onXChanged: updateMaskArea ( ) ;
onYChanged: updateMaskArea ( )
onWidthChanged: updateMaskArea ( ) ;
onHeightChanged: updateMaskArea ( ) ;
}
Connections {
target: animations . needBothAxis
onCountChanged: updateMaskArea ( ) ;
}
Connections {
target: animations . needLength
onCountChanged: updateMaskArea ( ) ;
}
Connections {
target: animations . needThickness
onCountChanged: updateMaskArea ( ) ;
}
Connections {
target: layoutsManager
onCurrentLayoutIsSwitching: {
if ( LatteCore . WindowSystem . compositingActive && latteView && latteView . layout && latteView . layout . name === layoutName ) {
parabolic . sglClearZoom ( ) ;
}
}
}
Connections {
target: metrics . mask . thickness
onMaxZoomedChanged: updateMaskArea ( )
}
Connections {
target: root . myView
onInRelocationAnimationChanged: {
if ( ! root . myView . inRelocationAnimation ) {
manager . updateMaskArea ( ) ;
}
}
}
Connections {
target: latteView ? latteView.effects : null
onRectChanged: manager . updateMaskArea ( )
}
Connections {
target: themeExtended ? themeExtended : null
onThemeChanged: latteView . effects . forceMaskRedraw ( ) ;
}
Connections {
target: LatteCore . WindowSystem
onCompositingActiveChanged: {
manager . updateMaskArea ( ) ;
}
}
onIsFloatingInClientSideChanged: updateMaskArea ( ) ;
onInNormalStateChanged: {
if ( inNormalState ) {
updateMaskArea ( ) ;
}
}
onInSlidingInChanged: {
if ( latteView && ! inSlidingIn && latteView . positioner . inRelocationShowing ) {
latteView . positioner . inRelocationShowing = false ;
}
}
onUpdateIsEnabledChanged: {
if ( updateIsEnabled ) {
updateMaskArea ( ) ;
}
}
function slotContainsMouseChanged ( ) {
if ( latteView . visibility . containsMouse && latteView . visibility . mode !== LatteCore . Types . SidebarOnDemand ) {
updateMaskArea ( ) ;
if ( slidingAnimationAutoHiddenOut . running && ! inRelocationHiding ) {
slotMustBeShown ( ) ;
}
}
}
function slotMustBeShown ( ) {
//! WindowsCanCover case
if ( latteView && latteView . visibility . mode === LatteCore . Types . WindowsCanCover ) {
latteView . visibility . setViewOnFrontLayer ( ) ;
return ;
}
if ( ! latteView . visibility . isHidden && latteView . positioner . inSlideAnimation ) {
// Do not update when Positioner mid-slide animation takes place, for example:
// 1. Latte panel is hiding its floating gap for maximized window
// 2. the user clicks on an applet popup.
// 3. Applet popups showing/hiding are triggering hidingIsBlockedChanged() signals.
// 4. hidingIsBlockedChanged() signals create mustBeShown events when visibility::hidingIsBlocked() is not enabled.
return ;
}
//! Normal Dodge/AutoHide case
if ( ! slidingAnimationAutoHiddenIn . running
&& ! inRelocationHiding
&& ( latteView . visibility . isHidden || slidingAnimationAutoHiddenOut . running /*it is not already shown or is trying to hide*/ ) ) {
slidingAnimationAutoHiddenIn . init ( ) ;
}
}
function slotMustBeHide ( ) {
if ( inSlidingIn && ! inRelocationHiding ) {
/*consider hiding after sliding in has finished*/
return ;
}
if ( latteView && latteView . visibility . mode === LatteCore . Types . WindowsCanCover ) {
latteView . visibility . setViewOnBackLayer ( ) ;
return ;
}
//! Normal Dodge/AutoHide case
if ( ! slidingAnimationAutoHiddenOut . running
&& ! latteView . visibility . blockHiding
&& ( ! latteView . visibility . containsMouse || latteView . visibility . mode === LatteCore . Types . SidebarOnDemand /*for SidebarOnDemand mouse should be ignored on hiding*/ )
&& ( ! latteView . visibility . isHidden || slidingAnimationAutoHiddenIn . running /*it is not already hidden or is trying to show*/ ) ) {
slidingAnimationAutoHiddenOut . init ( ) ;
}
}
//! functions used for sliding out/in during location/screen changes
function slotHideDockDuringLocationChange ( ) {
inRelocationHiding = true ;
if ( ! slidingAnimationAutoHiddenOut . running ) {
slidingAnimationAutoHiddenOut . init ( ) ;
}
}
function slotShowDockAfterLocationChange ( ) {
slidingAnimationAutoHiddenIn . init ( ) ;
}
function sendHideDockDuringLocationChangeFinished ( ) {
latteView . positioner . hidingForRelocationFinished ( ) ;
}
function sendSlidingOutAnimationEnded ( ) {
latteView . visibility . hide ( ) ;
latteView . visibility . isHidden = true ;
if ( debug . maskEnabled ) {
console . log ( "hiding animation ended..." ) ;
}
sendHideDockDuringLocationChangeFinished ( ) ;
}
///test maskArea
function updateMaskArea ( ) {
if ( ! latteView || ! root . viewIsAvailable ) {
return ;
}
var localX = 0 ;
var localY = 0 ;
// debug maskArea criteria
if ( debug . maskEnabled ) {
console . log ( animations . needBothAxis . count + ", " + animations . needLength . count + ", " +
animations . needThickness . count + ", " + latteView . visibility . isHidden ) ;
}
//console.log("reached updating geometry ::: "+dock.maskArea);
if ( ! latteView . visibility . isHidden && updateIsEnabled && inNormalState ) {
//! Important: Local Geometry must not be updated when view ISHIDDEN
//! because it breaks Dodge(s) modes in such case
var localGeometry = Qt . rect ( 0 , 0 , root . width , root . height ) ;
//the shadows size must be removed from the maskArea
//before updating the localDockGeometry
if ( ! latteView . behaveAsPlasmaPanel ) {
var cleanThickness = metrics . totals . thickness ;
var edgeMargin = metrics . mask . screenEdge ;
if ( plasmoid . location === PlasmaCore . Types . TopEdge ) {
localGeometry . x = latteView . effects . rect . x ; // from effects area
localGeometry . width = latteView . effects . rect . width ; // from effects area
localGeometry . y = edgeMargin ;
localGeometry . height = cleanThickness ;
} else if ( plasmoid . location === PlasmaCore . Types . BottomEdge ) {
localGeometry . x = latteView . effects . rect . x ; // from effects area
localGeometry . width = latteView . effects . rect . width ; // from effects area
localGeometry . y = root . height - cleanThickness - edgeMargin ;
localGeometry . height = cleanThickness ;
} else if ( plasmoid . location === PlasmaCore . Types . LeftEdge ) {
localGeometry . y = latteView . effects . rect . y ; // from effects area
localGeometry . height = latteView . effects . rect . height ; // from effects area
localGeometry . x = edgeMargin ;
localGeometry . width = cleanThickness ;
} else if ( plasmoid . location === PlasmaCore . Types . RightEdge ) {
localGeometry . y = latteView . effects . rect . y ; // from effects area
localGeometry . height = latteView . effects . rect . height ; // from effects area
localGeometry . x = root . width - cleanThickness - edgeMargin ;
localGeometry . width = cleanThickness ;
}
//set the boundaries for latteView local geometry
//qBound = qMax(min, qMin(value, max)).
localGeometry . x = Math . max ( 0 , Math . min ( localGeometry . x , latteView . width ) ) ;
localGeometry . y = Math . max ( 0 , Math . min ( localGeometry . y , latteView . height ) ) ;
localGeometry . width = Math . min ( localGeometry . width , latteView . width ) ;
localGeometry . height = Math . min ( localGeometry . height , latteView . height ) ;
}
//console.log("update geometry ::: "+localGeometry);
latteView . localGeometry = localGeometry ;
}
//! Input Mask
if ( updateIsEnabled ) {
var animated = ( animations . needBothAxis . count > 0 ) ;
if ( ! LatteCore . WindowSystem . compositingActive || animated || latteView . behaveAsPlasmaPanel ) {
//! clear input mask
latteView . effects . inputMask = Qt . rect ( 0 , 0 , - 1 , - 1 ) ;
} else {
var floatingInternalGapAcceptsInput = behaveAsDockWithMask && floatingInternalGapIsForced ;
var inputThickness ;
if ( latteView . visibility . isHidden ) {
inputThickness = metrics . mask . thickness . hidden ;
} else if ( root . hasFloatingGapInputEventsDisabled ) {
inputThickness = metrics . totals . thickness ;
} else {
inputThickness = metrics . mask . screenEdge + metrics . totals . thickness ;
}
var subtractedScreenEdge = root . hasFloatingGapInputEventsDisabled && ! latteView . visibility . isHidden ? metrics.mask.screenEdge : 0 ;
var inputGeometry = Qt . rect ( 0 , 0 , root . width , root . height ) ;
//!use view.localGeometry for length properties
if ( plasmoid . location === PlasmaCore . Types . TopEdge ) {
inputGeometry . x = latteView . localGeometry . x ;
inputGeometry . y = subtractedScreenEdge ;
inputGeometry . width = latteView . localGeometry . width ;
inputGeometry . height = inputThickness ;
} else if ( plasmoid . location === PlasmaCore . Types . BottomEdge ) {
inputGeometry . x = latteView . localGeometry . x ;
inputGeometry . y = root . height - inputThickness - subtractedScreenEdge ;
inputGeometry . width = latteView . localGeometry . width ;
inputGeometry . height = inputThickness ;
} else if ( plasmoid . location === PlasmaCore . Types . LeftEdge ) {
inputGeometry . x = subtractedScreenEdge ;
inputGeometry . y = latteView . localGeometry . y ;
inputGeometry . width = inputThickness ;
inputGeometry . height = latteView . localGeometry . height ;
} else if ( plasmoid . location === PlasmaCore . Types . RightEdge ) {
inputGeometry . x = root . width - inputThickness - subtractedScreenEdge ;
inputGeometry . y = latteView . localGeometry . y ;
inputGeometry . width = inputThickness ;
inputGeometry . height = latteView . localGeometry . height ;
}
//set the boundaries for latteView local geometry
//qBound = qMax(min, qMin(value, max)).
inputGeometry . x = Math . max ( 0 , Math . min ( inputGeometry . x , latteView . width ) ) ;
inputGeometry . y = Math . max ( 0 , Math . min ( inputGeometry . y , latteView . height ) ) ;
inputGeometry . width = Math . min ( inputGeometry . width , latteView . width ) ;
inputGeometry . height = Math . min ( inputGeometry . height , latteView . height ) ;
if ( latteView . visibility . isSidebar ( ) && latteView . visibility . isHidden ) {
//! this way we make sure than no input is accepted anywhere
inputGeometry = Qt . rect ( - 1 , - 1 , 1 , 1 ) ;
}
latteView . effects . inputMask = inputGeometry ;
}
}
}
Loader {
anchors.fill: parent
active: debug . graphicsEnabled
sourceComponent: Item {
anchors.fill: parent
Rectangle {
id: windowBackground
anchors.fill: parent
border.color: "red"
border.width: 1
color: "transparent"
}
Rectangle {
x: latteView ? latteView.effects.mask.x : - 1
y: latteView ? latteView.effects.mask.y : - 1
height: latteView ? latteView.effects.mask.height : 0
width: latteView ? latteView.effects.mask.width : 0
border.color: "green"
border.width: 1
color: "transparent"
}
}
}
/***Hiding/Showing Animations*****/
//////////////// Animations - Slide In - Out
SequentialAnimation {
id: slidingAnimationAutoHiddenOut
PropertyAnimation {
target: ! root . behaveAsPlasmaPanel ? layoutsContainer : latteView . positioner
property: ! root . behaveAsPlasmaPanel ? ( root . isVertical ? "x" : "y" ) : "slideOffset"
to: {
if ( root . behaveAsPlasmaPanel ) {
return slidingOutToPos ;
}
if ( LatteCore . WindowSystem . compositingActive ) {
return slidingOutToPos ;
} else {
if ( ( plasmoid . location === PlasmaCore . Types . LeftEdge ) || ( plasmoid . location === PlasmaCore . Types . TopEdge ) ) {
return slidingOutToPos + 1 ;
} else {
return slidingOutToPos - 1 ;
}
}
}
duration: manager . animationSpeed
easing.type: Easing . InQuad
}
ScriptAction {
script: {
latteView . visibility . isHidden = true ;
if ( root . behaveAsPlasmaPanel && latteView . positioner . slideOffset !== 0 ) {
//! hide real panels when they slide-out
latteView . visibility . hide ( ) ;
}
}
}
onStarted: {
if ( debug . maskEnabled ) {
console . log ( "hiding animation started..." ) ;
}
}
onStopped: {
//! Trying to move the ending part of the signals at the end of editing animation
if ( ! manager . inRelocationHiding ) {
manager . updateMaskArea ( ) ;
} else {
if ( ! root . editMode ) {
manager . sendSlidingOutAnimationEnded ( ) ;
}
}
latteView . visibility . slideOutFinished ( ) ;
}
function init ( ) {
if ( manager . inRelocationAnimation || ! latteView . visibility . blockHiding ) {
start ( ) ;
}
}
}
SequentialAnimation {
id: slidingAnimationAutoHiddenIn
PauseAnimation {
duration: manager . inRelocationHiding && animations . active ? 500 : 0
}
PropertyAnimation {
target: ! root . behaveAsPlasmaPanel ? layoutsContainer : latteView . positioner
property: ! root . behaveAsPlasmaPanel ? ( root . isVertical ? "x" : "y" ) : "slideOffset"
to: 0
duration: manager . animationSpeed
easing.type: Easing . OutQuad
}
ScriptAction {
script: {
// deprecated
// root.inStartup = false;
}
}
onStarted: {
latteView . visibility . show ( ) ;
if ( debug . maskEnabled ) {
console . log ( "showing animation started..." ) ;
}
}
onStopped: {
inSlidingIn = false ;
if ( manager . inRelocationHiding ) {
manager . inRelocationHiding = false ;
autosize . updateIconSize ( ) ;
}
manager . inRelocationHiding = false ;
autosize . updateIconSize ( ) ;
if ( debug . maskEnabled ) {
console . log ( "showing animation ended..." ) ;
}
latteView . visibility . slideInFinished ( ) ;
//! this is needed in order to update dock absolute geometry correctly in the end AND
//! when a floating dock is sliding-in through masking techniques
updateMaskArea ( ) ;
}
function init ( ) {
if ( ! root . viewIsAvailable ) {
return ;
}
inSlidingIn = true ;
if ( slidingAnimationAutoHiddenOut . running ) {
slidingAnimationAutoHiddenOut . stop ( ) ;
}
latteView . visibility . isHidden = false ;
updateMaskArea ( ) ;
start ( ) ;
}
}
//! Slides Animations for FLOATING+BEHAVEASPLASMAPANEL when
//! HIDETHICKSCREENCAP dynamically is enabled/disabled
SequentialAnimation {
id: slidingInRealFloating
PropertyAnimation {
target: latteView ? latteView.positioner : null
property: "slideOffset"
to: 0
duration: manager . animationSpeed
easing.type: Easing . OutQuad
}
ScriptAction {
script: {
latteView . positioner . inSlideAnimation = false ;
}
}
onStopped: latteView . positioner . inSlideAnimation = false ;
}
SequentialAnimation {
id: slidingOutRealFloating
ScriptAction {
script: {
latteView . positioner . inSlideAnimation = true ;
}
}
PropertyAnimation {
target: latteView ? latteView.positioner : null
property: "slideOffset"
to: plasmoid . configuration . screenEdgeMargin
duration: manager . animationSpeed
easing.type: Easing . InQuad
}
}
Connections {
target: root
onHideThickScreenGapChanged: {
if ( ! latteView || ! root . viewIsAvailable ) {
return ;
}
if ( root . behaveAsPlasmaPanel && ! latteView . visibility . isHidden && ! inSlidingIn && ! inSlidingOut && ! inStartup ) {
slideInOutRealFloating ( ) ;
}
}
onInStartupChanged: {
//! used for positioning properly real floating panels when there is a maximized window
if ( root . hideThickScreenGap && ! inStartup && latteView . positioner . slideOffset === 0 ) {
if ( root . behaveAsPlasmaPanel && ! latteView . visibility . isHidden ) {
slideInOutRealFloating ( ) ;
}
}
}
function slideInOutRealFloating ( ) {
if ( root . hideThickScreenGap ) {
slidingInRealFloating . stop ( ) ;
slidingOutRealFloating . start ( ) ;
} else {
slidingOutRealFloating . stop ( ) ;
slidingInRealFloating . start ( ) ;
}
}
}
}