Skip to content

Commit 1a08620

Browse files
committed
feat(weg): collapse weg items based on monitor #271
1 parent 7a11c65 commit 1a08620

Some content is hidden

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

86 files changed

+365
-35
lines changed

documentation/schemas/settings.schema.json

+18
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@
376376
"hideMode": "On-Overlap",
377377
"margin": 8,
378378
"mode": "Min-Content",
379+
"multitaskbarItemVisibilityBehaviour": "All screen",
379380
"padding": 8,
380381
"position": "Bottom",
381382
"size": 40,
@@ -1414,6 +1415,14 @@
14141415
}
14151416
}
14161417
},
1418+
"SeelenWegItemDisplayOption": {
1419+
"type": "string",
1420+
"enum": [
1421+
"Primary screen all, anyway where open",
1422+
"All screen",
1423+
"Show where open"
1424+
]
1425+
},
14171426
"SeelenWegMode": {
14181427
"type": "string",
14191428
"enum": [
@@ -1468,6 +1477,15 @@
14681477
}
14691478
]
14701479
},
1480+
"multitaskbarItemVisibilityBehaviour": {
1481+
"description": "Dock item visibility in case of multiple monitor",
1482+
"default": "All screen",
1483+
"allOf": [
1484+
{
1485+
"$ref": "#/definitions/SeelenWegItemDisplayOption"
1486+
}
1487+
]
1488+
},
14711489
"padding": {
14721490
"description": "Dock/Taskbar padding in px",
14731491
"default": 8,

src/apps/seelenweg/modules/bar/index.tsx

+58-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { useTranslation } from 'react-i18next';
44
import { useDispatch, useSelector } from 'react-redux';
55
import {
66
HideMode,
7+
MonitorInfo,
8+
SeelenWegItemDisplayOption,
79
SeelenWegMode,
810
SeelenWegSide,
911
SeparatorWegItem,
@@ -19,7 +21,7 @@ import { UserApplication } from '../item/infra/UserApplication';
1921

2022
import { RootActions, Selectors } from '../shared/store/app';
2123

22-
import { SwItem } from '../shared/store/domain';
24+
import { OpenedWindow, SwItem } from '../shared/store/domain';
2325

2426
import { cx } from '../../../shared/styles';
2527
import { WithContextMenu } from '../../components/WithContextMenu';
@@ -62,6 +64,8 @@ export function SeelenWeg() {
6264
const pinnedOnCenter = useSelector(Selectors.itemsOnCenter);
6365
const pinnedOnRight = useSelector(Selectors.itemsOnRight);
6466

67+
const monitorInfo: MonitorInfo = useSelector(Selectors.monitorInfo);
68+
6569
const dispatch = useDispatch();
6670
const { t } = useTranslation();
6771

@@ -140,11 +144,58 @@ export function SeelenWeg() {
140144
const isHorizontal =
141145
settings.position === SeelenWegSide.Top || settings.position === SeelenWegSide.Bottom;
142146

147+
let shouldBeReduced = [];
148+
switch (settings.multitaskbarItemVisibilityBehaviour) {
149+
case SeelenWegItemDisplayOption.PrimaryScreenAll:
150+
if (monitorInfo.isPrimary) {
151+
shouldBeReduced = [];
152+
} else {
153+
shouldBeReduced = [ SwItemType.Pinned, SwItemType.TemporalApp ];
154+
}
155+
break;
156+
case SeelenWegItemDisplayOption.Minimal:
157+
if (monitorInfo.isPrimary) {
158+
shouldBeReduced = [ SwItemType.TemporalApp ];
159+
} else {
160+
shouldBeReduced = [ SwItemType.Pinned, SwItemType.TemporalApp ];
161+
}
162+
break;
163+
default:
164+
shouldBeReduced = [];
165+
break;
166+
}
167+
168+
function isOnMonitor(item: SwItem) {
169+
if (shouldBeReduced.includes(item.type)) {
170+
return !('opens' in item) || item.opens.some((current: OpenedWindow) => current.presentative_monitor == monitorInfo.index);
171+
} else {
172+
return true;
173+
}
174+
}
175+
176+
function filterOpenElement(item: SwItem): SwItem {
177+
const newItem = { ...item };
178+
179+
if (('opens' in item)) {
180+
if (monitorInfo.isPrimary) {
181+
newItem.opens = item.opens.filter((current: OpenedWindow) => settings.multitaskbarItemVisibilityBehaviour != SeelenWegItemDisplayOption.Minimal || current.presentative_monitor == monitorInfo.index);
182+
} else {
183+
newItem.opens = item.opens.filter((current: OpenedWindow) => current.presentative_monitor == monitorInfo.index);
184+
}
185+
}
186+
187+
return newItem;
188+
}
189+
190+
const filteredPinOnLeft = pinnedOnLeft.filter(isOnMonitor).map(filterOpenElement);
191+
const filteredPinOnCenter = pinnedOnCenter.filter(isOnMonitor).map(filterOpenElement);
192+
const filteredPinOnRight = pinnedOnRight.filter(isOnMonitor).map(filterOpenElement);
193+
143194
return (
144195
<WithContextMenu items={getSeelenWegMenu(t)}>
145196
<Reorder.Group
146197
as="div"
147-
values={[...pinnedOnLeft, Separator1, ...pinnedOnCenter, Separator2, ...pinnedOnRight]}
198+
values={[...filteredPinOnLeft, Separator1, ...filteredPinOnCenter, Separator2, ...filteredPinOnRight]}
148199
onReorder={onReorderPinned}
149200
axis={isHorizontal ? 'x' : 'y'}
150201
className={cx('taskbar', settings.position.toLowerCase(), {
@@ -157,7 +208,7 @@ export function SeelenWeg() {
157208
>
158209
<BackgroundByLayersV2 prefix="taskbar" />
159210
{[
160-
...pinnedOnLeft.map(ItemByType),
211+
...filteredPinOnLeft.map(ItemByType),
161212
<Reorder.Item
162213
as="div"
163214
key="separator1"
@@ -166,9 +217,9 @@ export function SeelenWeg() {
166217
visible: settings.visibleSeparators,
167218
})}
168219
drag={false}
169-
style={getSeparatorComplementarySize(pinnedOnLeft.length, pinnedOnCenter.length)}
220+
style={getSeparatorComplementarySize(filteredPinOnLeft.length, filteredPinOnCenter.length)}
170221
/>,
171-
...pinnedOnCenter.map(ItemByType),
222+
...filteredPinOnCenter.map(ItemByType),
172223
<Reorder.Item
173224
as="div"
174225
key="separator2"
@@ -177,9 +228,9 @@ export function SeelenWeg() {
177228
visible: settings.visibleSeparators,
178229
})}
179230
drag={false}
180-
style={getSeparatorComplementarySize(pinnedOnRight.length, pinnedOnCenter.length)}
231+
style={getSeparatorComplementarySize(filteredPinOnRight.length, filteredPinOnCenter.length)}
181232
/>,
182-
...pinnedOnRight.map(ItemByType),
233+
...filteredPinOnRight.map(ItemByType),
183234
]}
184235
</Reorder.Group>
185236
</WithContextMenu>

src/apps/seelenweg/modules/item/app/TemporalApp.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class SwTemporalAppUtils {
2323
execution_command: item.execution_path,
2424
is_dir: false,
2525
title: item.exe.split('\\').at(-1) || 'Unknown',
26-
opens: [item.hwnd],
26+
opens: [{ hwnd: item.hwnd, presentative_monitor: item.presentative_monitor }],
2727
};
2828
}
2929
}

src/apps/seelenweg/modules/item/infra/UserApplication.tsx

+13-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Selectors } from '../../shared/store/app';
1313
import {
1414
ExtendedPinnedWegItem,
1515
ExtendedTemporalWegItem,
16+
OpenedWindow,
1617
RootState,
1718
} from '../../shared/store/domain';
1819

@@ -63,7 +64,7 @@ export const UserApplication = memo(({ item }: Props) => {
6364

6465
useEffect(() => {
6566
if (openPreview) {
66-
updatePreviews(item.opens);
67+
updatePreviews(item.opens.map((current: OpenedWindow) => current.hwnd));
6768
}
6869
}, [openPreview]);
6970

@@ -90,31 +91,33 @@ export const UserApplication = memo(({ item }: Props) => {
9091
prefix="preview"
9192
>
9293
<div className="weg-item-preview-scrollbar">
93-
{item.opens.map((hwnd) => (
94-
<UserApplicationPreview key={hwnd} hwnd={hwnd} />
95-
))}
94+
{item.opens
95+
.map((current: OpenedWindow) => (
96+
<UserApplicationPreview key={current.hwnd} hwnd={current.hwnd} />
97+
))
98+
}
9699
</div>
97100
</BackgroundByLayersV2>
98101
}
99102
>
100103
<div
101104
className="weg-item"
102105
onClick={() => {
103-
let hwnd = item.opens[0];
104-
if (!hwnd) {
106+
let openWindow: OpenedWindow = item.opens[0];
107+
if (!openWindow) {
105108
if (item.path.endsWith('.lnk')) {
106109
invoke(SeelenCommand.OpenFile, { path: item.path });
107110
} else {
108111
invoke(SeelenCommand.OpenFile, { path: item.execution_command });
109112
}
110113
} else {
111-
invoke(SeelenCommand.WegToggleWindowState, { hwnd });
114+
invoke(SeelenCommand.WegToggleWindowState, { hwnd: openWindow.hwnd });
112115
}
113116
}}
114117
onAuxClick={(e) => {
115-
let hwnd = item.opens[0];
116-
if (e.button === 1 && hwnd) {
117-
invoke(SeelenCommand.WegCloseApp, { hwnd });
118+
let opened: OpenedWindow = item.opens[0];
119+
if (e.button === 1 && opened) {
120+
invoke(SeelenCommand.WegCloseApp, { hwnd: opened.hwnd });
118121
}
119122
}}
120123
onContextMenu={(e) => e.stopPropagation()}

src/apps/seelenweg/modules/shared/store/app.ts

+26-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ExtendedPinnedWegItem,
1010
ExtendedTemporalWegItem,
1111
HWND,
12+
OpenedWindow,
1213
RootState,
1314
SwItem,
1415
} from './domain';
@@ -25,6 +26,7 @@ const initialState: RootState = {
2526
isOverlaped: false,
2627
settings: new SeelenWegSettings(),
2728
mediaSessions: [],
29+
monitorInfo: null,
2830
colors: UIColors.default(),
2931
};
3032

@@ -168,8 +170,8 @@ export const RootSlice = createSlice({
168170
return;
169171
}
170172

171-
if (!pinedApp.opens.includes(new_app.hwnd)) {
172-
pinedApp.opens.push(new_app.hwnd);
173+
if (!pinedApp.opens.some((current: OpenedWindow) => current.hwnd === new_app.hwnd)) {
174+
pinedApp.opens.push({ hwnd: new_app.hwnd, presentative_monitor: new_app.presentative_monitor });
173175
}
174176

175177
// update path to pinned apps normally changed on updates
@@ -185,12 +187,33 @@ export const RootSlice = createSlice({
185187
found.title = action.payload.title;
186188
}
187189
},
190+
updateMonitorPosition(state, action: PayloadAction<OpenedWindow>) {
191+
const current_app = state.openApps[action.payload.hwnd];
192+
if (current_app) {
193+
current_app.presentative_monitor = action.payload.presentative_monitor;
194+
}
195+
196+
function openSearch(window: OpenedWindow) {
197+
return window.hwnd === action.payload.hwnd;
198+
}
199+
200+
state.itemsOnLeft.concat(state.itemsOnCenter).concat(state.itemsOnRight)
201+
.filter((app: SwItem) => {
202+
if ('opens' in app && app.opens.some(openSearch)) {
203+
return true;
204+
}
205+
206+
return false;
207+
}).forEach((app: SwItem) => {
208+
app.opens = app.opens.filter((window: OpenedWindow) => !openSearch(window)).concat({ hwnd: action.payload.hwnd, presentative_monitor: action.payload.presentative_monitor });
209+
});
210+
},
188211
removeOpenApp(state, action: PayloadAction<HWND>) {
189212
delete state.openApps[action.payload];
190213

191214
function filter(app: SwItem) {
192215
if ('opens' in app) {
193-
app.opens = app.opens.filter((hwnd) => hwnd !== action.payload);
216+
app.opens = app.opens.filter((window: OpenedWindow) => window.hwnd !== action.payload);
194217
}
195218
return app.type !== SwItemType.TemporalApp || app.opens.length > 0;
196219
}

src/apps/seelenweg/modules/shared/store/domain.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { modify } from 'readable-types';
22
import {
33
MediaWegItem,
4+
MonitorInfo,
45
PinnedWegItem,
56
SeelenWegSettings,
67
SeparatorWegItem,
@@ -12,6 +13,10 @@ import { IRootState } from '../../../../../shared.interfaces';
1213
import { FocusedApp } from '../../../../shared/interfaces/common';
1314

1415
export type HWND = number & {};
16+
export type OpenedWindow = {
17+
hwnd: HWND;
18+
presentative_monitor: number;
19+
};
1520

1621
export interface AppFromBackground {
1722
title: string;
@@ -21,6 +26,7 @@ export interface AppFromBackground {
2126
icon_path: string;
2227
hwnd: HWND;
2328
creator_hwnd: HWND;
29+
presentative_monitor: number;
2430
}
2531

2632
export enum AppsSides {
@@ -48,7 +54,7 @@ export type ExtendedPinnedWegItem = modify<
4854
{
4955
icon: string;
5056
title: string;
51-
opens: HWND[];
57+
opens: OpenedWindow[];
5258
}
5359
>;
5460

@@ -74,5 +80,6 @@ export interface RootState extends IRootState<SeelenWegSettings> {
7480
// ----------------------
7581
focusedApp: FocusedApp | null;
7682
isOverlaped: boolean;
83+
monitorInfo: MonitorInfo;
7784
mediaSessions: MediaSession[];
7885
}

src/apps/seelenweg/modules/shared/store/infra.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import { SwPinnedAppUtils } from '../../item/app/PinnedApp';
1515
import { SwTemporalAppUtils } from '../../item/app/TemporalApp';
1616
import { RootActions, RootSlice } from './app';
1717

18-
import { AppFromBackground, HWND, MediaSession, SwItem } from './domain';
18+
import { AppFromBackground, HWND, MediaSession, OpenedWindow, SwItem } from './domain';
1919

2020
import { UserSettingsLoader } from '../../../../settings/modules/shared/store/storeApi';
2121
import { FocusedApp } from '../../../../shared/interfaces/common';
2222
import { StartThemingTool } from '../../../../shared/styles';
2323
import i18n from '../../../i18n';
24-
import { IsSavingPinnedItems, loadPinnedItems } from './storeApi';
24+
import { IsSavingPinnedItems, loadMonitorInfo, loadPinnedItems } from './storeApi';
2525

2626
export const store = configureStore({
2727
reducer: RootSlice.reducer,
@@ -92,6 +92,10 @@ export async function registerStoreEvents() {
9292
store.dispatch(RootActions.updateOpenAppInfo(item));
9393
});
9494

95+
await listenGlobal<OpenedWindow>('update-monitor-position', (event) => {
96+
store.dispatch(RootActions.updateMonitorPosition(event.payload));
97+
});
98+
9599
const onFocusChanged = debounce((app: FocusedApp) => {
96100
store.dispatch(RootActions.setFocusedApp(app));
97101
}, 200);
@@ -142,6 +146,10 @@ export async function registerStoreEvents() {
142146
await loadSettingsToStore();
143147
});
144148

149+
await listenGlobal<[]>('workspaces-changed', async (_) => {
150+
store.dispatch(RootActions.setMonitorInfo(await loadMonitorInfo()));
151+
});
152+
145153
await StartThemingTool();
146154
await view.emitTo(view.label, 'request-all-open-apps');
147155
}
@@ -197,4 +205,5 @@ export async function loadStore() {
197205
store.dispatch(RootActions.setItemsOnLeft(await cleanSavedItems(apps.left)));
198206
store.dispatch(RootActions.setItemsOnCenter(await cleanSavedItems(apps.center)));
199207
store.dispatch(RootActions.setItemsOnRight(await cleanSavedItems(apps.right)));
208+
store.dispatch(RootActions.setMonitorInfo(await loadMonitorInfo()));
200209
}

src/apps/seelenweg/modules/shared/store/storeApi.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api/core';
33
import { writeTextFile } from '@tauri-apps/plugin-fs';
44
import yaml from 'js-yaml';
55
import { debounce } from 'lodash';
6-
import { SwItemType, WegItem, WegItems } from 'seelen-core';
6+
import { MonitorInfo, SwItemType, WegItem, WegItems } from 'seelen-core';
77

88
import { store } from './infra';
99

@@ -50,3 +50,7 @@ export const savePinnedItems = debounce(
5050
export const loadPinnedItems = async (): Promise<WegItems> => {
5151
return invoke<WegItems>('state_get_weg_items');
5252
};
53+
54+
export const loadMonitorInfo = async (): Promise<MonitorInfo> => {
55+
return invoke<MonitorInfo>('weg_get_monitor_info');
56+
};

0 commit comments

Comments
 (0)