introduce AutomaticItemSizer and add protector

--the automatic item sizer algorithm now is present
in its own class/responsibility area. As a new
improvement/fix the new implementation provides also
a protector/tracker that when the current prediction
to grow has already be applied two steps back in
history then the growing is not applied. This way
endless loops with growings and shrinks are blocked.

BUG:414180
FIXED-IN:0.9.5
pull/16/head
Michail Vourlakos 5 years ago
parent b31bf99673
commit 9739c43674

@ -0,0 +1,234 @@
/*
* Copyright 2019 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.8
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.plasmoid 2.0
import org.kde.latte 0.2 as Latte
Item {
id: sizer
// when there are only plasma style task managers OR any applets that fill width or height
// the automatic icon size algorithm should better be disabled
readonly property bool isActive: !root.containsOnlyPlasmaTasks && layoutsContainer.fillApplets<=0
property bool automaticSizeAnimation: false
readonly property int automaticStep: 8
readonly property int historyMaxSize: 10
readonly property int historyMinSize: 4
property int automaticIconSizeBasedSize: -1 //it is not set, this is the default
//! Prediction History of the algorithm in order to track cases where the algorithm produces
//! grows and shrinks endlessly
property variant history: []
onAutomaticIconSizeBasedSizeChanged: {
if (!automaticSizeAnimation) {
automaticSizeAnimation = true;
root.slotAnimationsNeedBothAxis(1);
}
}
onIsActiveChanged: clearHistory();
Connections {
target: root
onContainsOnlyPlasmaTasksChanged: sizer.updateAutomaticIconSize();
onEditModeChanged: sizer.updateAutomaticIconSize();
onMaxLengthChanged: {
if (root.editMode) {
sizer.updateAutomaticIconSize();
}
}
onProportionIconSizeChanged: {
if (root.proportionIconSize!==-1) {
sizer.updateAutomaticIconSize();
}
}
onIconSizeChanged: {
if (((root.iconSize === sizer.automaticIconSizeBasedSize) || (root.iconSize === root.maxIconSize)) && sizer.automaticSizeAnimation){
root.slotAnimationsNeedBothAxis(-1);
sizer.automaticSizeAnimation=false;
}
}
}
Connections {
target: latteView
onWidthChanged:{
if (root.isHorizontal && root.proportionIconSize!==-1) {
sizer.updateAutomaticIconSize();
}
}
onHeightChanged:{
if (root.isVertical && root.proportionIconSize!==-1) {
sizer.updateAutomaticIconSize();
}
}
}
//! Prediction History Functions
function clearHistory() {
history.length = 0;
}
function addPrediction(currentLength, prediction) {
history.unshift({current: currentLength, predicted: prediction});
/* console.log(" -- PREDICTION ARRAY -- ");
for(var i=0; i<history.length; ++i) {
console.log( i + ". " + history[i].current + " : " + history[i].predicted);
}*/
if (history.length > historyMaxSize) {
history.splice(historyMinSize, history.length - historyMinSize);
}
}
function producesEndlessLoop(currentLength, prediction) {
if (history.length < 2) {
return false;
}
if (history[1].current === currentLength
&& history[1].predicted === prediction) {
//! the current prediction is the same like two steps before in prediction history
if(history[0].current > history[0].predicted && history[1].current < history[1].predicted) {
//! case2: the algorithm that is trying to SHRINK has already produced same results subsequently
console.log(" AUTOMATIC ITEM SIZE PROTECTOR, :: ENDLESS AUTOMATIC SIZE LOOP DETECTED");
return true;
}
}
return false;
}
function updateAutomaticIconSize() {
if ( !doubleCallAutomaticUpdateIconSize.running && !visibilityManager.inTempHiding
&& ((visibilityManager.normalState || root.editMode)
&& (sizer.isActive || (!sizer.isActive && root.iconSize!==root.maxIconSize)))
&& (root.iconSize===root.maxIconSize || root.iconSize === sizer.automaticIconSizeBasedSize) ) {
//!doubler timer
if (!doubleCallAutomaticUpdateIconSize.secondTimeCallApplied) {
doubleCallAutomaticUpdateIconSize.start();
} else {
doubleCallAutomaticUpdateIconSize.secondTimeCallApplied = false;
}
var layoutLength;
var maxLength = root.maxLength;
//console.log("------Entered check-----");
//console.log("max length: "+ maxLength);
if (root.isVertical) {
layoutLength = (plasmoid.configuration.panelPosition === Latte.Types.Justify) ?
layoutsContainer.startLayout.height+layoutsContainer.mainLayout.height+layoutsContainer.endLayout.height : layoutsContainer.mainLayout.height
} else {
layoutLength = (plasmoid.configuration.panelPosition === Latte.Types.Justify) ?
layoutsContainer.startLayout.width+layoutsContainer.mainLayout.width+layoutsContainer.endLayout.width : layoutsContainer.mainLayout.width
}
var toShrinkLimit = maxLength-((1+(root.zoomFactor-1))*(root.iconSize + lengthMargins));
var toGrowLimit = maxLength-((1+(root.zoomFactor-1))*(root.iconSize + lengthMargins));
//console.log("toShrinkLimit: "+ toShrinkLimit);
//console.log("toGrowLimit: "+ toGrowLimit);
var newIconSizeFound = false;
if (layoutLength > toShrinkLimit) { //must shrink
// console.log("step3");
var nextIconSize = root.maxIconSize;
do {
nextIconSize = nextIconSize - automaticStep;
var factor = nextIconSize / root.iconSize;
var nextLength = factor * layoutLength;
} while ( (nextLength>toShrinkLimit) && (nextIconSize !== 16));
var intLength = Math.round(layoutLength);
var intNextLength = Math.round(nextLength);
automaticIconSizeBasedSize = nextIconSize;
newIconSizeFound = true;
addPrediction(intLength, intNextLength);
// console.log("Step 3 - found:"+automaticIconSizeBasedSize);
} else if ((layoutLength<toGrowLimit
&& (root.iconSize === automaticIconSizeBasedSize)) ) { //must grow probably
// console.log("step4");
var nextIconSize2 = automaticIconSizeBasedSize;
var foundGoodSize = -1;
do {
nextIconSize2 = nextIconSize2 + automaticStep;
var factor2 = nextIconSize2 / automaticIconSizeBasedSize;
var nextLength2 = factor2 * layoutLength;
if (nextLength2 < toGrowLimit) {
foundGoodSize = nextIconSize2;
}
} while ( (nextLength2<toGrowLimit) && (nextIconSize2 !== root.maxIconSize ));
var intLength2 = Math.round(layoutLength);
var intNextLength2 = Math.round(nextLength2);
if (foundGoodSize > 0 && !producesEndlessLoop(intLength2, intNextLength2)) {
if (foundGoodSize === root.maxIconSize) {
automaticIconSizeBasedSize = -1;
} else {
automaticIconSizeBasedSize = foundGoodSize;
}
newIconSizeFound = true
addPrediction(intLength2, intNextLength2);
// console.log("Step 4 - found:"+automaticIconSizeBasedSize);
} else {
// console.log("Step 4 - did not found...");
}
}
}
}
//! This functions makes sure to call the updateAutomaticIconSize(); function which is costly
//! one more time after its last call to confirm the applied icon size found
Timer{
id:doubleCallAutomaticUpdateIconSize
interval: 1000
property bool secondTimeCallApplied: false
onTriggered: {
if (!secondTimeCallApplied) {
secondTimeCallApplied = true;
sizer.updateAutomaticIconSize();
}
}
}
}

@ -418,7 +418,7 @@ Window{
}
Text{
text: root.automaticIconSizeBasedSize
text: automaticItemSizer.automaticIconSizeBasedSize
}
Text{

@ -109,7 +109,7 @@ Item{
value: root.behaveAsPlasmaPanel && !root.editMode ? thicknessAsPanel : thicknessZoomOriginal
}
property bool validIconSize: (root.iconSize===root.maxIconSize || root.iconSize === root.automaticIconSizeBasedSize)
property bool validIconSize: (root.iconSize===root.maxIconSize || root.iconSize === automaticItemSizer.automaticIconSizeBasedSize)
property bool inPublishingState: validIconSize && !inSlidingIn && !inSlidingOut && !inTempHiding && !inForceHiding
Binding{
@ -313,7 +313,7 @@ Item{
onNormalStateChanged: {
if (normalState) {
root.updateAutomaticIconSize();
automaticItemSizer.updateAutomaticIconSize();
root.updateSizeForAppletsInFill();
}
}
@ -598,7 +598,7 @@ Item{
}
}
var validIconSize = (root.iconSize===root.maxIconSize || root.iconSize === root.automaticIconSizeBasedSize);
var validIconSize = (root.iconSize===root.maxIconSize || root.iconSize === automaticItemSizer.automaticIconSizeBasedSize);
//console.log("reached updating geometry ::: "+dock.maskArea);
if(inPublishingState && (normalState || root.editMode)) {
@ -759,11 +759,11 @@ Item{
if (manager.inTempHiding) {
manager.inTempHiding = false;
updateAutomaticIconSize();
automaticItemSizer.updateAutomaticIconSize();
}
manager.inTempHiding = false;
updateAutomaticIconSize();
automaticItemSizer.updateAutomaticIconSize();
if (manager.debugMagager) {
console.log("showing animation ended...");

@ -380,7 +380,7 @@ Item{
editVisual.editAnimationEnded = true;
editVisual.editAnimationInFullThickness = true;
updateEffectsArea();
updateAutomaticIconSize();
automaticItemSizer.updateAutomaticIconSize();
visibilityManager.updateMaskArea();
}
}

@ -123,7 +123,7 @@ Item{
}
if (latteView && ((contentsWidth >= root.maxLength) || firstHalfExited || secondHalfExited)) {
updateAutomaticIconSize();
automaticItemSizer.updateAutomaticIconSize();
}
if (!animationSent) {
@ -148,7 +148,7 @@ Item{
}
if (latteView && ((contentsHeight >= root.maxLength) || firstHalfExited || secondHalfExited)) {
updateAutomaticIconSize();
automaticItemSizer.updateAutomaticIconSize();
}
if (!animationSent) {

@ -73,9 +73,6 @@ Item {
property bool addLaunchersMessage: false
property bool addLaunchersInTaskManager: plasmoid.configuration.addLaunchersInTaskManager
// when there are only plasma style task managers OR any applets that fill width or height
// the automatic icon size algorithm should better be disabled
property bool autoDecreaseIconSize: !containsOnlyPlasmaTasks && layoutsContainer.fillApplets<=0
property bool backgroundOnlyOnMaximized: plasmoid.configuration.backgroundOnlyOnMaximized
property bool behaveAsPlasmaPanel: {
if (!latteView || !latteView.visibility) {
@ -238,12 +235,10 @@ Item {
property int appletsNeedWindowsTracking: 0
property int automaticIconSizeBasedSize: -1 //it is not set, this is the defautl
//what is the highest icon size based on what icon size is used, screen calculated or user specified
property int maxIconSize: proportionIconSize!==-1 ? proportionIconSize : plasmoid.configuration.iconSize
property int iconSize: automaticIconSizeBasedSize > 0 && autoDecreaseIconSize ?
Math.min(automaticIconSizeBasedSize, root.maxIconSize) :
property int iconSize: automaticItemSizer.automaticIconSizeBasedSize > 0 && automaticItemSizer.isActive ?
Math.min(automaticItemSizer.automaticIconSizeBasedSize, root.maxIconSize) :
root.maxIconSize
property int proportionIconSize: { //icon size based on screen height
@ -253,7 +248,6 @@ Item {
return Math.max(16,Math.round(latteView.screenGeometry.height * plasmoid.configuration.proportionIconSize/100/8)*8);
}
property int iconStep: 8
property int latteAppletPos: -1
property int maxLength: {
if (root.isHorizontal) {
@ -334,8 +328,6 @@ Item {
property string appShadowColorSolid: "#" + appChosenShadowColor
property int totalPanelEdgeSpacing: 0 //this is set by PanelBox
//FIXME: this is not needed any more probably
property int previousAllTasks: -1 //is used to forbid updateAutomaticIconSize when hovering
property int offset: {
if (behaveAsPlasmaPanel) {
return 0;
@ -644,15 +636,11 @@ Item {
//// END OF Behaviors
//////////////START OF CONNECTIONS
onContainsOnlyPlasmaTasksChanged: updateAutomaticIconSize();
onEditModeChanged: {
if (editMode) {
visibilityManager.updateMaskArea();
updateAutomaticIconSize();
clearZoom();
} else {
updateAutomaticIconSize();
layoutsContainer.updateSizeForAppletsInFill();
}
@ -732,9 +720,6 @@ Item {
onMaxLengthChanged: {
layoutsContainer.updateSizeForAppletsInFill();
if (root.editMode) {
updateAutomaticIconSize();
}
}
onToolBoxChanged: {
@ -743,21 +728,6 @@ Item {
}
}
property bool automaticSizeAnimation: false;
onAutomaticIconSizeBasedSizeChanged: {
if (!automaticSizeAnimation) {
automaticSizeAnimation = true;
slotAnimationsNeedBothAxis(1);
}
}
onIconSizeChanged: {
if (((iconSize === automaticIconSizeBasedSize) || (iconSize === root.maxIconSize)) && automaticSizeAnimation){
slotAnimationsNeedBothAxis(-1);
automaticSizeAnimation=false;
}
}
onIsReadyChanged: {
if (isReady && !titleTooltipDialog.visible && titleTooltipDialog.activeItemHovered){
titleTooltipDialog.show(titleTooltipDialog.activeItem, titleTooltipDialog.activeItemText);
@ -778,11 +748,6 @@ Item {
}
}
onProportionIconSizeChanged: {
if (proportionIconSize!==-1)
updateAutomaticIconSize();
}
// onIconSizeChanged: console.log("Icon Size Changed:"+iconSize);
Component.onCompleted: {
@ -1376,85 +1341,6 @@ Item {
}
}
function updateAutomaticIconSize() {
if ( !doubleCallAutomaticUpdateIconSize.running && !visibilityManager.inTempHiding
&& ((visibilityManager.normalState || root.editMode)
&& (root.autoDecreaseIconSize || (!root.autoDecreaseIconSize && root.iconSize!=root.maxIconSize)))
&& (iconSize===root.maxIconSize || iconSize === automaticIconSizeBasedSize) ) {
//!doubler timer
if (!doubleCallAutomaticUpdateIconSize.secondTimeCallApplied) {
doubleCallAutomaticUpdateIconSize.start();
} else {
doubleCallAutomaticUpdateIconSize.secondTimeCallApplied = false;
}
var layoutLength;
var maxLength = root.maxLength;
//console.log("------Entered check-----");
//console.log("max length: "+ maxLength);
if (root.isVertical) {
layoutLength = (plasmoid.configuration.panelPosition === Latte.Types.Justify) ?
layoutsContainer.startLayout.height+layoutsContainer.mainLayout.height+layoutsContainer.endLayout.height : layoutsContainer.mainLayout.height
} else {
layoutLength = (plasmoid.configuration.panelPosition === Latte.Types.Justify) ?
layoutsContainer.startLayout.width+layoutsContainer.mainLayout.width+layoutsContainer.endLayout.width : layoutsContainer.mainLayout.width
}
var toShrinkLimit = maxLength-((1+(root.zoomFactor-1))*(iconSize + lengthMargins));
var toGrowLimit = maxLength-((1+(root.zoomFactor-1))*(iconSize + lengthMargins));
//console.log("toShrinkLimit: "+ toShrinkLimit);
//console.log("toGrowLimit: "+ toGrowLimit);
var newIconSizeFound = false;
if (layoutLength > toShrinkLimit) { //must shrink
// console.log("step3");
var nextIconSize = root.maxIconSize;
do {
nextIconSize = nextIconSize - iconStep;
var factor = nextIconSize / iconSize;
var nextLength = factor * layoutLength;
} while ( (nextLength>toShrinkLimit) && (nextIconSize !== 16));
automaticIconSizeBasedSize = nextIconSize;
newIconSizeFound = true;
// console.log("Step 3 - found:"+automaticIconSizeBasedSize);
} else if ((layoutLength<toGrowLimit
&& (iconSize === automaticIconSizeBasedSize)) ) { //must grow probably
// console.log("step4");
var nextIconSize2 = automaticIconSizeBasedSize;
var foundGoodSize = -1;
do {
nextIconSize2 = nextIconSize2 + iconStep;
var factor2 = nextIconSize2 / automaticIconSizeBasedSize;
var nextLength2 = factor2 * layoutLength;
if (nextLength2 < toGrowLimit) {
foundGoodSize = nextIconSize2;
}
} while ( (nextLength2<toGrowLimit) && (nextIconSize2 !== root.maxIconSize ));
if (foundGoodSize > 0) {
if (foundGoodSize === root.maxIconSize) {
automaticIconSizeBasedSize = -1;
} else {
automaticIconSizeBasedSize = foundGoodSize;
}
newIconSizeFound = true
// console.log("Step 4 - found:"+automaticIconSizeBasedSize);
} else {
// console.log("Step 4 - did not found...");
}
}
}
}
function updateContainsOnlyPlasmaTasks() {
if (latteView) {
root.containsOnlyPlasmaTasks = (latteView.tasksPresent() && !latteApplet);
@ -1537,15 +1423,6 @@ Item {
Connections {
target: latteView
onWidthChanged:{
if (root.isHorizontal && proportionIconSize!==-1)
updateAutomaticIconSize();
}
onHeightChanged:{
if (root.isVertical && proportionIconSize!==-1)
updateAutomaticIconSize();
}
onContextMenuIsShownChanged: {
if (!latteView.contextMenuIsShown) {
@ -1797,6 +1674,10 @@ Item {
}
}
AutomaticItemSizer {
id: automaticItemSizer
}
VisibilityManager{ id: visibilityManager }
DragDropArea {
@ -1915,21 +1796,6 @@ Item {
}
}
//! This functions makes sure to call the updateAutomaticIconSize(); function which is costly
//! one more time after its last call to confirm the applied icon size found
Timer{
id:doubleCallAutomaticUpdateIconSize
interval: 1000
property bool secondTimeCallApplied: false
onTriggered: {
if (!secondTimeCallApplied) {
secondTimeCallApplied = true;
root.updateAutomaticIconSize();
}
}
}
//! It is used in order to slide-in the latteView on startup
Timer{
id: inStartupTimer

Loading…
Cancel
Save