From 44a4faebc00c67becba925bd4bb928b941cde91a Mon Sep 17 00:00:00 2001 From: remi <768.jac@gmail.com> Date: Sat, 8 Apr 2023 05:55:05 +0200 Subject: [PATCH] feat: add minimize/Fold button for windows (#10508) * add window minimizing! * Fix window being able to go offscreen * Revert en-US.yml changes --------- Co-authored-by: mothmoon <remilia@remilia.se> --- CHANGELOG.md | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/components/MkWindow.vue | 64 +++++++++++++------ 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e701d84bbf..3fce3195ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - 左耳は上からおよそ 10%, 左からおよそ 20% の位置で決定します - 右耳は上からおよそ 10%, 左からおよそ 80% の位置で決定します - 「UIのアニメーションを減らす」 (`reduceAnimation`) で猫耳を撫でられなくなります +- Add Minimizing ("folding") of windows ### Server - イベント用Redisを別サーバーに分離できるように diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 66b591760c..04e4d52b28 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -920,6 +920,7 @@ pushNotificationNotSupported: "ブラウザかサーバーがプッシュ通知 sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を削除する" sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。" windowMaximize: "最大化" +windowMinimize: "最小化" windowRestore: "元に戻す" caption: "キャプション" loggedInAsBot: "Botアカウントでログイン中" diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index 30b5391e9a..1afcb104e9 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -11,15 +11,17 @@ <div :class="$style.body" class="_shadow" @mousedown="onBodyMousedown" @keydown="onKeydown"> <div :class="[$style.header, { [$style.mini]: mini }]" @contextmenu.prevent.stop="onContextmenu"> <span :class="$style.headerLeft"> - <button v-for="button in buttonsLeft" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button> + <button v-for="button in buttonsLeft" v-if="!minimized" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button> </span> <span :class="$style.headerTitle" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown"> <slot name="header"></slot> </span> <span :class="$style.headerRight"> - <button v-for="button in buttonsRight" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button> + <button v-for="button in buttonsRight" v-if="!minimized" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button> + <button v-if="canResize && minimized" v-tooltip="i18n.ts.windowRestore" class="_button" :class="$style.headerButton" @click="unMinimize()"><i class="ti ti-maximize"></i></button> + <button v-else-if="canResize && !maximized" v-tooltip="i18n.ts.windowMinimize" class="_button" :class="$style.headerButton" @click="minimize()"><i class="ti ti-minimize"></i></button> <button v-if="canResize && maximized" v-tooltip="i18n.ts.windowRestore" class="_button" :class="$style.headerButton" @click="unMaximize()"><i class="ti ti-picture-in-picture"></i></button> - <button v-else-if="canResize && !maximized" v-tooltip="i18n.ts.windowMaximize" class="_button" :class="$style.headerButton" @click="maximize()"><i class="ti ti-rectangle"></i></button> + <button v-else-if="canResize && !maximized && !minimized" v-tooltip="i18n.ts.windowMaximize" class="_button" :class="$style.headerButton" @click="maximize()"><i class="ti ti-rectangle"></i></button> <button v-if="closeButton" v-tooltip="i18n.ts.close" class="_button" :class="$style.headerButton" @click="close()"><i class="ti ti-x"></i></button> </span> </div> @@ -27,7 +29,7 @@ <slot></slot> </div> </div> - <template v-if="canResize"> + <template v-if="canResize && !minimized"> <div :class="$style.handleTop" @mousedown.prevent="onTopHandleMousedown"></div> <div :class="$style.handleRight" @mousedown.prevent="onRightHandleMousedown"></div> <div :class="$style.handleBottom" @mousedown.prevent="onBottomHandleMousedown"></div> @@ -100,10 +102,11 @@ let rootEl = $shallowRef<HTMLElement | null>(); let showing = $ref(true); let beforeClickedAt = 0; let maximized = $ref(false); -let unMaximizedTop = ''; -let unMaximizedLeft = ''; -let unMaximizedWidth = ''; -let unMaximizedHeight = ''; +let minimized = $ref(false); +let unResizedTop = ''; +let unResizedLeft = ''; +let unResizedWidth = ''; +let unResizedHeight = ''; function close() { showing = false; @@ -132,10 +135,10 @@ function top() { function maximize() { maximized = true; - unMaximizedTop = rootEl.style.top; - unMaximizedLeft = rootEl.style.left; - unMaximizedWidth = rootEl.style.width; - unMaximizedHeight = rootEl.style.height; + unResizedTop = rootEl.style.top; + unResizedLeft = rootEl.style.left; + unResizedWidth = rootEl.style.width; + unResizedHeight = rootEl.style.height; rootEl.style.top = '0'; rootEl.style.left = '0'; rootEl.style.width = '100%'; @@ -144,10 +147,35 @@ function maximize() { function unMaximize() { maximized = false; - rootEl.style.top = unMaximizedTop; - rootEl.style.left = unMaximizedLeft; - rootEl.style.width = unMaximizedWidth; - rootEl.style.height = unMaximizedHeight; + rootEl.style.top = unResizedTop; + rootEl.style.left = unResizedLeft; + rootEl.style.width = unResizedWidth; + rootEl.style.height = unResizedHeight; +} + +function minimize() { + minimized = true; + unResizedWidth = rootEl.style.width; + unResizedHeight = rootEl.style.height; + rootEl.style.width = minWidth + 'px'; + rootEl.style.height = props.mini ? '32px' : '39px'; +} + +function unMinimize() { + const main = rootEl; + if (main == null) return; + + minimized = false; + rootEl.style.width = unResizedWidth; + rootEl.style.height = unResizedHeight; + const browserWidth = window.innerWidth; + const browserHeight = window.innerHeight; + const windowWidth = main.offsetWidth; + const windowHeight = main.offsetHeight; + + const position = main.getBoundingClientRect(); + if (position.top + windowHeight > browserHeight) main.style.top = browserHeight - windowHeight + 'px'; + if (position.left + windowWidth > browserWidth) main.style.left = browserWidth - windowWidth + 'px'; } function onBodyMousedown() { @@ -155,7 +183,7 @@ function onBodyMousedown() { } function onDblClick() { - maximize(); + if (minimized) {unMinimize();} else {maximize();} } function onHeaderMousedown(evt: MouseEvent) { @@ -187,7 +215,7 @@ function onHeaderMousedown(evt: MouseEvent) { const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX; const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY; - const moveBaseX = beforeMaximized ? parseInt(unMaximizedWidth, 10) / 2 : clickX - position.left; // TODO: parseIntやめる + const moveBaseX = beforeMaximized ? parseInt(unResizedWidth, 10) / 2 : clickX - position.left; // TODO: parseIntやめる const moveBaseY = beforeMaximized ? 20 : clickY - position.top; const browserWidth = window.innerWidth; const browserHeight = window.innerHeight;