Skip to content

Commit b2d6e94

Browse files
committed
feat!: create widget islands using another widget as the separator
Replaces manual configuration of islands with a user selectable widget to be used as islands separator by positioning it around other widgets Just like before it loops through the widgets in the panel and defines the type for each segment (single, start/middle/end of island) Also handles more cases of blacklisted and hidden widgets by shrinking or breaking islands into multiple ones when possible refs: #88 BREAKING CHANGE: Islands configuration has been reworked, islands are now created by specifying a widget that will act as the separator and adding/positioning them around other widgets. Previously configured islands need to be recreated using the new method.
1 parent 34e5bf8 commit b2d6e94

45 files changed

Lines changed: 471 additions & 410 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package/contents/config/main.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,25 @@
132132
type="Bool">
133133
<default>true</default>
134134
</entry>
135+
<entry
136+
name="islandSeparatorPairing"
137+
type="Bool">
138+
<default>false</default>
139+
</entry>
140+
<entry
141+
name="islandSeparatorWidget"
142+
type="String">
143+
<default>org.kde.plasma.panelspacer</default>
144+
</entry>
145+
<entry
146+
name="islandsEnabled"
147+
type="Bool">
148+
<default>true</default>
149+
</entry>
150+
<entry
151+
name="blacklistIslandSeparator"
152+
type="Bool">
153+
<default>true</default>
154+
</entry>
135155
</group>
136156
</kcfg>

package/contents/ui/code/enum.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ const WidgetStates = {
1111
Hovered: 3,
1212
Expanded: 4,
1313
};
14+
15+
const IslandSectionType = {
16+
Default: 0,
17+
Start: 1,
18+
Middle: 2,
19+
End: 3
20+
};

package/contents/ui/code/globals.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,6 @@ const defaultConfig = {
341341
overrides: {},
342342
associations: [],
343343
},
344-
unifiedBackground: [],
345344
};
346345

347346
const ignoredConfigs = [
@@ -369,6 +368,11 @@ const ignoredConfigs = [
369368
"systemTrayIconUserReplacements",
370369
"systemTrayIconBuiltinReplacementsEnabled",
371370
"logSystemTrayIconChanges",
371+
"islandSeparatorPairing",
372+
"islandSeparatorWidget",
373+
"islandSeparatorWidget",
374+
"islandsEnabled",
375+
"blacklistIslandSeparator"
372376
];
373377

374378
const editModeGridSettings = {

package/contents/ui/code/utils.js

Lines changed: 162 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -582,69 +582,6 @@ function getGlobalPosition(rect, panelElement) {
582582
return rect.mapToItem(panelElement, 0, 0, rect.width, rect.height);
583583
}
584584

585-
function getUnifyBgTypes(itemTypes) {
586-
// 0: default or middle | 1: start | 2: end
587-
let areas = [[]];
588-
for (let i = 0; i < itemTypes.length; i++) {
589-
const item = itemTypes[i];
590-
if (!item) continue;
591-
592-
areas[areas.length - 1].push(item);
593-
if (
594-
(itemTypes.length > i + 1 && itemTypes[i + 1]?.type === 1) ||
595-
itemTypes[i]?.type === 3
596-
) {
597-
areas[areas.length] = [];
598-
}
599-
}
600-
// remove invalid areas
601-
areas = removeInvalidAreas(areas);
602-
areas.forEach((area) => shrinkAreaIfNeeded(area));
603-
// shrinking may leave invalid areas
604-
areas = removeInvalidAreas(areas);
605-
areas = setMiddleAreaType(areas);
606-
return Array.prototype.concat.apply([], areas);
607-
}
608-
609-
function setMiddleAreaType(areas) {
610-
areas.forEach((area) => {
611-
return area.forEach((item, index) => {
612-
if (index !== area.length - 1 && index !== 0) {
613-
item.type = 2;
614-
}
615-
});
616-
});
617-
return areas;
618-
}
619-
620-
function removeInvalidAreas(areas) {
621-
return areas.filter((area) => {
622-
return (
623-
area.length > 1 && area[0].type === 1 && area[area.length - 1].type === 3
624-
);
625-
});
626-
}
627-
628-
function shrinkAreaIfNeeded(area) {
629-
let lo = 0;
630-
let hi = area.length - 1;
631-
while (lo < hi) {
632-
if (!area[0].visible) {
633-
// shift start to next item
634-
area[1].type = area[0].type;
635-
area.shift();
636-
}
637-
if (!area[area.length - 1].visible) {
638-
// shift end to next item
639-
area[area.length - 2].type = area[area.length - 1].type;
640-
area.pop();
641-
}
642-
lo++;
643-
hi--;
644-
}
645-
return area;
646-
}
647-
648585
function setPanelModeScript(panelId, panelSettings) {
649586
var setPanelModeScript = `
650587
var panel = panelById(${panelId});
@@ -686,15 +623,6 @@ function clearOldWidgetConfig(config) {
686623
} else return [];
687624
}
688625

689-
function fixV2UnifiedWidgetConfig(config) {
690-
config.forEach((widget) => {
691-
if (widget.unifyBgType === 2) {
692-
widget.unifyBgType = 3;
693-
}
694-
});
695-
return config;
696-
}
697-
698626
function getWidgetConfigIdx(id, name, config) {
699627
// console.log("getWidgetConfigIdx()")
700628
return config.findIndex((item) => item.id == id && item.name == name);
@@ -1124,15 +1052,6 @@ function applyFgColor(
11241052
};
11251053
}
11261054

1127-
function updateUnifiedBackgroundTracker(index, type, visible, tracker) {
1128-
tracker[index] = {
1129-
index: index,
1130-
type: type,
1131-
visible: visible,
1132-
};
1133-
return getUnifyBgTypes(tracker);
1134-
}
1135-
11361055
/**
11371056
* Finds and returns an icon from the replacement rules if it matches the tray item properties
11381057
* @param {Array} rules Replacement rules
@@ -1164,3 +1083,165 @@ function getTrayIconFromRules(rules, trayItemProperties) {
11641083
function pointToPixel(pointSize) {
11651084
return Math.round(pointSize / 72 * main.pixelsPerInch);
11661085
}
1086+
1087+
/**
1088+
* Calculates types of each widget to create islands
1089+
* @param {GridLayout} panelLayout - The panel's widgets layout
1090+
* @param {Set<int>} noBgTracker - IDs of widgets with background disabled or blacklisted
1091+
* @param {Set<int>} hiddenTracker - IDs of widgets that are currently hidden
1092+
* @param {string} islandSeparatorWidget - Widget used as island separator
1093+
* @param {boolean} islandsEnabled - Enable/disable island functionality
1094+
* @param {boolean} islandSeparatorPairing - Treat separators as pairs (start and end) instead of sharing the separator between two islands
1095+
* @returns {Array<{id: int, name: string, type: Enum.IslandSectionType}>} Widgets with their corresponding island section type
1096+
*/
1097+
function updateIslandWidgetTypes(panelLayout, noBgTracker, hiddenTracker, islandSeparatorWidget, islandsEnabled, islandSeparatorPairing) {
1098+
let output = [];
1099+
if (!panelLayout || !islandsEnabled) {
1100+
return output;
1101+
}
1102+
1103+
const SectionType = Enum.IslandSectionType;
1104+
1105+
const items = [];
1106+
let totalSeparators = 0;
1107+
1108+
for (let i = 0; i < panelLayout.children.length; i++) {
1109+
const current = panelLayout.children[i];
1110+
if (!current.applet?.plasmoid?.pluginName)
1111+
continue;
1112+
1113+
const currentName = current.applet.plasmoid.pluginName;
1114+
const currentId = current.applet.plasmoid.id;
1115+
const currentIsBlank = noBgTracker.has(currentId);
1116+
const currentIsHidden = hiddenTracker.has(currentId);
1117+
const currentIsSeparator = currentName === islandSeparatorWidget;
1118+
1119+
if (currentIsSeparator) {
1120+
totalSeparators++;
1121+
items.push({
1122+
type: "separator",
1123+
id: currentId,
1124+
name: currentName
1125+
});
1126+
} else if (currentIsBlank) {
1127+
items.push({
1128+
type: "blank",
1129+
id: currentId,
1130+
name: currentName
1131+
});
1132+
} else if (currentIsHidden) {
1133+
items.push({
1134+
type: "hidden",
1135+
id: currentId,
1136+
name: currentName
1137+
});
1138+
} else {
1139+
items.push({
1140+
type: "widget",
1141+
id: currentId,
1142+
name: currentName
1143+
});
1144+
}
1145+
}
1146+
1147+
function pushWidget(item, type) {
1148+
output.push({
1149+
id: item.id,
1150+
name: item.name,
1151+
type: type,
1152+
});
1153+
}
1154+
1155+
// Pass 1: split into separator-delimited groups and mark active groups.
1156+
const groups = [];
1157+
let separatorsSeen = 0;
1158+
let groupStart = 0;
1159+
1160+
function pushGroup(end) {
1161+
if (groupStart > end) {
1162+
return;
1163+
}
1164+
const active = !islandSeparatorPairing
1165+
? true
1166+
: (separatorsSeen % 2 === 1 && separatorsSeen < totalSeparators);
1167+
groups.push({
1168+
start: groupStart,
1169+
end: end,
1170+
active: active,
1171+
});
1172+
}
1173+
1174+
for (let i = 0; i < items.length; i++) {
1175+
if (items[i].type !== "separator") {
1176+
continue;
1177+
}
1178+
1179+
pushGroup(i - 1);
1180+
separatorsSeen++;
1181+
groupStart = i + 1;
1182+
}
1183+
1184+
// Flush trailing non-separator group.
1185+
pushGroup(items.length - 1);
1186+
1187+
function pushWidgetChunk(start, end) {
1188+
let firstWidgetIndex = -1;
1189+
let lastWidgetIndex = -1;
1190+
let widgetCount = 0;
1191+
1192+
for (let i = start; i <= end; i++) {
1193+
if (items[i].type === "widget") {
1194+
if (firstWidgetIndex === -1) {
1195+
firstWidgetIndex = i;
1196+
}
1197+
lastWidgetIndex = i;
1198+
widgetCount++;
1199+
}
1200+
}
1201+
1202+
for (let i = start; i <= end; i++) {
1203+
const current = items[i];
1204+
if (current.type !== "widget" || widgetCount < 2) {
1205+
pushWidget(current, SectionType.Default);
1206+
continue;
1207+
}
1208+
1209+
if (i === firstWidgetIndex) {
1210+
pushWidget(current, SectionType.Start);
1211+
} else if (i === lastWidgetIndex) {
1212+
pushWidget(current, SectionType.End);
1213+
} else {
1214+
pushWidget(current, SectionType.Middle);
1215+
}
1216+
}
1217+
}
1218+
1219+
// Pass 2: assign section type within each active group, splitting on blanks.
1220+
for (const group of groups) {
1221+
if (!group.active) {
1222+
for (let i = group.start; i <= group.end; i++) {
1223+
pushWidget(items[i], SectionType.Default);
1224+
}
1225+
continue;
1226+
}
1227+
1228+
let chunkStart = group.start;
1229+
for (let i = group.start; i <= group.end + 1; i++) {
1230+
const atGroupEnd = i === group.end + 1;
1231+
const atBlank = !atGroupEnd && items[i].type === "blank";
1232+
if (!atGroupEnd && !atBlank) {
1233+
continue;
1234+
}
1235+
1236+
if (chunkStart <= i - 1) {
1237+
pushWidgetChunk(chunkStart, i - 1);
1238+
}
1239+
if (atBlank) {
1240+
pushWidget(items[i], SectionType.Default);
1241+
}
1242+
chunkStart = i + 1;
1243+
}
1244+
}
1245+
1246+
return output;
1247+
}

0 commit comments

Comments
 (0)