@lvce-editor/title-bar-worker 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/titleBarWorkerMain.js +328 -216
- package/package.json +1 -1
|
@@ -864,6 +864,55 @@ const WebWorkerRpcClient = {
|
|
|
864
864
|
create: create$2
|
|
865
865
|
};
|
|
866
866
|
|
|
867
|
+
const RenderEntries = 1;
|
|
868
|
+
const RenderFocusedIndex = 2;
|
|
869
|
+
const RenderMenus = 3;
|
|
870
|
+
|
|
871
|
+
const diffType$2 = RenderEntries;
|
|
872
|
+
const isEqual$2 = (oldState, newState) => {
|
|
873
|
+
return oldState.titleBarEntries === newState.titleBarEntries && oldState.width === newState.width && oldState.focusedIndex === newState.focusedIndex && oldState.isMenuOpen === newState.isMenuOpen;
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const DiffEntries = {
|
|
877
|
+
__proto__: null,
|
|
878
|
+
diffType: diffType$2,
|
|
879
|
+
isEqual: isEqual$2
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
const diffType$1 = RenderFocusedIndex;
|
|
883
|
+
const isEqual$1 = (oldState, newState) => {
|
|
884
|
+
return oldState.focusedIndex === newState.focusedIndex && oldState.isMenuOpen === newState.isMenuOpen;
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
const DiffFocusedIndex = {
|
|
888
|
+
__proto__: null,
|
|
889
|
+
diffType: diffType$1,
|
|
890
|
+
isEqual: isEqual$1
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
const diffType = RenderMenus;
|
|
894
|
+
const isEqual = (oldState, newState) => {
|
|
895
|
+
return oldState.menus === newState.menus;
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
const DiffMenus = {
|
|
899
|
+
__proto__: null,
|
|
900
|
+
diffType,
|
|
901
|
+
isEqual
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const modules = [DiffFocusedIndex, DiffEntries, DiffMenus];
|
|
905
|
+
|
|
906
|
+
const diff = (oldState, newState) => {
|
|
907
|
+
const diffResult = [];
|
|
908
|
+
for (const module of modules) {
|
|
909
|
+
if (!module.isEqual(oldState, newState)) {
|
|
910
|
+
diffResult.push(module.diffType);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return diffResult;
|
|
914
|
+
};
|
|
915
|
+
|
|
867
916
|
const Menu$1 = 'menu';
|
|
868
917
|
const MenuBar = 'menubar';
|
|
869
918
|
const MenuItem$1 = 'menuitem';
|
|
@@ -1187,10 +1236,10 @@ const parseKey = rawKey => {
|
|
|
1187
1236
|
|
|
1188
1237
|
const Button = 1;
|
|
1189
1238
|
const Div = 4;
|
|
1190
|
-
const Span = 8;
|
|
1191
1239
|
const Text = 12;
|
|
1192
1240
|
const I = 16;
|
|
1193
1241
|
const Img = 17;
|
|
1242
|
+
const Span = 8;
|
|
1194
1243
|
|
|
1195
1244
|
const text = data => {
|
|
1196
1245
|
return {
|
|
@@ -1354,10 +1403,15 @@ const getMenuVirtualDom = menuItems => {
|
|
|
1354
1403
|
};
|
|
1355
1404
|
|
|
1356
1405
|
const HandleClick = 'handleClick';
|
|
1406
|
+
const HandleClickMinimize = 'handleClickMinimize';
|
|
1407
|
+
const HandleClickToggleClose = 'handleClickToggleClose';
|
|
1408
|
+
const HandleClickToggleMaximize = 'handleClickToggleMaximize';
|
|
1357
1409
|
const HandleFocusIn = 'handleFocusIn';
|
|
1358
1410
|
const HandleFocusOut = 'handleFocusOut';
|
|
1359
1411
|
const HandlePointerOut = 'handlePointerOut';
|
|
1360
1412
|
const HandlePointerOver = 'handlePointerOver';
|
|
1413
|
+
const HandleMenuClick = 'handleMenuClick';
|
|
1414
|
+
const HandleMenuMouseOver = 'handleMenuMouseOver';
|
|
1361
1415
|
|
|
1362
1416
|
const getItemVirtualDom = item => {
|
|
1363
1417
|
// @ts-ignore
|
|
@@ -1600,7 +1654,8 @@ const doRender = async uid => {
|
|
|
1600
1654
|
return commands;
|
|
1601
1655
|
};
|
|
1602
1656
|
|
|
1603
|
-
const commandsIds = ['closeMenu', 'focus', 'focusFirst', 'focusIndex', 'focusLast', 'focusNext', 'focusPrevious', 'handleKeyArrowDown', 'handleKeyArrowLeft', 'handleKeyArrowRight', 'handleKeyArrowUp', 'handleKeyEnd', 'handleKeyEnter', 'handleKeyEscape', 'handleKeyHome', 'handleKeySpace', 'handleMenuClick', 'handleMenuMouseOver', '
|
|
1657
|
+
const commandsIds = ['closeMenu', 'focus', 'focusFirst', 'focusIndex', 'focusLast', 'focusNext', 'focusPrevious', 'handleClick', 'handleFocus', 'handleKeyArrowDown', 'handleKeyArrowLeft', 'handleKeyArrowRight', 'handleKeyArrowUp', 'handleKeyEnd', 'handleKeyEnter', 'handleKeyEscape', 'handleKeyHome', 'handleKeySpace', 'handleMenuClick', 'handleMenuMouseOver', 'handleMouseOut', 'handleMouseOver', 'handlePointerOver', 'handlePointerOut', 'toggleIndex', 'toggleMenu'];
|
|
1658
|
+
|
|
1604
1659
|
const getCommandIds = () => {
|
|
1605
1660
|
return commandsIds;
|
|
1606
1661
|
};
|
|
@@ -2288,6 +2343,248 @@ const handleContextMenu = state => {
|
|
|
2288
2343
|
return state;
|
|
2289
2344
|
};
|
|
2290
2345
|
|
|
2346
|
+
const getTitleBarIndexFromPosition = (titleBarEntries, x, y) => {
|
|
2347
|
+
let currentX = 0;
|
|
2348
|
+
for (let i = 0; i < titleBarEntries.length; i++) {
|
|
2349
|
+
const entry = titleBarEntries[i];
|
|
2350
|
+
const entryWidth = entry.width;
|
|
2351
|
+
if (x >= currentX && x < currentX + entryWidth) {
|
|
2352
|
+
return i;
|
|
2353
|
+
}
|
|
2354
|
+
currentX += entryWidth;
|
|
2355
|
+
}
|
|
2356
|
+
return -1;
|
|
2357
|
+
};
|
|
2358
|
+
|
|
2359
|
+
const getTotalWidth = entries => {
|
|
2360
|
+
let total = 0;
|
|
2361
|
+
for (const entry of entries) {
|
|
2362
|
+
total += entry.width;
|
|
2363
|
+
}
|
|
2364
|
+
return total;
|
|
2365
|
+
};
|
|
2366
|
+
|
|
2367
|
+
// TODO lazyload menuEntries and use Command.execute (maybe)
|
|
2368
|
+
const CONTEXT_MENU_ITEM_HEIGHT = 26;
|
|
2369
|
+
const CONTEXT_MENU_SEPARATOR_HEIGHT = 11;
|
|
2370
|
+
const CONTEXT_MENU_PADDING = 8;
|
|
2371
|
+
const getMenuHeight = items => {
|
|
2372
|
+
let height = CONTEXT_MENU_PADDING;
|
|
2373
|
+
for (const item of items) {
|
|
2374
|
+
switch (item.flags) {
|
|
2375
|
+
case Separator:
|
|
2376
|
+
height += CONTEXT_MENU_SEPARATOR_HEIGHT;
|
|
2377
|
+
break;
|
|
2378
|
+
default:
|
|
2379
|
+
height += CONTEXT_MENU_ITEM_HEIGHT;
|
|
2380
|
+
break;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
return height;
|
|
2384
|
+
};
|
|
2385
|
+
|
|
2386
|
+
// TODO lazyload menuEntries and use Command.execute (maybe)
|
|
2387
|
+
const MENU_WIDTH = 150;
|
|
2388
|
+
const CONTEXT_MENU_WIDTH = 250;
|
|
2389
|
+
const getMenuWidth = () => {
|
|
2390
|
+
return CONTEXT_MENU_WIDTH;
|
|
2391
|
+
};
|
|
2392
|
+
|
|
2393
|
+
// TODO difference between focusing with mouse or keyboard
|
|
2394
|
+
// with mouse -> open submenu
|
|
2395
|
+
// with keyboard -> don't open submenu, only focus
|
|
2396
|
+
|
|
2397
|
+
const getIndexToFocusNextStartingAt = (items, startIndex) => {
|
|
2398
|
+
for (let i = startIndex; i < startIndex + items.length; i++) {
|
|
2399
|
+
const index = i % items.length;
|
|
2400
|
+
const item = items[index];
|
|
2401
|
+
if (canBeFocused(item)) {
|
|
2402
|
+
return index;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
return -1;
|
|
2406
|
+
};
|
|
2407
|
+
const getIndexToFocusFirst = items => {
|
|
2408
|
+
return getIndexToFocusNextStartingAt(items, 0);
|
|
2409
|
+
};
|
|
2410
|
+
const getIndexToFocusLast = items => {
|
|
2411
|
+
return getIndexToFocusPreviousStartingAt(items, items.length - 1);
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
// TODO this code seems a bit too complicated, maybe it can be simplified
|
|
2415
|
+
const getIndexToFocusPreviousStartingAt = (items, startIndex) => {
|
|
2416
|
+
for (let i = startIndex; i > startIndex - items.length; i--) {
|
|
2417
|
+
const index = (i + items.length) % items.length;
|
|
2418
|
+
const item = items[index];
|
|
2419
|
+
if (canBeFocused(item)) {
|
|
2420
|
+
return index;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
return -1;
|
|
2424
|
+
};
|
|
2425
|
+
const getIndexToFocusPrevious = menu => {
|
|
2426
|
+
const startIndex = menu.focusedIndex === -1 ? menu.items.length - 1 : menu.focusedIndex - 1;
|
|
2427
|
+
return getIndexToFocusPreviousStartingAt(menu.items, startIndex);
|
|
2428
|
+
};
|
|
2429
|
+
const canBeFocused = item => {
|
|
2430
|
+
switch (item.flags) {
|
|
2431
|
+
case Separator:
|
|
2432
|
+
case Disabled:
|
|
2433
|
+
return false;
|
|
2434
|
+
default:
|
|
2435
|
+
return true;
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
const getIndexToFocusNext = menu => {
|
|
2439
|
+
const startIndex = menu.focusedIndex + 1;
|
|
2440
|
+
return getIndexToFocusNextStartingAt(menu.items, startIndex);
|
|
2441
|
+
};
|
|
2442
|
+
|
|
2443
|
+
// TODO handle printable letter and focus item that starts with that letter
|
|
2444
|
+
|
|
2445
|
+
// TODO pageup / pagedown keys
|
|
2446
|
+
|
|
2447
|
+
// TODO more tests
|
|
2448
|
+
|
|
2449
|
+
const menus = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
|
|
2450
|
+
const getMenus = () => {
|
|
2451
|
+
return menus;
|
|
2452
|
+
};
|
|
2453
|
+
const getModule = id => {
|
|
2454
|
+
for (const module of menus) {
|
|
2455
|
+
if (module.id === id) {
|
|
2456
|
+
return module;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
return undefined;
|
|
2460
|
+
};
|
|
2461
|
+
const getMenuEntries = async (id, ...args) => {
|
|
2462
|
+
try {
|
|
2463
|
+
const module = getModule(id);
|
|
2464
|
+
// @ts-ignore
|
|
2465
|
+
const inject = module.inject || [];
|
|
2466
|
+
// @ts-ignore
|
|
2467
|
+
return module.getMenuEntries(...args);
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
throw new VError(error, `Failed to load menu entries for id ${id}`);
|
|
2470
|
+
}
|
|
2471
|
+
};
|
|
2472
|
+
|
|
2473
|
+
const openMenuAtIndex = async (state, index, shouldBeFocused) => {
|
|
2474
|
+
const {
|
|
2475
|
+
titleBarEntries,
|
|
2476
|
+
titleBarHeight,
|
|
2477
|
+
x
|
|
2478
|
+
} = state;
|
|
2479
|
+
// TODO race conditions
|
|
2480
|
+
// TODO send renderer process
|
|
2481
|
+
// 1. open menu, items to show
|
|
2482
|
+
// 2. focus menu
|
|
2483
|
+
const titleBarEntry = titleBarEntries[index];
|
|
2484
|
+
const {
|
|
2485
|
+
id
|
|
2486
|
+
} = titleBarEntry;
|
|
2487
|
+
const items = await getMenuEntries(id);
|
|
2488
|
+
const relevantEntries = titleBarEntries.slice(0, index);
|
|
2489
|
+
const totalWidths = getTotalWidth(relevantEntries);
|
|
2490
|
+
const offset = totalWidths;
|
|
2491
|
+
// TODO race condition: another menu might already be open at this point
|
|
2492
|
+
|
|
2493
|
+
const menuX = x + offset;
|
|
2494
|
+
const menuY = titleBarHeight;
|
|
2495
|
+
const width = getMenuWidth();
|
|
2496
|
+
const height = getMenuHeight(items);
|
|
2497
|
+
const menuFocusedIndex = shouldBeFocused ? getIndexToFocusNextStartingAt(items, 0) : -1;
|
|
2498
|
+
const menu = {
|
|
2499
|
+
id,
|
|
2500
|
+
items,
|
|
2501
|
+
focusedIndex: menuFocusedIndex,
|
|
2502
|
+
level: 0,
|
|
2503
|
+
x: menuX,
|
|
2504
|
+
y: menuY,
|
|
2505
|
+
width,
|
|
2506
|
+
height
|
|
2507
|
+
};
|
|
2508
|
+
const menus = [menu];
|
|
2509
|
+
return {
|
|
2510
|
+
...state,
|
|
2511
|
+
isMenuOpen: true,
|
|
2512
|
+
focusedIndex: index,
|
|
2513
|
+
menus
|
|
2514
|
+
};
|
|
2515
|
+
};
|
|
2516
|
+
|
|
2517
|
+
const focusIndex = async (state, index) => {
|
|
2518
|
+
object(state);
|
|
2519
|
+
number(index);
|
|
2520
|
+
const {
|
|
2521
|
+
isMenuOpen,
|
|
2522
|
+
focusedIndex
|
|
2523
|
+
} = state;
|
|
2524
|
+
if (index === focusedIndex) {
|
|
2525
|
+
return state;
|
|
2526
|
+
}
|
|
2527
|
+
if (isMenuOpen) {
|
|
2528
|
+
return openMenuAtIndex(state, index, /* focus */false);
|
|
2529
|
+
}
|
|
2530
|
+
return {
|
|
2531
|
+
...state,
|
|
2532
|
+
focusedIndex: index
|
|
2533
|
+
};
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
const handleMouseOutMenuClosed = state => {
|
|
2537
|
+
return focusIndex(state, -1);
|
|
2538
|
+
};
|
|
2539
|
+
|
|
2540
|
+
const handleMouseOutMenuOpen = state => {
|
|
2541
|
+
return state;
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
const ifElse = (menuOpenFunction, menuClosedFunction) => {
|
|
2545
|
+
const ifElseFunction = (state, ...args) => {
|
|
2546
|
+
const {
|
|
2547
|
+
isMenuOpen
|
|
2548
|
+
} = state;
|
|
2549
|
+
if (isMenuOpen) {
|
|
2550
|
+
return menuOpenFunction(state, ...args);
|
|
2551
|
+
}
|
|
2552
|
+
return menuClosedFunction(state, ...args);
|
|
2553
|
+
};
|
|
2554
|
+
return ifElseFunction;
|
|
2555
|
+
};
|
|
2556
|
+
|
|
2557
|
+
const handleMouseOut = ifElse(handleMouseOutMenuOpen, handleMouseOutMenuClosed);
|
|
2558
|
+
|
|
2559
|
+
const handlePointerOut = (state, clientX, clientY) => {
|
|
2560
|
+
const index = getTitleBarIndexFromPosition(state.titleBarEntries, clientX - state.x);
|
|
2561
|
+
if (index === -1) {
|
|
2562
|
+
return state;
|
|
2563
|
+
}
|
|
2564
|
+
return handleMouseOut(state, index);
|
|
2565
|
+
};
|
|
2566
|
+
|
|
2567
|
+
const handleMouseOverMenuClosed = (state, index) => {
|
|
2568
|
+
return focusIndex(state, index);
|
|
2569
|
+
};
|
|
2570
|
+
|
|
2571
|
+
const handleMouseOverMenuOpen = async (state, index) => {
|
|
2572
|
+
if (index === -1) {
|
|
2573
|
+
return state;
|
|
2574
|
+
}
|
|
2575
|
+
return focusIndex(state, index);
|
|
2576
|
+
};
|
|
2577
|
+
|
|
2578
|
+
const handleMouseOver = ifElse(handleMouseOverMenuOpen, handleMouseOverMenuClosed);
|
|
2579
|
+
|
|
2580
|
+
const handlePointerOver = (state, clientX, clientY) => {
|
|
2581
|
+
const index = getTitleBarIndexFromPosition(state.titleBarEntries, clientX - state.x);
|
|
2582
|
+
if (index === -1) {
|
|
2583
|
+
return state;
|
|
2584
|
+
}
|
|
2585
|
+
return handleMouseOver(state, index);
|
|
2586
|
+
};
|
|
2587
|
+
|
|
2291
2588
|
const getFontString = (fontWeight, fontSize, fontFamily) => {
|
|
2292
2589
|
return `${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
2293
2590
|
};
|
|
@@ -2380,40 +2677,37 @@ const loadContent = async (state, titleBarEntries) => {
|
|
|
2380
2677
|
};
|
|
2381
2678
|
};
|
|
2382
2679
|
|
|
2383
|
-
const menus = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
|
|
2384
|
-
const getMenus = () => {
|
|
2385
|
-
return menus;
|
|
2386
|
-
};
|
|
2387
|
-
const getModule = id => {
|
|
2388
|
-
for (const module of menus) {
|
|
2389
|
-
if (module.id === id) {
|
|
2390
|
-
return module;
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
return undefined;
|
|
2394
|
-
};
|
|
2395
|
-
const getMenuEntries = async (id, ...args) => {
|
|
2396
|
-
try {
|
|
2397
|
-
const module = getModule(id);
|
|
2398
|
-
// @ts-ignore
|
|
2399
|
-
const inject = module.inject || [];
|
|
2400
|
-
// @ts-ignore
|
|
2401
|
-
return module.getMenuEntries(...args);
|
|
2402
|
-
} catch (error) {
|
|
2403
|
-
throw new VError(error, `Failed to load menu entries for id ${id}`);
|
|
2404
|
-
}
|
|
2405
|
-
};
|
|
2406
|
-
|
|
2407
2680
|
const renderEventListeners = () => {
|
|
2408
2681
|
return [{
|
|
2409
|
-
name:
|
|
2682
|
+
name: HandleClickMinimize,
|
|
2410
2683
|
params: ['handleClickMinimize']
|
|
2411
2684
|
}, {
|
|
2412
|
-
name:
|
|
2685
|
+
name: HandleClickToggleClose,
|
|
2413
2686
|
params: ['handleClickClose']
|
|
2414
2687
|
}, {
|
|
2415
|
-
name:
|
|
2688
|
+
name: HandleClickToggleMaximize,
|
|
2416
2689
|
params: ['handleClickToggleMaximize']
|
|
2690
|
+
}, {
|
|
2691
|
+
name: HandleFocusIn,
|
|
2692
|
+
params: ['handlefocus']
|
|
2693
|
+
}, {
|
|
2694
|
+
name: HandleMenuClick,
|
|
2695
|
+
params: ['handleMenuClick', 'event.clientX', 'event.clientY']
|
|
2696
|
+
}, {
|
|
2697
|
+
name: HandleMenuMouseOver,
|
|
2698
|
+
params: ['handleMenuMouseOver', 'event.clientX', 'event.clientY']
|
|
2699
|
+
}, {
|
|
2700
|
+
name: HandleClick,
|
|
2701
|
+
params: ['handleClick', 'event.button', 'event.clientX', 'event.clientY']
|
|
2702
|
+
}, {
|
|
2703
|
+
name: HandlePointerOut,
|
|
2704
|
+
params: ['handlePointerOut', 'event.clientX', 'event.clientY']
|
|
2705
|
+
}, {
|
|
2706
|
+
name: HandlePointerOver,
|
|
2707
|
+
params: ['handlePointerOver', 'event.clientX', 'event.clientY']
|
|
2708
|
+
}, {
|
|
2709
|
+
name: HandleFocusOut,
|
|
2710
|
+
params: ['handleFocusOut', 'event.clientX', 'event.clientY'] // TODO maybe check relatedTarget
|
|
2417
2711
|
}];
|
|
2418
2712
|
};
|
|
2419
2713
|
|
|
@@ -2476,155 +2770,6 @@ const previous = (items, index) => {
|
|
|
2476
2770
|
return index === 0 ? items.length - 1 : index - 1;
|
|
2477
2771
|
};
|
|
2478
2772
|
|
|
2479
|
-
const getTotalWidth = entries => {
|
|
2480
|
-
let total = 0;
|
|
2481
|
-
for (const entry of entries) {
|
|
2482
|
-
total += entry.width;
|
|
2483
|
-
}
|
|
2484
|
-
return total;
|
|
2485
|
-
};
|
|
2486
|
-
|
|
2487
|
-
// TODO lazyload menuEntries and use Command.execute (maybe)
|
|
2488
|
-
const CONTEXT_MENU_ITEM_HEIGHT = 26;
|
|
2489
|
-
const CONTEXT_MENU_SEPARATOR_HEIGHT = 11;
|
|
2490
|
-
const CONTEXT_MENU_PADDING = 8;
|
|
2491
|
-
const getMenuHeight = items => {
|
|
2492
|
-
let height = CONTEXT_MENU_PADDING;
|
|
2493
|
-
for (const item of items) {
|
|
2494
|
-
switch (item.flags) {
|
|
2495
|
-
case Separator:
|
|
2496
|
-
height += CONTEXT_MENU_SEPARATOR_HEIGHT;
|
|
2497
|
-
break;
|
|
2498
|
-
default:
|
|
2499
|
-
height += CONTEXT_MENU_ITEM_HEIGHT;
|
|
2500
|
-
break;
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
return height;
|
|
2504
|
-
};
|
|
2505
|
-
|
|
2506
|
-
// TODO lazyload menuEntries and use Command.execute (maybe)
|
|
2507
|
-
const MENU_WIDTH = 150;
|
|
2508
|
-
const CONTEXT_MENU_WIDTH = 250;
|
|
2509
|
-
const getMenuWidth = () => {
|
|
2510
|
-
return CONTEXT_MENU_WIDTH;
|
|
2511
|
-
};
|
|
2512
|
-
|
|
2513
|
-
// TODO difference between focusing with mouse or keyboard
|
|
2514
|
-
// with mouse -> open submenu
|
|
2515
|
-
// with keyboard -> don't open submenu, only focus
|
|
2516
|
-
|
|
2517
|
-
const getIndexToFocusNextStartingAt = (items, startIndex) => {
|
|
2518
|
-
for (let i = startIndex; i < startIndex + items.length; i++) {
|
|
2519
|
-
const index = i % items.length;
|
|
2520
|
-
const item = items[index];
|
|
2521
|
-
if (canBeFocused(item)) {
|
|
2522
|
-
return index;
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
return -1;
|
|
2526
|
-
};
|
|
2527
|
-
const getIndexToFocusFirst = items => {
|
|
2528
|
-
return getIndexToFocusNextStartingAt(items, 0);
|
|
2529
|
-
};
|
|
2530
|
-
const getIndexToFocusLast = items => {
|
|
2531
|
-
return getIndexToFocusPreviousStartingAt(items, items.length - 1);
|
|
2532
|
-
};
|
|
2533
|
-
|
|
2534
|
-
// TODO this code seems a bit too complicated, maybe it can be simplified
|
|
2535
|
-
const getIndexToFocusPreviousStartingAt = (items, startIndex) => {
|
|
2536
|
-
for (let i = startIndex; i > startIndex - items.length; i--) {
|
|
2537
|
-
const index = (i + items.length) % items.length;
|
|
2538
|
-
const item = items[index];
|
|
2539
|
-
if (canBeFocused(item)) {
|
|
2540
|
-
return index;
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
return -1;
|
|
2544
|
-
};
|
|
2545
|
-
const getIndexToFocusPrevious = menu => {
|
|
2546
|
-
const startIndex = menu.focusedIndex === -1 ? menu.items.length - 1 : menu.focusedIndex - 1;
|
|
2547
|
-
return getIndexToFocusPreviousStartingAt(menu.items, startIndex);
|
|
2548
|
-
};
|
|
2549
|
-
const canBeFocused = item => {
|
|
2550
|
-
switch (item.flags) {
|
|
2551
|
-
case Separator:
|
|
2552
|
-
case Disabled:
|
|
2553
|
-
return false;
|
|
2554
|
-
default:
|
|
2555
|
-
return true;
|
|
2556
|
-
}
|
|
2557
|
-
};
|
|
2558
|
-
const getIndexToFocusNext = menu => {
|
|
2559
|
-
const startIndex = menu.focusedIndex + 1;
|
|
2560
|
-
return getIndexToFocusNextStartingAt(menu.items, startIndex);
|
|
2561
|
-
};
|
|
2562
|
-
|
|
2563
|
-
// TODO handle printable letter and focus item that starts with that letter
|
|
2564
|
-
|
|
2565
|
-
// TODO pageup / pagedown keys
|
|
2566
|
-
|
|
2567
|
-
// TODO more tests
|
|
2568
|
-
|
|
2569
|
-
const openMenuAtIndex = async (state, index, shouldBeFocused) => {
|
|
2570
|
-
const {
|
|
2571
|
-
titleBarEntries,
|
|
2572
|
-
titleBarHeight,
|
|
2573
|
-
x
|
|
2574
|
-
} = state;
|
|
2575
|
-
// TODO race conditions
|
|
2576
|
-
// TODO send renderer process
|
|
2577
|
-
// 1. open menu, items to show
|
|
2578
|
-
// 2. focus menu
|
|
2579
|
-
const titleBarEntry = titleBarEntries[index];
|
|
2580
|
-
const {
|
|
2581
|
-
id
|
|
2582
|
-
} = titleBarEntry;
|
|
2583
|
-
const items = await getMenuEntries(id);
|
|
2584
|
-
const relevantEntries = titleBarEntries.slice(0, index);
|
|
2585
|
-
const totalWidths = getTotalWidth(relevantEntries);
|
|
2586
|
-
const offset = totalWidths;
|
|
2587
|
-
// TODO race condition: another menu might already be open at this point
|
|
2588
|
-
|
|
2589
|
-
const menuX = x + offset;
|
|
2590
|
-
const menuY = titleBarHeight;
|
|
2591
|
-
const width = getMenuWidth();
|
|
2592
|
-
const height = getMenuHeight(items);
|
|
2593
|
-
const menuFocusedIndex = shouldBeFocused ? getIndexToFocusNextStartingAt(items, 0) : -1;
|
|
2594
|
-
const menu = {
|
|
2595
|
-
id,
|
|
2596
|
-
items,
|
|
2597
|
-
focusedIndex: menuFocusedIndex,
|
|
2598
|
-
level: 0,
|
|
2599
|
-
x: menuX,
|
|
2600
|
-
y: menuY,
|
|
2601
|
-
width,
|
|
2602
|
-
height
|
|
2603
|
-
};
|
|
2604
|
-
const menus = [menu];
|
|
2605
|
-
return {
|
|
2606
|
-
...state,
|
|
2607
|
-
isMenuOpen: true,
|
|
2608
|
-
focusedIndex: index,
|
|
2609
|
-
menus
|
|
2610
|
-
};
|
|
2611
|
-
};
|
|
2612
|
-
|
|
2613
|
-
const focusIndex = async (state, index) => {
|
|
2614
|
-
object(state);
|
|
2615
|
-
number(index);
|
|
2616
|
-
const {
|
|
2617
|
-
isMenuOpen
|
|
2618
|
-
} = state;
|
|
2619
|
-
if (isMenuOpen) {
|
|
2620
|
-
return openMenuAtIndex(state, index, /* focus */false);
|
|
2621
|
-
}
|
|
2622
|
-
return {
|
|
2623
|
-
...state,
|
|
2624
|
-
focusedIndex: index
|
|
2625
|
-
};
|
|
2626
|
-
};
|
|
2627
|
-
|
|
2628
2773
|
const focusFirst = state => {
|
|
2629
2774
|
const indexToFocus = first();
|
|
2630
2775
|
return focusIndex(state, indexToFocus);
|
|
@@ -2662,7 +2807,7 @@ const focusPrevious = state => {
|
|
|
2662
2807
|
|
|
2663
2808
|
const LeftClick = 0;
|
|
2664
2809
|
|
|
2665
|
-
const toggleIndex = (state, index) => {
|
|
2810
|
+
const toggleIndex = async (state, index) => {
|
|
2666
2811
|
const {
|
|
2667
2812
|
isMenuOpen,
|
|
2668
2813
|
focusedIndex
|
|
@@ -2673,7 +2818,7 @@ const toggleIndex = (state, index) => {
|
|
|
2673
2818
|
return openMenuAtIndex(state, index, /* focus */false);
|
|
2674
2819
|
};
|
|
2675
2820
|
|
|
2676
|
-
const handleClick = (state, button, index) => {
|
|
2821
|
+
const handleClick = async (state, button, index) => {
|
|
2677
2822
|
if (button !== LeftClick) {
|
|
2678
2823
|
return state;
|
|
2679
2824
|
}
|
|
@@ -2724,19 +2869,6 @@ const handleKeyArrowDownMenuOpen = state => {
|
|
|
2724
2869
|
};
|
|
2725
2870
|
};
|
|
2726
2871
|
|
|
2727
|
-
const ifElse = (menuOpenFunction, menuClosedFunction) => {
|
|
2728
|
-
const ifElseFunction = (state, ...args) => {
|
|
2729
|
-
const {
|
|
2730
|
-
isMenuOpen
|
|
2731
|
-
} = state;
|
|
2732
|
-
if (isMenuOpen) {
|
|
2733
|
-
return menuOpenFunction(state, ...args);
|
|
2734
|
-
}
|
|
2735
|
-
return menuClosedFunction(state, ...args);
|
|
2736
|
-
};
|
|
2737
|
-
return ifElseFunction;
|
|
2738
|
-
};
|
|
2739
|
-
|
|
2740
2872
|
const handleKeyArrowDown = ifElse(handleKeyArrowDownMenuOpen, handleKeyArrowDownMenuClosed);
|
|
2741
2873
|
|
|
2742
2874
|
const handleKeyArrowLeftMenuClosed = state => {
|
|
@@ -3061,29 +3193,6 @@ const handleMenuMouseOver = async (state, level, index) => {
|
|
|
3061
3193
|
};
|
|
3062
3194
|
};
|
|
3063
3195
|
|
|
3064
|
-
const handleMouseOutMenuClosed = state => {
|
|
3065
|
-
return focusIndex(state, -1);
|
|
3066
|
-
};
|
|
3067
|
-
|
|
3068
|
-
const handleMouseOutMenuOpen = state => {
|
|
3069
|
-
return state;
|
|
3070
|
-
};
|
|
3071
|
-
|
|
3072
|
-
const handleMouseOut = ifElse(handleMouseOutMenuOpen, handleMouseOutMenuClosed);
|
|
3073
|
-
|
|
3074
|
-
const handleMouseOverMenuClosed = (state, index) => {
|
|
3075
|
-
return focusIndex(state, index);
|
|
3076
|
-
};
|
|
3077
|
-
|
|
3078
|
-
const handleMouseOverMenuOpen = async (state, index) => {
|
|
3079
|
-
if (index === -1) {
|
|
3080
|
-
return state;
|
|
3081
|
-
}
|
|
3082
|
-
return focusIndex(state, index);
|
|
3083
|
-
};
|
|
3084
|
-
|
|
3085
|
-
const handleMouseOver = ifElse(handleMouseOverMenuOpen, handleMouseOverMenuClosed);
|
|
3086
|
-
|
|
3087
3196
|
const toggleMenu = state => {
|
|
3088
3197
|
const {
|
|
3089
3198
|
isMenuOpen
|
|
@@ -3125,6 +3234,7 @@ const commandMap = {
|
|
|
3125
3234
|
'TitleBarMenuBar.getCommands': getCommandIds,
|
|
3126
3235
|
'TitleBarMenuBar.getKeyBindings': getKeyBindings,
|
|
3127
3236
|
'TitleBarMenuBar.getMenus': getMenus,
|
|
3237
|
+
'TitleBarMenuBar.diff': diff,
|
|
3128
3238
|
'TitleBarMenuBar.getVirtualDom': getTitleBarMenuBarVirtualDom,
|
|
3129
3239
|
'TitleBarMenuBar.handleClick': wrapCommand(handleClick),
|
|
3130
3240
|
'TitleBarMenuBar.handleFocus': wrapCommand(handleFocus),
|
|
@@ -3132,8 +3242,10 @@ const commandMap = {
|
|
|
3132
3242
|
'TitleBarMenuBar.handleKeyArrowLeft': wrapCommand(handleKeyArrowLeft),
|
|
3133
3243
|
'TitleBarMenuBar.handleKeyArrowRight': wrapCommand(handleKeyArrowRight),
|
|
3134
3244
|
'TitleBarMenuBar.handleKeyArrowUp': wrapCommand(handleKeyArrowUp),
|
|
3245
|
+
'TitleBarMenuBar.handlePointerOver': wrapCommand(handlePointerOver),
|
|
3135
3246
|
'TitleBarMenuBar.handleKeyEnd': wrapCommand(handleKeyEnd),
|
|
3136
3247
|
'TitleBarMenuBar.handleKeyEnter': wrapCommand(handleKeyEnter),
|
|
3248
|
+
'TitleBarMenuBar.handlePointerOut': wrapCommand(handlePointerOut),
|
|
3137
3249
|
'TitleBarMenuBar.handleKeyEscape': wrapCommand(handleKeyEscape),
|
|
3138
3250
|
'TitleBarMenuBar.handleKeyHome': wrapCommand(handleKeyHome),
|
|
3139
3251
|
'TitleBarMenuBar.handleKeySpace': wrapCommand(handleKeySpace),
|