This commit is contained in:
こぴなたみぽ 2018-02-11 22:04:08 +09:00
parent 6cf1fdbf82
commit a0f3182a0a
2 changed files with 361 additions and 341 deletions

View file

@ -79,6 +79,11 @@ export default class MiOS extends EventEmitter {
*/ */
private shouldRegisterSw: boolean; private shouldRegisterSw: boolean;
/**
*
*/
public windows = new WindowSystem();
/** /**
* MiOSインスタンスを作成します * MiOSインスタンスを作成します
* @param shouldRegisterSw ServiceWorkerを登録するかどうか * @param shouldRegisterSw ServiceWorkerを登録するかどうか
@ -359,6 +364,22 @@ export default class MiOS extends EventEmitter {
} }
} }
class WindowSystem {
private windows = new Set();
public add(window) {
this.windows.add(window);
}
public remove(window) {
this.windows.delete(window);
}
public getAll() {
return this.windows;
}
}
/** /**
* Convert the URL safe base64 string to a Uint8Array * Convert the URL safe base64 string to a Uint8Array
* @param base64String base64 string * @param base64String base64 string

View file

@ -3,23 +3,23 @@
<div class="bg" ref="bg" v-show="isModal" @click="onBgClick"></div> <div class="bg" ref="bg" v-show="isModal" @click="onBgClick"></div>
<div class="main" ref="main" tabindex="-1" :data-is-modal="isModal" @mousedown="onBodyMousedown" @keydown="onKeydown" :style="{ width, height }"> <div class="main" ref="main" tabindex="-1" :data-is-modal="isModal" @mousedown="onBodyMousedown" @keydown="onKeydown" :style="{ width, height }">
<div class="body"> <div class="body">
<header ref="header" @mousedown="onHeaderMousedown"> <header ref="header" @contextmenu.prevent="() => {}" @mousedown.prevent="onHeaderMousedown">
<h1 data-yield="header"><yield from="header"/></h1> <h1 data-yield="header"><yield from="header"/></h1>
<div> <div>
<button class="popout" v-if="popoutUrl" @mousedown="repelMove" @click="popout" title="ポップアウト">%fa:R window-restore%</button> <button class="popout" v-if="popoutUrl" @mousedown.stop="() => {}" @click="popout" title="ポップアウト">%fa:R window-restore%</button>
<button class="close" v-if="canClose" @mousedown="repelMove" @click="close" title="閉じる">%fa:times%</button> <button class="close" v-if="canClose" @mousedown.stop="() => {}" @click="close" title="閉じる">%fa:times%</button>
</div> </div>
</header> </header>
<div class="content" data-yield="content"><yield from="content"/></div> <div class="content" data-yield="content"><yield from="content"/></div>
</div> </div>
<div class="handle top" v-if="canResize" @mousedown="onTopHandleMousedown"></div> <div class="handle top" v-if="canResize" @mousedown.prevent="onTopHandleMousedown"></div>
<div class="handle right" v-if="canResize" @mousedown="onRightHandleMousedown"></div> <div class="handle right" v-if="canResize" @mousedown.prevent="onRightHandleMousedown"></div>
<div class="handle bottom" v-if="canResize" @mousedown="onBottomHandleMousedown"></div> <div class="handle bottom" v-if="canResize" @mousedown.prevent="onBottomHandleMousedown"></div>
<div class="handle left" v-if="canResize" @mousedown="onLeftHandleMousedown"></div> <div class="handle left" v-if="canResize" @mousedown.prevent="onLeftHandleMousedown"></div>
<div class="handle top-left" v-if="canResize" @mousedown="onTopLeftHandleMousedown"></div> <div class="handle top-left" v-if="canResize" @mousedown.prevent="onTopLeftHandleMousedown"></div>
<div class="handle top-right" v-if="canResize" @mousedown="onTopRightHandleMousedown"></div> <div class="handle top-right" v-if="canResize" @mousedown.prevent="onTopRightHandleMousedown"></div>
<div class="handle bottom-right" v-if="canResize" @mousedown="onBottomRightHandleMousedown"></div> <div class="handle bottom-right" v-if="canResize" @mousedown.prevent="onBottomRightHandleMousedown"></div>
<div class="handle bottom-left" v-if="canResize" @mousedown="onBottomLeftHandleMousedown"></div> <div class="handle bottom-left" v-if="canResize" @mousedown.prevent="onBottomLeftHandleMousedown"></div>
</div> </div>
</div> </div>
</template> </template>
@ -32,6 +32,18 @@ import contains from '../../common/scripts/contains';
const minHeight = 40; const minHeight = 40;
const minWidth = 200; const minWidth = 200;
function dragListen(fn) {
window.addEventListener('mousemove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
window.addEventListener('mouseup', dragClear.bind(null, fn));
}
function dragClear(fn) {
window.removeEventListener('mousemove', fn);
window.removeEventListener('mouseleave', dragClear);
window.removeEventListener('mouseup', dragClear);
}
export default Vue.extend({ export default Vue.extend({
props: { props: {
isModal: { isModal: {
@ -54,6 +66,7 @@ export default Vue.extend({
type: String type: String
} }
}, },
computed: { computed: {
isFlexible(): boolean { isFlexible(): boolean {
return this.height == null; return this.height == null;
@ -62,109 +75,84 @@ export default Vue.extend({
return !this.isFlexible; return !this.isFlexible;
} }
}, },
created() {
//
this.$root.$data.os.windows.add(this);
},
mounted() { mounted() {
const main = this.$refs.main as any;
} main.style.top = '15%';
}); main.style.left = (window.innerWidth / 2) - (main.offsetWidth / 2) + 'px';
</script>
<script lang="typescript">
this.on('mount', () => {
this.$refs.main.style.top = '15%';
this.$refs.main.style.left = (window.innerWidth / 2) - (this.$refs.main.offsetWidth / 2) + 'px';
this.$refs.header.addEventListener('contextmenu', e => {
e.preventDefault();
});
window.addEventListener('resize', this.onBrowserResize); window.addEventListener('resize', this.onBrowserResize);
this.open(); this.open();
}); },
destroyed() {
//
this.$root.$data.os.windows.remove(this);
this.on('unmount', () => {
window.removeEventListener('resize', this.onBrowserResize); window.removeEventListener('resize', this.onBrowserResize);
}); },
this.onBrowserResize = () => { methods: {
const position = this.$refs.main.getBoundingClientRect(); open() {
const browserWidth = window.innerWidth;
const browserHeight = window.innerHeight;
const windowWidth = this.$refs.main.offsetWidth;
const windowHeight = this.$refs.main.offsetHeight;
if (position.left < 0) this.$refs.main.style.left = 0;
if (position.top < 0) this.$refs.main.style.top = 0;
if (position.left + windowWidth > browserWidth) this.$refs.main.style.left = browserWidth - windowWidth + 'px';
if (position.top + windowHeight > browserHeight) this.$refs.main.style.top = browserHeight - windowHeight + 'px';
};
this.open = () => {
this.$emit('opening'); this.$emit('opening');
this.top(); this.top();
const bg = this.$refs.bg as any;
const main = this.$refs.main as any;
if (this.isModal) { if (this.isModal) {
this.$refs.bg.style.pointerEvents = 'auto'; bg.style.pointerEvents = 'auto';
anime({ anime({
targets: this.$refs.bg, targets: bg,
opacity: 1, opacity: 1,
duration: 100, duration: 100,
easing: 'linear' easing: 'linear'
}); });
} }
this.$refs.main.style.pointerEvents = 'auto'; main.style.pointerEvents = 'auto';
anime({ anime({
targets: this.$refs.main, targets: main,
opacity: 1, opacity: 1,
scale: [1.1, 1], scale: [1.1, 1],
duration: 200, duration: 200,
easing: 'easeOutQuad' easing: 'easeOutQuad'
}); });
//this.$refs.main.focus(); if (focus) main.focus();
setTimeout(() => { setTimeout(() => {
this.$emit('opened'); this.$emit('opened');
}, 300); }, 300);
}; },
this.popout = () => { close() {
const position = this.$refs.main.getBoundingClientRect();
const width = parseInt(getComputedStyle(this.$refs.main, '').width, 10);
const height = parseInt(getComputedStyle(this.$refs.main, '').height, 10);
const x = window.screenX + position.left;
const y = window.screenY + position.top;
const url = typeof this.popoutUrl == 'function' ? this.popoutUrl() : this.popoutUrl;
window.open(url, url,
`height=${height},width=${width},left=${x},top=${y}`);
this.close();
};
this.close = () => {
this.$emit('closing'); this.$emit('closing');
const bg = this.$refs.bg as any;
const main = this.$refs.main as any;
if (this.isModal) { if (this.isModal) {
this.$refs.bg.style.pointerEvents = 'none'; bg.style.pointerEvents = 'none';
anime({ anime({
targets: this.$refs.bg, targets: bg,
opacity: 0, opacity: 0,
duration: 300, duration: 300,
easing: 'linear' easing: 'linear'
}); });
} }
this.$refs.main.style.pointerEvents = 'none'; main.style.pointerEvents = 'none';
anime({ anime({
targets: this.$refs.main, targets: main,
opacity: 0, opacity: 0,
scale: 0.8, scale: 0.8,
duration: 300, duration: 300,
@ -174,46 +162,57 @@ this.close = () => {
setTimeout(() => { setTimeout(() => {
this.$emit('closed'); this.$emit('closed');
}, 300); }, 300);
}; },
// popout() {
this.top = () => { const main = this.$refs.main as any;
const position = main.getBoundingClientRect();
const width = parseInt(getComputedStyle(main, '').width, 10);
const height = parseInt(getComputedStyle(main, '').height, 10);
const x = window.screenX + position.left;
const y = window.screenY + position.top;
const url = typeof this.popoutUrl == 'function' ? this.popoutUrl() : this.popoutUrl;
window.open(url, url,
`height=${height}, width=${width}, left=${x}, top=${y}`);
this.close();
},
//
top() {
let z = 0; let z = 0;
const ws = document.querySelectorAll('mk-window'); this.$root.$data.os.windows.getAll().forEach(w => {
ws.forEach(w => { if (w == this) return;
if (w == this.root) return; const m = w.$refs.main;
const m = w.querySelector(':scope > .main');
const mz = Number(document.defaultView.getComputedStyle(m, null).zIndex); const mz = Number(document.defaultView.getComputedStyle(m, null).zIndex);
if (mz > z) z = mz; if (mz > z) z = mz;
}); });
if (z > 0) { if (z > 0) {
this.$refs.main.style.zIndex = z + 1; (this.$refs.main as any).style.zIndex = z + 1;
if (this.isModal) this.$refs.bg.style.zIndex = z + 1; if (this.isModal) (this.$refs.bg as any).style.zIndex = z + 1;
} }
}; },
this.repelMove = e => { onBgClick() {
e.stopPropagation();
return true;
};
this.bgClick = () => {
if (this.canClose) this.close(); if (this.canClose) this.close();
}; },
this.onBodyMousedown = () => { onBodyMousedown() {
this.top(); this.top();
}; },
// onHeaderMousedown(e) {
this.onHeaderMousedown = e => { const main = this.$refs.main as any;
e.preventDefault();
if (!contains(this.$refs.main, document.activeElement)) this.$refs.main.focus(); if (!contains(main, document.activeElement)) main.focus();
const position = this.$refs.main.getBoundingClientRect(); const position = main.getBoundingClientRect();
const clickX = e.clientX; const clickX = e.clientX;
const clickY = e.clientY; const clickY = e.clientY;
@ -221,8 +220,8 @@ this.onHeaderMousedown = e => {
const moveBaseY = clickY - position.top; const moveBaseY = clickY - position.top;
const browserWidth = window.innerWidth; const browserWidth = window.innerWidth;
const browserHeight = window.innerHeight; const browserHeight = window.innerHeight;
const windowWidth = this.$refs.main.offsetWidth; const windowWidth = main.offsetWidth;
const windowHeight = this.$refs.main.offsetHeight; const windowHeight = main.offsetHeight;
// //
dragListen(me => { dragListen(me => {
@ -241,172 +240,160 @@ this.onHeaderMousedown = e => {
// //
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
this.$refs.main.style.left = moveLeft + 'px'; main.style.left = moveLeft + 'px';
this.$refs.main.style.top = moveTop + 'px'; main.style.top = moveTop + 'px';
}); });
}; },
// //
this.onTopHandleMousedown = e => { onTopHandleMousedown(e) {
e.preventDefault(); const main = this.$refs.main as any;
const base = e.clientY; const base = e.clientY;
const height = parseInt(getComputedStyle(this.$refs.main, '').height, 10); const height = parseInt(getComputedStyle(main, '').height, 10);
const top = parseInt(getComputedStyle(this.$refs.main, '').top, 10); const top = parseInt(getComputedStyle(main, '').top, 10);
// //
dragListen(me => { dragListen(me => {
const move = me.clientY - base; const move = me.clientY - base;
if (top + move > 0) { if (top + move > 0) {
if (height + -move > this.minHeight) { if (height + -move > minHeight) {
this.applyTransformHeight(height + -move); this.applyTransformHeight(height + -move);
this.applyTransformTop(top + move); this.applyTransformTop(top + move);
} else { // } else { //
this.applyTransformHeight(this.minHeight); this.applyTransformHeight(minHeight);
this.applyTransformTop(top + (height - this.minHeight)); this.applyTransformTop(top + (height - minHeight));
} }
} else { // } else { //
this.applyTransformHeight(top + height); this.applyTransformHeight(top + height);
this.applyTransformTop(0); this.applyTransformTop(0);
} }
}); });
}; },
// //
this.onRightHandleMousedown = e => { onRightHandleMousedown(e) {
e.preventDefault(); const main = this.$refs.main as any;
const base = e.clientX; const base = e.clientX;
const width = parseInt(getComputedStyle(this.$refs.main, '').width, 10); const width = parseInt(getComputedStyle(main, '').width, 10);
const left = parseInt(getComputedStyle(this.$refs.main, '').left, 10); const left = parseInt(getComputedStyle(main, '').left, 10);
const browserWidth = window.innerWidth; const browserWidth = window.innerWidth;
// //
dragListen(me => { dragListen(me => {
const move = me.clientX - base; const move = me.clientX - base;
if (left + width + move < browserWidth) { if (left + width + move < browserWidth) {
if (width + move > this.minWidth) { if (width + move > minWidth) {
this.applyTransformWidth(width + move); this.applyTransformWidth(width + move);
} else { // } else { //
this.applyTransformWidth(this.minWidth); this.applyTransformWidth(minWidth);
} }
} else { // } else { //
this.applyTransformWidth(browserWidth - left); this.applyTransformWidth(browserWidth - left);
} }
}); });
}; },
// //
this.onBottomHandleMousedown = e => { onBottomHandleMousedown(e) {
e.preventDefault(); const main = this.$refs.main as any;
const base = e.clientY; const base = e.clientY;
const height = parseInt(getComputedStyle(this.$refs.main, '').height, 10); const height = parseInt(getComputedStyle(main, '').height, 10);
const top = parseInt(getComputedStyle(this.$refs.main, '').top, 10); const top = parseInt(getComputedStyle(main, '').top, 10);
const browserHeight = window.innerHeight; const browserHeight = window.innerHeight;
// //
dragListen(me => { dragListen(me => {
const move = me.clientY - base; const move = me.clientY - base;
if (top + height + move < browserHeight) { if (top + height + move < browserHeight) {
if (height + move > this.minHeight) { if (height + move > minHeight) {
this.applyTransformHeight(height + move); this.applyTransformHeight(height + move);
} else { // } else { //
this.applyTransformHeight(this.minHeight); this.applyTransformHeight(minHeight);
} }
} else { // } else { //
this.applyTransformHeight(browserHeight - top); this.applyTransformHeight(browserHeight - top);
} }
}); });
}; },
// //
this.onLeftHandleMousedown = e => { onLeftHandleMousedown(e) {
e.preventDefault(); const main = this.$refs.main as any;
const base = e.clientX; const base = e.clientX;
const width = parseInt(getComputedStyle(this.$refs.main, '').width, 10); const width = parseInt(getComputedStyle(main, '').width, 10);
const left = parseInt(getComputedStyle(this.$refs.main, '').left, 10); const left = parseInt(getComputedStyle(main, '').left, 10);
// //
dragListen(me => { dragListen(me => {
const move = me.clientX - base; const move = me.clientX - base;
if (left + move > 0) { if (left + move > 0) {
if (width + -move > this.minWidth) { if (width + -move > minWidth) {
this.applyTransformWidth(width + -move); this.applyTransformWidth(width + -move);
this.applyTransformLeft(left + move); this.applyTransformLeft(left + move);
} else { // } else { //
this.applyTransformWidth(this.minWidth); this.applyTransformWidth(minWidth);
this.applyTransformLeft(left + (width - this.minWidth)); this.applyTransformLeft(left + (width - minWidth));
} }
} else { // } else { //
this.applyTransformWidth(left + width); this.applyTransformWidth(left + width);
this.applyTransformLeft(0); this.applyTransformLeft(0);
} }
}); });
}; },
// //
this.onTopLeftHandleMousedown = e => { onTopLeftHandleMousedown(e) {
this.onTopHandleMousedown(e); this.onTopHandleMousedown(e);
this.onLeftHandleMousedown(e); this.onLeftHandleMousedown(e);
}; },
// //
this.onTopRightHandleMousedown = e => { onTopRightHandleMousedown(e) {
this.onTopHandleMousedown(e); this.onTopHandleMousedown(e);
this.onRightHandleMousedown(e); this.onRightHandleMousedown(e);
}; },
// //
this.onBottomRightHandleMousedown = e => { onBottomRightHandleMousedown(e) {
this.onBottomHandleMousedown(e); this.onBottomHandleMousedown(e);
this.onRightHandleMousedown(e); this.onRightHandleMousedown(e);
}; },
// //
this.onBottomLeftHandleMousedown = e => { onBottomLeftHandleMousedown(e) {
this.onBottomHandleMousedown(e); this.onBottomHandleMousedown(e);
this.onLeftHandleMousedown(e); this.onLeftHandleMousedown(e);
}; },
// //
this.applyTransformHeight = height => { applyTransformHeight(height) {
this.$refs.main.style.height = height + 'px'; (this.$refs.main as any).style.height = height + 'px';
}; },
// //
this.applyTransformWidth = width => { applyTransformWidth(width) {
this.$refs.main.style.width = width + 'px'; (this.$refs.main as any).style.width = width + 'px';
}; },
// Y // Y
this.applyTransformTop = top => { applyTransformTop(top) {
this.$refs.main.style.top = top + 'px'; (this.$refs.main as any).style.top = top + 'px';
}; },
// X // X
this.applyTransformLeft = left => { applyTransformLeft(left) {
this.$refs.main.style.left = left + 'px'; (this.$refs.main as any).style.left = left + 'px';
}; },
function dragListen(fn) { onDragover(e) {
window.addEventListener('mousemove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
window.addEventListener('mouseup', dragClear.bind(null, fn));
}
function dragClear(fn) {
window.removeEventListener('mousemove', fn);
window.removeEventListener('mouseleave', dragClear);
window.removeEventListener('mouseup', dragClear);
}
this.ondragover = e => {
e.dataTransfer.dropEffect = 'none'; e.dataTransfer.dropEffect = 'none';
}; },
this.onKeydown = e => { onKeydown(e) {
if (e.which == 27) { // Esc if (e.which == 27) { // Esc
if (this.canClose) { if (this.canClose) {
e.preventDefault(); e.preventDefault();
@ -414,11 +401,24 @@ this.onKeydown = e => {
this.close(); this.close();
} }
} }
}; },
onBrowserResize() {
const main = this.$refs.main as any;
const position = main.getBoundingClientRect();
const browserWidth = window.innerWidth;
const browserHeight = window.innerHeight;
const windowWidth = main.offsetWidth;
const windowHeight = main.offsetHeight;
if (position.left < 0) main.style.left = 0;
if (position.top < 0) main.style.top = 0;
if (position.left + windowWidth > browserWidth) main.style.left = browserWidth - windowWidth + 'px';
if (position.top + windowHeight > browserHeight) main.style.top = browserHeight - windowHeight + 'px';
}
}
});
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-window .mk-window
display block display block
@ -584,4 +584,3 @@ this.onKeydown = e => {
height calc(100% - 40px) height calc(100% - 40px)
</style> </style>