diff --git a/desktop.css b/desktop.css index 86e02aca..6ff70d2a 100755 --- a/desktop.css +++ b/desktop.css @@ -16,6 +16,8 @@ --hover: #ffffff65; --hover-half: #ffffff40; --hover-b: #1111110f; + --pressed: #ffffff50; + --taskbar-bg: rgba(243, 243, 243, 0.85); --bggrey: #eee; --fill: #d3d3d370; --bgul: url('bg.svg'); @@ -44,6 +46,8 @@ --hover: #aaaaaa30; --hover-half: #ffffff20; --hover-b: #eeeeee0f; + --pressed: #aaaaaa20; + --taskbar-bg: rgba(24, 24, 24, 0.85); --bggrey: #444; --fill: #99999970; --bgul: url('bg-dark.svg'); @@ -429,45 +433,53 @@ list>span.focs.cl { #dock-box { position: fixed; - bottom: 10px; - height: 40px; + bottom: 0; + height: 48px; display: flex; width: 100%; z-index: 92; - padding: 0 5px; - justify-content: center; - transition: bottom 400ms; + padding: 0 10px; + justify-content: flex-start; + transition: bottom 400ms, transform 0.3s ease; + background-color: var(--taskbar-bg, rgba(24, 24, 24, 0.85)); + backdrop-filter: blur(30px) saturate(1.8); + -webkit-backdrop-filter: blur(30px) saturate(1.8); + border-top: 1px solid rgba(255, 255, 255, 0.08); } #dock-box.hide { - bottom: -60px; + bottom: -48px; } .dock { - border-radius: 10px; + border-radius: 0; height: 100%; - background-color: var(--contextmeu); - backdrop-filter: blur(20px) saturate(2); - -webkit-backdrop-filter: blur(20px) saturate(2); - box-shadow: 0 1px 2px #44444460, 0px 3px 25px 1px var(--shadow); - padding: 7px 6px; - margin: 0 3px; - outline: 1px solid #99999950; + background-color: transparent; + backdrop-filter: none; + -webkit-backdrop-filter: none; + box-shadow: none; + padding: 0; + margin: 0; + outline: none; display: flex; - transition: 200ms, transform 300ms cubic-bezier(0.14, 1.02, 0.17, 0.03), backdrop-filter, -webkit-backdrop-filter 0ms; + transition: background-color 100ms ease; bottom: 0; - /* transform: scale(1); */ - /* border: 1.5px solid #6f6f6f30; */ + position: relative; + overflow: hidden; +} + +.dock:hover { + background-color: var(--hover); } :root.corner_squ .dock { - corner-shape: squircle; - border-radius: 20px; + corner-shape: initial; + border-radius: 0; } .dock>* { - padding-left: 3px; - padding-right: 3px; + padding-left: 6px; + padding-right: 6px; } .dock.control:hover, @@ -478,27 +490,33 @@ list>span.focs.cl { } #toolbar { - display: flex; + display: none; } #taskbar { - padding: 3px 2px; - transition: 80ms; - overflow: hidden; - width: 0; + padding: 0; + transition: width 300ms ease; + overflow: visible; + width: auto; + display: none; + flex: 1; + min-width: 0; } #taskbar>a { - border-radius: 10px; - height: 34px; - width: 38px; - transition: 100ms; - padding: 4px 5px; - display: block; + border-radius: 0; + height: 48px; + width: 48px; + min-width: 48px; + transition: background-color 80ms ease; + padding: 0; + display: flex; + justify-content: center; + align-items: center; margin: 0; background-clip: padding-box; - border-left: 2px solid transparent; - border-right: 2px solid transparent; + border: none; + position: relative; } #taskbar>a:hover { @@ -506,14 +524,14 @@ list>span.focs.cl { } #taskbar>a:active>img { - transform: scale(0.8); + transform: scale(0.85); } #taskbar>a>img { - width: 26px; - height: 26px; + width: 28px; + height: 28px; transition: 100ms; - margin: -1px 0 0 -0.5px; + margin: 0; } #taskbar>a:not(.min)>img { @@ -581,72 +599,95 @@ list>span.focs.cl { #taskbar>a::after { content: ''; display: block; - width: 18px; + width: 16px; background: linear-gradient(90deg, var(--theme-1), var(--theme-2)); - height: 4px; - border-radius: 2px; - position: relative; - bottom: 4px; - left: 3.2px; - transition: 150ms; + height: 3px; + border-radius: 0; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + transition: width 150ms ease, opacity 150ms ease; + opacity: 0; +} + +#taskbar>a.foc::after { + opacity: 1; + width: 16px; +} + +#taskbar>a:hover::after { + opacity: 1; + width: 48px; } #taskbar>a:not(.foc)::after { background: linear-gradient(90deg, #7f7f7f, #7f7f7f); - width: 12px; - left: 6px; } #taskbar>a:not(.foc):hover:after { - width: 16px; - left: 4.2px; + width: 48px; } #taskbar>a.foc:hover:after { - width: 22px; - left: 1.2px; + width: 48px; } #taskbar>a.min:hover:after { - width: 18px; - left: 3.2px; + width: 48px; } .dock.date { - width: 100px; - padding: 0px 5px; - display: block; - text-align: center; + height: 100%; + width: auto; + min-width: 80px; + padding: 0 12px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; overflow: hidden; + cursor: pointer; + transition: background-color 100ms ease; +} + +.dock.date:hover { + background-color: var(--hover); } .dock.date:active { - transform: scale(0.9); - filter: opacity(0.6) !important; + background-color: var(--pressed); } .dock.control { + height: 100%; + padding: 0 8px; display: flex; justify-content: center; align-items: center; - gap: 5px; -} - -.dock.control>icon { - font-size: 17px; + gap: 8px; + cursor: pointer; + transition: background-color 100ms ease; } -.dock.control>* { - margin: 0 -2px 0 -1px; +.dock.control:hover { + background-color: var(--hover); } .dock.control:active { - transform: scale(0.9); - filter: opacity(0.6) !important; + background-color: var(--pressed); } .dock.date>.date { - margin-top: -8px; + margin-top: 0; + font-size: 12px; + line-height: 1.3; +} + +.dock.date>.time { + font-size: 12px; + line-height: 1.3; + margin-bottom: 2px; } .dock.date>p { @@ -658,38 +699,37 @@ list>span.focs.cl { } .dock.date>.bi { - position: absolute; - display: block; - text-align: center; - left: 0; - top: 37px; - width: 100%; - transition: 200ms cubic-bezier(0.9, 0, 0.1, 1); + display: none; } -.dock.date.show>.bi { - top: 7px; +.dock.theme { + margin-left: auto; + width: 48px; + height: 100%; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + overflow: visible; + position: relative; + cursor: pointer; + transition: background-color 100ms ease; } -.dock.theme { - width: 40px; - padding-left: 3.5px; - text-align: center; - overflow: hidden; - box-shadow: 0 0 6px 1px var(--shadow); +.dock.theme:hover { + background-color: var(--hover); } .dock.theme:active { - transform: scale(0.9); - filter: opacity(0.6) !important; + background-color: var(--pressed); } .dock.theme>.dark { position: absolute; height: 30px; width: 30px; - top: 5px; - left: 40px; + top: 9px; + left: 9px; transition: 200ms cubic-bezier(0.9, 0, 0.1, 1); opacity: 0; } @@ -698,26 +738,27 @@ list>span.focs.cl { height: 30px; width: 30px; position: absolute; - top: 5px; - left: 5px; + top: 9px; + left: 9px; transition: 200ms cubic-bezier(0.9, 0, 0.1, 1); opacity: 1; } .dock.theme.dk>.dark { - left: 5px; + left: 9px; opacity: 1; } .dock.theme.dk>.light { - left: -30px; + left: 9px; opacity: 0; } .dock-btn { transition: 200ms; - width: 32px; - height: 26px; + width: 38px; + height: 34px; + margin-top: 10px; overflow: hidden; } @@ -726,28 +767,17 @@ list>span.focs.cl { filter: brightness(1.2); } -#start-btn>svg.menu { - position: relative; - top: 27px; - transition: 200ms; -} - -#start-btn.show>svg.menu { - top: 0; - transition-delay: 200ms; +.dock-btn:active { + transform: scale(0.85); } -#start-btn>.ico { +#start-btn { position: relative; - top: -32px; - transition: transform 200ms, top 200ms 100ms, left 200ms; - left: 0; - /* transition-delay: 100ms; */ + transition: all 200ms ease; } -#start-btn.show>.ico { - transform: scale(0.5); - top: -26px; +#start-btn.show { + filter: drop-shadow(0 0 4px var(--theme-1)); } #search-btn>svg.in { @@ -868,12 +898,15 @@ list>span.focs.cl { #start-menu { height: 594px; max-height: calc(100% - 120px); - width: 900px; - left: calc(50% - 450px); + width: 800px; + left: 12px; box-shadow: 3px 3px 25px 1px var(--shadow); padding: 2px 4px; bottom: 60px; - transform: translateY(700px); + transform: translateY(50px) scale(0.8); + transform-origin: bottom left; + opacity: 0; + transition: transform 200ms cubic-bezier(0.1, 0.9, 0.2, 1), opacity 200ms cubic-bezier(0.1, 0.9, 0.2, 1); } #start-menu.show-begin { @@ -882,16 +915,17 @@ list>span.focs.cl { #start-menu.show { opacity: 1; - transform: none !important; + transform: translateY(0) scale(1) !important; } #start-menu.max { left: 0; - bottom: 0; - transform: translateY(100%); + bottom: 48px; + transform: none; + opacity: 1; width: 100%; max-height: 100%; - height: 100%; + height: calc(100% - 48px); border-radius: 0; transition: 200ms cubic-bezier(0.9, 0, 0.1, 1); } @@ -966,26 +1000,41 @@ input-before { #startmenu-r { flex-grow: 1; - overflow: hidden; - padding-bottom: 10px; + overflow: auto; + padding: 10px; + display: grid; + grid-template-columns: repeat(auto-fill, 100px); + grid-auto-rows: 100px; + gap: 6px; + align-content: start; + position: relative; +} + +#startmenu-r>.row1 { + position: absolute; + bottom: 10px; + right: 10px; display: flex; - flex-direction: column; + justify-content: flex-end; + align-items: flex-end; + transition-delay: 200ms; + z-index: 10; } -#startmenu-r>* { +#startmenu-r>.tile { opacity: 0; transition: transform 200ms cubic-bezier(0, 0, 0, 1), opacity 200ms 30ms; transform: translate(0, 50px); } -#start-menu.show>#startmenu-r>* { +#start-menu.show>#startmenu-r>.tile { transform: none; opacity: 1; } -#startmenu-r>.row1 { - display: flex; - transition-delay: 200ms; +#start-menu.show>#startmenu-r>.row1 { + transform: none; + opacity: 1; } #startmenu-r>.row1>.tool { @@ -1056,8 +1105,6 @@ input-before { margin-right: 50px; } -#startmenu-r>.row1>.folder, -#startmenu-r>.pinned, #startmenu-r>.tuijian { background-color: var(--card); box-shadow: 0px 1px 8px var(--s3d); @@ -1067,51 +1114,217 @@ input-before { :root.corner_squ { - #startmenu-r>.row1>.folder, - #startmenu-r>.pinned, #startmenu-r>.tuijian { corner-shape: squircle; border-radius: 25px; } } -#startmenu-r>.row1>.folder { +.tile { display: flex; - margin: 15px 15px; - flex-grow: 1; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 8px; + background-color: var(--card-bg, rgba(255, 255, 255, 0.05)); + transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1); + cursor: pointer; + position: relative; + overflow: hidden; + text-decoration: none; + color: var(--text); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); } -#startmenu-r>.row1>.folder>.sm-app { - height: 72px; +.a.tile { + text-decoration: none; + color: inherit; } -#startmenu-r>.pinned { - margin-right: 10px; - margin-left: 15px; - transition-delay: 300ms; - height: 220px; - display: flex; - flex-direction: column; +.tile::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 50%, rgba(0,0,0,0.05) 100%); + opacity: 1; + transition: opacity 200ms ease; + pointer-events: none; } -#startmenu-r>.pinned>.apps { - display: flex; - flex-wrap: wrap; - padding-left: 10px; - overflow: auto; - flex-grow: 1; +.tile::after { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle at center, rgba(255,255,255,0.15) 0%, transparent 70%); + opacity: 0; + transition: opacity 200ms ease; + pointer-events: none; } +.tile:hover::after { + opacity: 1; +} -#startmenu-r>.pinned>.title { - padding: 5px 0 5px 10px; - font-size: 18px; - font-weight: 530; - display: flex; +.tile:hover { + transform: scale(1.03) translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); } -#startmenu-r>.pinned>.title>* { - width: 100%; +.tile:active { + transform: scale(0.97); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); +} + +.tile.medium { + grid-column: span 1; + grid-row: span 1; +} + +.tile.wide { + grid-column: span 2; + grid-row: span 1; +} + +.tile.large { + grid-column: span 2; + grid-row: span 2; +} + +.tile>img { + width: 32px; + height: 32px; + margin-bottom: 6px; + transition: transform 150ms ease; +} + +.tile:hover>img { + transform: scale(1.1); +} + +.tile>p { + font-size: 11px; + text-align: center; + line-height: 1.2; + padding: 0 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} + +.tile.wide>p, +.tile.large>p { + max-width: calc(100% - 8px); +} + +.tile.setting { + background: linear-gradient(135deg, #6B6B6B 0%, #4A4A4A 100%); + color: #fff; +} +.tile.explorer { + background: linear-gradient(135deg, #FFB900 0%, #E6A800 100%); + color: #fff; +} +.tile.edge { + background: linear-gradient(135deg, #0078D7 0%, #005A9E 100%); + color: #fff; +} +.tile.msstore { + background: linear-gradient(135deg, #0078D4 0%, #005A9E 100%); + color: #fff; +} +.tile.calc { + background: linear-gradient(135deg, #744DA9 0%, #5B2C8E 100%); + color: #fff; +} +.tile.notepad { + background: linear-gradient(135deg, #4A90D9 0%, #357ABD 100%); + color: #fff; +} +.tile.terminal { + background: linear-gradient(135deg, #2D2D2D 0%, #1A1A1A 100%); + color: #fff; +} +.tile.defender { + background: linear-gradient(135deg, #0078D4 0%, #005A9E 100%); + color: #fff; +} +.tile.camera { + background: linear-gradient(135deg, #4A90D9 0%, #357ABD 100%); + color: #fff; +} +.tile.about { + background: linear-gradient(135deg, #744DA9 0%, #5B2C8E 100%); + color: #fff; +} +.tile.windows12 { + background: linear-gradient(135deg, #0078D4 0%, #005A9E 100%); + color: #fff; +} + +.tile.run { + background: linear-gradient(135deg, #0078D7 0%, #005A9E 100%); + color: #fff; +} + +.tile.taskmgr { + background: linear-gradient(135deg, #107C10 0%, #0B5B0B 100%); + color: #fff; +} + +.tile.copilot { + background: linear-gradient(135deg, #9B4DCA 0%, #3B91D8 100%); + color: #fff; +} + +.tile.pythonEditor { + background: linear-gradient(135deg, #3776AB 0%, #2B5B84 100%); + color: #fff; +} + +.tile.python { + background: linear-gradient(135deg, #3776AB 0%, #FFD43B 100%); + color: #fff; +} + +.tile.whiteboard { + background: linear-gradient(135deg, #17A589 0%, #0E6655 100%); + color: #fff; +} + +.tile.word { + background: linear-gradient(135deg, #2B579A 0%, #1B3A64 100%); + color: #fff; +} + +.tile.minesweeper { + background: linear-gradient(135deg, #6A5ACD 0%, #4B3FBF 100%); + color: #fff; +} + +.tile.bilibili { + background: linear-gradient(135deg, #FB7299 0%, #D4567A 100%); + color: #fff; +} + +.tile.vscode { + background: linear-gradient(135deg, #007ACC 0%, #005A9E 100%); + color: #fff; +} + +.tile.wsa { + background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%); + color: #fff; +} + +.tile:hover { + filter: brightness(1.15); } #startmenu-r>.tuijian { @@ -1288,9 +1501,13 @@ input-before { height: 594px; max-height: calc(100% - 120px); width: 600px; - left: calc(50% - 300px); + left: 12px; padding: 15px; - transform: translateY(660px); + bottom: 60px; + transform: translateY(50px) scale(0.8); + transform-origin: bottom left; + opacity: 0; + transition: transform 200ms cubic-bezier(0.1, 0.9, 0.2, 1), opacity 200ms cubic-bezier(0.1, 0.9, 0.2, 1); } @@ -1433,9 +1650,12 @@ input-before { height: 600px; max-height: calc(90% - 50px); width: 1000px; - left: calc(50% - 500px); + left: 12px; padding: 10px 0; - transform: translateY(660px); + transform: translateY(50px) scale(0.8); + transform-origin: bottom left; + opacity: 0; + transition: transform 200ms cubic-bezier(0.1, 0.9, 0.2, 1), opacity 200ms cubic-bezier(0.1, 0.9, 0.2, 1); } #widgets.show-begin { @@ -1857,7 +2077,21 @@ input-before { margin: 8px 10px 2px 10px; padding: 0 3px 3px 3px; z-index: 3; - transition: 80ms; + transition: opacity 80ms, box-shadow 80ms; + user-select: none; + -webkit-user-select: none; +} + +#desktop>div.dragging { + opacity: 0.85; + z-index: 100; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4); + transition: none; +} + +#desktop>div.selected { + background-color: var(--hover-half); + border: 1px solid var(--href); } @@ -2428,10 +2662,7 @@ input-before { } .window.min { - top: calc(95%) !important; - left: 15% !important; - transform: scale(0.3) !important; - transition: cubic-bezier(0.9, 0, 0.1, 1) 200ms, top 200ms 100ms, backdrop-filter background-color 0s; + opacity: 0 !important; } .window.show-begin { @@ -2540,16 +2771,11 @@ github-red2026 */ /* 定义当窗口处于最大状态时的样式 */ .window.max { width: 100% !important; - height: 100% !important; - - - /* 定位属性,确保窗口从屏幕的左上角开始 */ + height: calc(100% - 48px) !important; left: 0 !important; top: 0 !important; - /* 移除圆角和边框,使窗口边缘与屏幕边缘无缝对接 */ border-radius: 0; border: none; - /* 定义过渡效果,确保窗口在最大化时有平滑的过渡 */ transition: cubic-bezier(0.9, 0, 0.1, 1) 200ms, top 200ms 100ms, backdrop-filter, background 0ms; } @@ -2818,34 +3044,35 @@ body>.container>.image-container>.svg { #taskbar-preview { position: fixed; bottom: 60px; - background: var(--card-bg); - backdrop-filter: blur(20px) saturate(1.5); - -webkit-backdrop-filter: blur(20px) saturate(1.5); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--bg70); + backdrop-filter: blur(30px) saturate(1.8); + -webkit-backdrop-filter: blur(30px) saturate(1.8); + box-shadow: 0 12px 48px rgba(0, 0, 0, 0.2); + border: 1px solid var(--bd); border-radius: 8px; padding: 12px; - display: none; - z-index: 10000; - transition: all 0.2s ease-out; + display: block; + visibility: hidden; opacity: 0; - transform: translateY(10px); + transform: translateY(8px) scale(0.95); + z-index: 10000; + transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease; pointer-events: none; } #taskbar-preview.show { - display: block; + visibility: visible; opacity: 1; - transform: translateY(0); + transform: translateY(0) scale(1); } #taskbar-preview .preview-title { font-size: 13px; - margin-bottom: 10px; + font-weight: 500; + margin-bottom: 8px; display: flex; align-items: center; color: var(--text); - opacity: 0.9; } #taskbar-preview .preview-title img { @@ -2855,19 +3082,20 @@ body>.container>.image-container>.svg { } #taskbar-preview .preview-content { - width: 280px; - height: 180px; - background: var(--hover); - border-radius: 6px; + width: 320px; + height: 200px; + background: var(--card); + border-radius: 4px; overflow: hidden; - border: 1px solid rgba(255, 255, 255, 0.05); + border: 1px solid var(--bd); } #taskbar-preview .preview-content img { width: 100%; height: 100%; - object-fit: cover; + object-fit: contain; pointer-events: none; + background: var(--bg); } @media screen and (max-width: 768px) { diff --git a/desktop.html b/desktop.html index 22ee8817..2a7f0743 100755 --- a/desktop.html +++ b/desktop.html @@ -360,27 +360,57 @@
+ + + +

设置

+
+ + +

文件资源管理器

+
+ + +

Microsoft Edge

+
+ + +

Microsoft Store

+
+ + +

计算器

+
+ + +

记事本

+
+ + +

终端

+
+ + +

Windows 安全中心

+
+ + +

相机

+
+ + +

关于 Win12 网页版

+
+ + +

Windows 12

+
+ + +

反馈中心

+
-
@@ -793,27 +749,6 @@
- - - - - - - - - - - - - - diff --git a/desktop.js b/desktop.js index 51589981..0573e7f2 100755 --- a/desktop.js +++ b/desktop.js @@ -188,13 +188,451 @@ $('input,textarea,*[contenteditable=true]').on('contextmenu', (e) => { stop(e); return true; }); -// 给桌面上的图标加右键菜单 +var desktopIconPositions = {}; +var selectedIcons = new Set(); +var lastSelectedIcon = null; + +const GRID_SIZE = 85; + +function selectIcon($icon, append = false) { + const appname = $icon.attr('appname'); + + if (!append) { + clearSelection(); + } + + selectedIcons.add(appname); + $icon.addClass('selected'); + lastSelectedIcon = appname; +} + +function deselectIcon($icon) { + const appname = $icon.attr('appname'); + selectedIcons.delete(appname); + $icon.removeClass('selected'); +} + +function clearSelection() { + selectedIcons.forEach(appname => { + $(`#desktop>div[appname="${appname}"]`).removeClass('selected'); + }); + selectedIcons.clear(); + lastSelectedIcon = null; +} + +function selectRange(startAppname, endAppname) { + const icons = $('#desktop>div[appname]').toArray(); + const startIndex = icons.findIndex(el => $(el).attr('appname') === startAppname); + const endIndex = icons.findIndex(el => $(el).attr('appname') === endAppname); + + if (startIndex === -1 || endIndex === -1) return; + + const minIndex = Math.min(startIndex, endIndex); + const maxIndex = Math.max(startIndex, endIndex); + + for (let i = minIndex; i <= maxIndex; i++) { + const $icon = $(icons[i]); + const appname = $icon.attr('appname'); + selectedIcons.add(appname); + $icon.addClass('selected'); + } +} + +function getSelectedIcons() { + return Array.from(selectedIcons).map(appname => $(`#desktop>div[appname="${appname}"]`)); +} + +function findEmptyGridPosition() { + const $desktop = $('#desktop'); + const desktopRect = $desktop[0].getBoundingClientRect(); + const maxCols = Math.floor(desktopRect.width / GRID_SIZE); + const maxRows = Math.floor(desktopRect.height / GRID_SIZE); + + // 获取所有已占用的网格位置 + const occupied = new Set(); + $('#desktop>div[appname]').each(function() { + const $icon = $(this); + let left = parseFloat($icon.css('left')); + let top = parseFloat($icon.css('top')); + + // 如果图标没有绝对定位,计算它在flex布局中的位置 + if (isNaN(left) || isNaN(top) || $icon.css('position') !== 'absolute') { + const rect = this.getBoundingClientRect(); + left = rect.left - desktopRect.left; + top = rect.top - desktopRect.top; + } + + const pos = getIconGridPosition(left, top); + occupied.add(`${pos.col},${pos.row}`); + }); + + // 按列优先顺序查找空位,从第2列开始(第0列留给系统图标) + for (let col = 1; col < maxCols; col++) { + for (let row = 0; row < maxRows; row++) { + if (!occupied.has(`${col},${row}`)) { + return { col, row }; + } + } + } + + // 如果没有空位,返回第2列第1行 + return { col: 1, row: 0 }; +} + +function createDesktopLink(appId, appName) { + const pos = findEmptyGridPosition(); + const left = pos.col * GRID_SIZE; + const top = pos.row * GRID_SIZE; + + const html = `
+ +

${appName}

+
`; + + $('#desktop').append(html); + desktopItem.push(html); + addMenu(); + saveDesktop(); + initDesktopIconDrag(); +} + +function snapToGrid(value, gridSize) { + return Math.round(value / gridSize) * gridSize; +} + +function getIconGridPosition(left, top) { + return { + col: Math.round(left / GRID_SIZE), + row: Math.round(top / GRID_SIZE) + }; +} + +function isPositionOccupied(col, row, excludeElement) { + let occupied = false; + $('#desktop>div[appname]').each(function() { + if (this === excludeElement) return; + + const $icon = $(this); + let left = parseFloat($icon.css('left')); + let top = parseFloat($icon.css('top')); + + // 如果图标没有绝对定位,计算它在flex布局中的位置 + if (isNaN(left) || isNaN(top) || $icon.css('position') !== 'absolute') { + const desktopRect = $('#desktop')[0].getBoundingClientRect(); + const rect = this.getBoundingClientRect(); + left = rect.left - desktopRect.left; + top = rect.top - desktopRect.top; + } + + const pos = getIconGridPosition(left, top); + if (pos.col === col && pos.row === row) { + occupied = true; + return false; + } + }); + return occupied; +} + +function findNearestEmptyGrid(col, row, excludeElement, maxCols, maxRows) { + if (!isPositionOccupied(col, row, excludeElement)) { + return { col, row }; + } + + for (let radius = 1; radius < Math.max(maxCols, maxRows); radius++) { + for (let dx = -radius; dx <= radius; dx++) { + for (let dy = -radius; dy <= radius; dy++) { + if (Math.abs(dx) !== radius && Math.abs(dy) !== radius) continue; + + const newCol = col + dx; + const newRow = row + dy; + + if (newCol < 0 || newRow < 0 || newCol >= maxCols || newRow >= maxRows) continue; + + if (!isPositionOccupied(newCol, newRow, excludeElement)) { + return { col: newCol, row: newRow }; + } + } + } + } + + return { col, row }; +} + +function snapAllIconsToGrid() { + const $desktop = $('#desktop'); + const desktopRect = $desktop[0].getBoundingClientRect(); + const maxX = desktopRect.width - 85; + const maxY = desktopRect.height - 84; + + $('#desktop>div[appname]').each(function() { + const $icon = $(this); + + let currentLeft = parseFloat($icon.css('left')); + let currentTop = parseFloat($icon.css('top')); + + if (isNaN(currentLeft) || $icon.css('position') !== 'absolute') { + const rect = this.getBoundingClientRect(); + currentLeft = rect.left - desktopRect.left; + currentTop = rect.top - desktopRect.top; + } + + const snappedLeft = snapToGrid(currentLeft, GRID_SIZE); + const snappedTop = snapToGrid(currentTop, GRID_SIZE); + + $icon.css({ + position: 'absolute', + left: Math.max(0, Math.min(snappedLeft, maxX)) + 'px', + top: Math.max(0, Math.min(snappedTop, maxY)) + 'px' + }); + }); + + saveIconPositions(); +} + +function initDesktopIconDrag() { + $('#desktop').off('mousedown.desktopdrag touchstart.desktopdrag dragstart.desktopdrag'); + + $('#desktop').on('dragstart.desktopdrag', '>div[appname], >div[appname] img', function(e) { + e.preventDefault(); + return false; + }); + + $('#desktop').on('mousedown.desktopdrag touchstart.desktopdrag', '>div[appname]', function(e) { + if (e.button === 2) return; + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) { + return; + } + + const $icon = $(this); + const appname = $icon.attr('appname'); + + // 处理多选逻辑 + if (e.ctrlKey || e.metaKey) { + if (selectedIcons.has(appname)) { + deselectIcon($icon); + } else { + selectIcon($icon, true); + } + return; + } else if (e.shiftKey && lastSelectedIcon) { + selectRange(lastSelectedIcon, appname); + return; + } else if (!selectedIcons.has(appname)) { + selectIcon($icon, false); + } + + e.preventDefault(); + e.stopPropagation(); + + const startEvent = e.type === 'touchstart' ? e.originalEvent.touches[0] : e; + const startX = startEvent.clientX; + const startY = startEvent.clientY; + + let isDragging = false; + let offsetLeft, offsetTop; + + const $desktop = $('#desktop'); + const desktopRect = $desktop[0].getBoundingClientRect(); + + const currentLeft = parseFloat($icon.css('left')); + const currentTop = parseFloat($icon.css('top')); + + if (isNaN(currentLeft) || $icon.css('position') !== 'absolute') { + const rect = this.getBoundingClientRect(); + offsetLeft = rect.left - desktopRect.left; + offsetTop = rect.top - desktopRect.top; + } else { + offsetLeft = currentLeft; + offsetTop = currentTop; + } + + function onMove(moveEvent) { + moveEvent.preventDefault(); + moveEvent.stopPropagation(); + + const moveData = moveEvent.type === 'touchmove' ? moveEvent.originalEvent.touches[0] : moveEvent; + const deltaX = moveData.clientX - startX; + const deltaY = moveData.clientY - startY; + + if (Math.abs(deltaX) < 5 && Math.abs(deltaY) < 5) { + return; + } + + if (!isDragging) { + isDragging = true; + isIconDragging = true; + + // 多选拖拽时,记录所有选中图标的初始位置 + const selectedIconsList = getSelectedIcons(); + if (selectedIconsList.length > 1 && selectedIcons.has(appname)) { + $icon.data('multi-drag', { + icons: selectedIconsList.map($el => ({ + $el: $el, + origLeft: parseFloat($el.css('left')) || $el[0].getBoundingClientRect().left - desktopRect.left, + origTop: parseFloat($el.css('top')) || $el[0].getBoundingClientRect().top - desktopRect.top + })) + }); + } + + $icon.addClass('dragging'); + $icon.css({ + position: 'absolute', + left: offsetLeft + 'px', + top: offsetTop + 'px' + }); + } + + const newLeft = offsetLeft + deltaX; + const newTop = offsetTop + deltaY; + + const maxX = desktopRect.width - 85; + const maxY = desktopRect.height - 84; + + $icon.css({ + left: Math.max(0, Math.min(newLeft, maxX)) + 'px', + top: Math.max(0, Math.min(newTop, maxY)) + 'px' + }); + + // 多选拖拽时,同步移动其他选中的图标 + const multiDrag = $icon.data('multi-drag'); + if (multiDrag) { + multiDrag.icons.forEach(icon => { + if (icon.$el[0] !== $icon[0]) { + const iconNewLeft = icon.origLeft + deltaX; + const iconNewTop = icon.origTop + deltaY; + icon.$el.css({ + position: 'absolute', + left: Math.max(0, Math.min(iconNewLeft, maxX)) + 'px', + top: Math.max(0, Math.min(iconNewTop, maxY)) + 'px' + }); + } + }); + } + } + + function onEnd(endEvent) { + endEvent.stopPropagation(); + + $(document).off('mousemove.desktopdrag mouseup.desktopdrag'); + $(document).off('touchmove.desktopdrag touchend.desktopdrag touchcancel.desktopdrag'); + + if (isDragging) { + isIconDragging = false; + $icon.removeClass('dragging'); + + const maxX = desktopRect.width - 85; + const maxY = desktopRect.height - 84; + const maxCols = Math.floor(desktopRect.width / GRID_SIZE); + const maxRows = Math.floor(desktopRect.height / GRID_SIZE); + + // 处理多选图标的最终位置 + const multiDrag = $icon.data('multi-drag'); + if (multiDrag) { + multiDrag.icons.forEach(icon => { + const iconLeft = parseFloat(icon.$el.css('left')); + const iconTop = parseFloat(icon.$el.css('top')); + const iconSnappedLeft = snapToGrid(iconLeft, GRID_SIZE); + const iconSnappedTop = snapToGrid(iconTop, GRID_SIZE); + const iconGridPos = getIconGridPosition(iconSnappedLeft, iconSnappedTop); + const iconEmptyGrid = findNearestEmptyGrid( + iconGridPos.col, + iconGridPos.row, + icon.$el[0], + maxCols, + maxRows + ); + + icon.$el.css({ + left: Math.max(0, Math.min(iconEmptyGrid.col * GRID_SIZE, maxX)) + 'px', + top: Math.max(0, Math.min(iconEmptyGrid.row * GRID_SIZE, maxY)) + 'px' + }); + }); + $icon.removeData('multi-drag'); + } else { + // 单图标处理 + const currentLeft = parseFloat($icon.css('left')); + const currentTop = parseFloat($icon.css('top')); + + const snappedLeft = snapToGrid(currentLeft, GRID_SIZE); + const snappedTop = snapToGrid(currentTop, GRID_SIZE); + + const currentGridPos = getIconGridPosition(snappedLeft, snappedTop); + const emptyGrid = findNearestEmptyGrid( + currentGridPos.col, + currentGridPos.row, + $icon[0], + maxCols, + maxRows + ); + + const finalLeft = emptyGrid.col * GRID_SIZE; + const finalTop = emptyGrid.row * GRID_SIZE; + + $icon.css({ + left: Math.max(0, Math.min(finalLeft, maxX)) + 'px', + top: Math.max(0, Math.min(finalTop, maxY)) + 'px' + }); + } + + saveIconPositions(); + } + } + + if (e.type === 'touchstart') { + $(document).on('touchmove.desktopdrag', onMove); + $(document).on('touchend.desktopdrag touchcancel.desktopdrag', onEnd); + } else { + $(document).on('mousemove.desktopdrag', onMove); + $(document).on('mouseup.desktopdrag', onEnd); + } + }); +} + +function saveIconPositions() { + const positions = {}; + $('#desktop>div[appname]').each(function(index) { + const appname = $(this).attr('appname'); + const left = parseFloat($(this).css('left')); + const top = parseFloat($(this).css('top')); + if (!isNaN(left) && !isNaN(top)) { + // 使用索引+appname作为唯一key,避免同名图标冲突 + positions[`${index}_${appname}`] = { left, top }; + } + }); + localStorage.setItem('desktopIconPositions', JSON.stringify(positions)); +} + +function restoreIconPositions() { + const saved = localStorage.getItem('desktopIconPositions'); + if (!saved) return; + + try { + const positions = JSON.parse(saved); + if (!positions || typeof positions !== 'object') return; + + $('#desktop>div[appname]').each(function(index) { + const appname = $(this).attr('appname'); + const key = `${index}_${appname}`; + if (positions[key]) { + const pos = positions[key]; + $(this).css({ + position: 'absolute', + left: pos.left + 'px', + top: pos.top + 'px' + }); + } + }); + } catch (e) { + console.error('Failed to restore icon positions:', e); + } +} + function addMenu() { var parentDiv = $('#desktop')[0]; var childDivs = parentDiv.$$('#div'); for (var i = 0; i < childDivs.length; i++) { - if (i <= 4) {//win12内置的5个图标不添加 + if (i <= 4) { continue; } let div = childDivs[i]; @@ -279,6 +717,7 @@ const cms = { return [' ' + lang('进入编辑模式', 'desktop.enteredit'), 'editMode();']; } }, + [' ' + lang('对齐到网格', 'desktop.snapgrid'), 'snapAllIconsToGrid();'], [' ' + lang('关于 Win12 网页版', 'about.name'), '$(\'#win-about>.about\').addClass(\'show\');$(\'#win-about>.update\').removeClass(\'show\');openapp(\'about\');if($(\'.window.about\').hasClass(\'min\'))minwin(\'about\');'], [' ' + lang('个性化', 'psnl'), 'openapp(\'setting\');$(\'#win-setting > div.menu > list > a.enable.appearance\')[0].click()'] ], @@ -323,10 +762,10 @@ const cms = { return [' ' + lang('打开', 'open'), `openapp('${arg[0]}');hide_startmenu();`]; }, arg => { - return [' 在桌面创建链接', 'var s=`

' + arg[1] + '

`;$(\'#desktop\').append(s);desktopItem[desktopItem.length]=s;addMenu();saveDesktop();']; + return [' 在桌面创建链接', `createDesktopLink('${arg[0]}', '${arg[1]}')`]; }, arg => { - return [' 取消固定', `$('#startmenu-r>.pinned>.apps>.sm-app.${arg[0]}').remove()`]; + return [' 取消固定', `$('#startmenu-r>.tile.${arg[0]}').remove()`]; } ], 'smlapp': [ @@ -334,7 +773,7 @@ const cms = { return [' ' + lang('打开', 'open'), `openapp('${arg[0]}');hide_startmenu();`]; }, arg => { - return [' 在桌面创建链接', 'var s=`

' + arg[1] + '

`;$(\'#desktop\').append(s);desktopItem[desktopItem.length]=s;addMenu();saveDesktop();']; + return [' 在桌面创建链接', `createDesktopLink('${arg[0]}', '${arg[1]}')`]; }, arg => { return [' 固定到开始菜单', 'pinapp(\'' + arg[0] + '\', \'' + arg[1] + '\', \'openapp("' + arg[0] + '");hide_startmenu();\')']; @@ -1850,8 +2289,8 @@ for (let i = 1; i <= daysum; i++) { $('#datebox>.cont>.body')[0].innerHTML += `

${i}

`; } function pinapp(id, name, command) { - if ($('#startmenu-r>.pinned>.apps>.a.sm-app.' + id).length) return; - $('#startmenu-r>.pinned>.apps').append(`

${name}

`); + if ($('#startmenu-r>.tile.' + id).length) return; + $('#startmenu-r').append(`

${name}

`); } // 应用方法 @@ -2003,7 +2442,6 @@ function openDockWidget(name) { } } else if (name == "datebox") { //打开时间框 if ($('#datebox').hasClass('show')) { - $('.dock.date').removeClass('show'); $('#datebox').removeClass('show'); setTimeout(() => { $('#datebox').removeClass('show-begin'); @@ -2016,8 +2454,18 @@ function openDockWidget(name) { $('#control').removeClass('show-begin'); }, 200); } - $('.dock.date').addClass('show'); - $('#datebox').css('left', $('.a.dock.date').position().left - 125); + const dateDock = $('.a.dock.date'); + const dateDockRect = dateDock[0].getBoundingClientRect(); + const dateboxWidth = 350; + let leftPos = dateDockRect.left + dateDockRect.width / 2 - dateboxWidth / 2; + // Ensure datebox stays within screen bounds + if (leftPos + dateboxWidth > window.innerWidth) { + leftPos = window.innerWidth - dateboxWidth - 10; + } + if (leftPos < 10) { + leftPos = 10; + } + $('#datebox').css('left', leftPos + 'px'); $('#datebox').addClass('show-begin'); setTimeout(() => { $('#datebox').addClass('show'); @@ -2200,25 +2648,86 @@ var flyStatus = false; // 选择框 let chstX, chstY; -function ch(e) { - $('#desktop>.selection').css('left', Math.min(chstX, e.clientX)); - $('#desktop>.selection').css('width', Math.abs(e.clientX - chstX)); - $('#desktop>.selection').css('display', 'block'); - $('#desktop>.selection').css('top', Math.min(chstY, e.clientY)); - $('#desktop>.selection').css('height', Math.abs(e.clientY - chstY)); +let isIconDragging = false; +let isBoxSelecting = false; + +function updateSelectionBox(e) { + if (isIconDragging) return; + + const left = Math.min(chstX, e.clientX); + const top = Math.min(chstY, e.clientY); + const width = Math.abs(e.clientX - chstX); + const height = Math.abs(e.clientY - chstY); + + $('#desktop>.selection').css({ + left: left, + top: top, + width: width, + height: height, + display: 'block' + }); + + // 框选多选逻辑 + if (!e.ctrlKey && !e.shiftKey) { + clearSelection(); + } + + const selectionRect = { + left: left, + top: top, + right: left + width, + bottom: top + height + }; + + $('#desktop>div[appname]').each(function() { + const $icon = $(this); + const rect = this.getBoundingClientRect(); + const desktopRect = $('#desktop')[0].getBoundingClientRect(); + + const iconRect = { + left: rect.left - desktopRect.left, + top: rect.top - desktopRect.top, + right: rect.right - desktopRect.left, + bottom: rect.bottom - desktopRect.top + }; + + // 检查图标是否与选择框相交 + if (iconRect.left < selectionRect.right && + iconRect.right > selectionRect.left && + iconRect.top < selectionRect.bottom && + iconRect.bottom > selectionRect.top) { + const appname = $icon.attr('appname'); + selectedIcons.add(appname); + $icon.addClass('selected'); + } + }); } + $('#desktop')[0].addEventListener('mousedown', e => { + if (isIconDragging) return; + if ($(e.target).closest('div[appname]').length) return; + + // 点击空白处清除选择(不按Ctrl时) + if (!e.ctrlKey && !e.shiftKey) { + clearSelection(); + } + + isBoxSelecting = true; chstX = e.clientX; chstY = e.clientY; - this.onmousemove = ch; + this.onmousemove = updateSelectionBox; }); + window.addEventListener('mouseup', e => { this.onmousemove = null; - $('#desktop>.selection').css('left', 0); - $('#desktop>.selection').css('top', 0); - $('#desktop>.selection').css('display', 'none'); - $('#desktop>.selection').css('width', 0); - $('#desktop>.selection').css('height', 0); + $('#desktop>.selection').css({ + left: 0, + top: 0, + width: 0, + height: 0, + display: 'none' + }); + isBoxSelecting = false; }); let isDark = false; @@ -2292,7 +2801,7 @@ function setIcon() {

Microsoft Edge

-
+

${lang('反馈中心', 'feedback.name')}

@@ -2304,6 +2813,10 @@ function setIcon() { $('#desktop')[0].innerHTML += item; }); addMenu(); + initDesktopIconDrag(); + setTimeout(() => { + restoreIconPositions(); + }, 50); } if (Array.isArray(JSON.parse(localStorage.getItem('topmost')))) { topmost = JSON.parse(localStorage.getItem('topmost')); @@ -2580,64 +3093,77 @@ let previewTimeout; function showTaskbarPreview(name, event) { clearTimeout(previewTimeout); - const preview = document.getElementById('taskbar-preview'); - if (!preview) { - const previewEl = document.createElement('div'); - previewEl.id = 'taskbar-preview'; - previewEl.innerHTML = ` -
- - -
-
-
-
- `; - document.body.appendChild(previewEl); - } - - const win = $(`.window.${name}`); - if (win.length && !win.hasClass('min')) { - const preview = $('#taskbar-preview'); - const taskbarItem = $(event.currentTarget); - const itemRect = taskbarItem[0].getBoundingClientRect(); - - - - // Set window title and icon - const titleImg = win.find('.titbar img.icon').attr('src'); - const title = win.find('.titbar p').text() || win.find('.titbar span').text(); - - preview.find('.preview-title img').attr('src', titleImg); - preview.find('.preview-title span').text(title); - - // Create simplified window preview - const previewWindow = preview.find('.preview-content .preview-window'); - previewWindow.empty(); - - // Clone important window elements for preview - const content = win.find('.content').clone(); - content.find('script').remove(); // Remove any scripts - content.find('iframe').remove(); // Remove iframes - - // Scale down the content - content.css({ - transform: 'scale(0.2)', - transformOrigin: 'top left', - width: '500%', // 1/0.2 = 5 - height: '500%' - }); - - previewWindow.append(content); - preview.addClass('show'); + previewTimeout = setTimeout(() => { + const preview = document.getElementById('taskbar-preview'); + if (!preview) { + const previewEl = document.createElement('div'); + previewEl.id = 'taskbar-preview'; + previewEl.innerHTML = ` +
+ + +
+
+
+
+ `; + document.body.appendChild(previewEl); + } + + const win = $(`.window.${name}`); + if (win.length && !win.hasClass('min')) { + const preview = $('#taskbar-preview'); + const taskbarItem = $(event.currentTarget); + const itemRect = taskbarItem[0].getBoundingClientRect(); + + // Reset animation for content switch + preview.removeClass('show'); + preview.css('transition', 'none'); + + // Force reflow + preview[0].offsetHeight; + + // Restore transition + preview.css('transition', ''); + + // Set window title and icon + const titleImg = win.find('.titbar img.icon').attr('src'); + const title = win.find('.titbar p').text() || win.find('.titbar span').text(); + + preview.find('.preview-title img').attr('src', titleImg); + preview.find('.preview-title span').text(title); + + // Create simplified window preview + const previewWindow = preview.find('.preview-content .preview-window'); + previewWindow.empty(); + + // Clone important window elements for preview + const content = win.find('.content').clone(); + content.find('script').remove(); + content.find('iframe').remove(); + + // Scale down the content + content.css({ + transform: 'scale(0.2)', + transformOrigin: 'top left', + width: '500%', + height: '500%' + }); - // Set preview position - console.log(content[0].offsetWidth * 0.2); - preview.css({ - left: itemRect.left - (content[0].offsetWidth * 0.2 / 2), - bottom: '60px' - }); - } + previewWindow.append(content); + + // Set preview position + preview.css({ + left: itemRect.left - (content[0].offsetWidth * 0.2 / 2), + bottom: '60px' + }); + + // Trigger animation + requestAnimationFrame(() => { + preview.addClass('show'); + }); + } + }, 500); } function hideTaskbarPreview() { diff --git a/module/window.js b/module/window.js index 6827db9d..ee31bc3c 100644 --- a/module/window.js +++ b/module/window.js @@ -15,12 +15,7 @@ function showwin(name) { orderwin(); $('.window.' + name).addClass('foc'); if (!$('#start-menu.show')[0] && !$('#search-win.show')[0] && !$('#widgets.show')[0] && !$('#control.show')[0] && !$('#datebox.show')[0]) { - if ($('.window.max:not(.left):not(.right)')[0]) { - $('#dock-box').addClass('hide'); - } - else { - $('#dock-box').removeClass('hide'); - } + $('#dock-box').removeClass('hide'); } else { $('#dock-box').removeClass('hide'); @@ -58,12 +53,7 @@ function hidewin(name, arg = 'window') { focwin(wo[wo.length - 1]); // orderwindow(); if (!$('#start-menu.show')[0] && !$('#search-win.show')[0] && !$('#widgets.show')[0] && !$('#control.show')[0] && !$('#datebox.show')[0]) { - if ($('.window.max:not(.left):not(.right)')[0]) { - $('#dock-box').addClass('hide'); - } - else { - $('#dock-box').removeClass('hide'); - } + $('#dock-box').removeClass('hide'); } else { $('#dock-box').removeClass('hide'); @@ -102,46 +92,81 @@ function maxwin(name, trigger = true) { $('.window.' + name).addClass('max'); $('.window.' + name + '>.titbar>div>.wbtg.max').html(''); } - if (!$('#start-menu.show')[0] && !$('#search-win.show')[0] && !$('#widgets.show')[0] && !$('#control.show')[0] && !$('#datebox.show')[0]) { - if ($('.window.max:not(.left):not(.right)')[0]) { - $('#dock-box').addClass('hide'); - } - else { - $('#dock-box').removeClass('hide'); - } - } - else { - $('#dock-box').removeClass('hide'); - } + $('#dock-box').removeClass('hide'); } function minwin(name) { if ($('.window.' + name).hasClass('min')) { - $('.window.' + name).addClass('show-begin'); - focwin(name); + const win = $('.window.' + name); + const origLeft = win.attr('data-pos-x'); + const origTop = win.attr('data-pos-y'); + + // Remove min class first + win.removeClass('min'); + $('#taskbar>.' + name).removeClass('min'); + + // Restore transform and position with animation setTimeout(() => { - $('#taskbar>.' + name).removeClass('min'); - $('.window.' + name).removeClass('min'); - if ($('.window.' + name).hasClass('min-max')) { - $('.window.' + name).addClass('max'); + win.css('transition', 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)'); + win.css('transform', 'translate(0, 0) scale(1)'); + win.css('opacity', '1'); + + if (origLeft && origTop && !win.hasClass('max') && origLeft !== 'auto' && origTop !== 'auto') { + win.css('left', origLeft); + win.css('top', origTop); + } + + if (win.hasClass('min-max')) { + win.addClass('max'); + win.removeClass('min-max'); } - $('.window.' + name).removeClass('min-max'); - }, 0); + }, 20); + + // Clean up after animation - don't clear left/top setTimeout(() => { - if (!$('.window.' + name).hasClass('max')) { - $('.window.' + name).addClass('notrans'); + win.css('transition', ''); + win.css('transform', ''); + win.css('opacity', ''); + if (!win.hasClass('max')) { + win.addClass('notrans'); } - }, 200); + win.addClass('foc'); + }, 220); } else { - focwin(null); - if ($('.window.' + name).hasClass('max')) { - $('.window.' + name).addClass('min-max'); + const taskbarItem = $('#taskbar>.' + name); + if (taskbarItem.length) { + const rect = taskbarItem[0].getBoundingClientRect(); + const win = $('.window.' + name); + + if (!win.attr('data-pos-x')) { + win.attr('data-pos-x', win.css('left')); + } + if (!win.attr('data-pos-y')) { + win.attr('data-pos-y', win.css('top')); + } + + const winRect = win[0].getBoundingClientRect(); + const deltaX = (rect.left + rect.width / 2) - (winRect.left + winRect.width / 2); + const deltaY = (rect.top + rect.height / 2) - (winRect.top + winRect.height / 2); + + win.css('left', winRect.left + 'px'); + win.css('top', winRect.top + 'px'); + win[0].offsetHeight; + win.css('transition', 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)'); + + setTimeout(() => { + focwin(null); + if (win.hasClass('max')) { + win.addClass('min-max'); + } + win.removeClass('foc'); + win.removeClass('max'); + $('#taskbar>.' + name).addClass('min'); + win.addClass('min'); + win.removeClass('notrans'); + win.css('transform', `translate(${deltaX}px, ${deltaY}px) scale(0.2)`); + win.css('opacity', '0'); + }, 20); } - $('.window.' + name).removeClass('foc'); - $('.window.' + name).removeClass('max'); - $('#taskbar>.' + name).addClass('min'); - $('.window.' + name).addClass('min'); - $('.window.' + name).removeClass('notrans'); - setTimeout(() => { $('.window.' + name).removeClass('show-begin'); }, 200); } } @@ -458,17 +483,7 @@ page.addEventListener('mousemove', (e) => { $('#dock-box').removeClass('hide'); } else { - if (!$('#start-menu.show')[0] && !$('#search-win.show')[0] && !$('#widgets.show')[0] && !$('#control.show')[0] && !$('#datebox.show')[0]) { - if ($('.window.max:not(.left):not(.right)')[0]) { - $('#dock-box').addClass('hide'); - } - else { - $('#dock-box').removeClass('hide'); - } - } - else { - $('#dock-box').removeClass('hide'); - } + $('#dock-box').removeClass('hide'); } });