update os logic
This commit is contained in:
@@ -1,4 +1,12 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
renderPartial: function(app, view, data = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
app.render(view, data, (err, html) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(html);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
// objectTree: function(context, options) {
|
// objectTree: function(context, options) {
|
||||||
// let ret = '';
|
// let ret = '';
|
||||||
|
|
||||||
|
|||||||
@@ -1476,19 +1476,54 @@ function createTd(tr, text, options = {}) {
|
|||||||
};
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
//#region Tabs
|
//#region Tabs
|
||||||
function createTab(tabSelector, name, onclick = null) {
|
function createTab(tabSelector, name, options = {}) {
|
||||||
const tabElement = document.createElement('div');
|
const {
|
||||||
tabElement.className = 'tab';
|
id = null,
|
||||||
tabElement.dataset.tab = name;
|
hidden = false,
|
||||||
tabElement.textContent = name;
|
classes = [],
|
||||||
tabSelector.appendChild(tabElement);
|
styles = null,
|
||||||
tabElement.addEventListener('click', async () => {
|
attributes = {},
|
||||||
Array.from(document.querySelectorAll('.tab')).forEach(t => t.classList.remove('active'));
|
onclick = null
|
||||||
tabElement.classList.add('active');
|
} = options;
|
||||||
if(typeof onclick === 'function') {
|
|
||||||
onclick();
|
const tabElement = document.createElement('div');
|
||||||
|
tabElement.className = 'tab';
|
||||||
|
tabElement.dataset.tab = name;
|
||||||
|
tabElement.textContent = name;
|
||||||
|
tabSelector.appendChild(tabElement);
|
||||||
|
|
||||||
|
if(id !== null) {
|
||||||
|
tabElement.id = id;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
// Standard-Stile & Attribute
|
||||||
|
tabElement.hidden = hidden;
|
||||||
|
|
||||||
|
classes.forEach(c => tabElement.classList.add(c));
|
||||||
|
|
||||||
|
if (styles && typeof styles === 'object') {
|
||||||
|
Object.entries(styles).forEach(([prop, value]) => {
|
||||||
|
tabElement.style[prop] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data attributes
|
||||||
|
if (attributes && typeof attributes === 'object') {
|
||||||
|
Object.entries(attributes).forEach(([key, value]) => {
|
||||||
|
tabElement.setAttribute(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tabElement.addEventListener('click', async (evt) => {
|
||||||
|
Array.from(tabSelector.querySelectorAll('.tab')).forEach(t => t.classList.remove('active'));
|
||||||
|
tabElement.classList.add('active');
|
||||||
|
if(typeof onclick === 'function') {
|
||||||
|
onclick(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tabElement;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
@@ -378,6 +378,7 @@ function applyMaximized(win) {
|
|||||||
|
|
||||||
win.classList.add('max');
|
win.classList.add('max');
|
||||||
win.dataset.state = 'maximized';
|
win.dataset.state = 'maximized';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -600,16 +601,18 @@ function wireWindowControls(win, taskbarBtn = null) {
|
|||||||
if (taskbarBtn !== null) {
|
if (taskbarBtn !== null) {
|
||||||
titlebar.addEventListener('dblclick', (evt) => {
|
titlebar.addEventListener('dblclick', (evt) => {
|
||||||
toggleMaximize();
|
toggleMaximize();
|
||||||
|
titlebar.querySelector('.maximize').innerHTML = win.dataset.state === 'maximized' ? '🗗' : '▢';
|
||||||
saveOpenWindows()
|
saveOpenWindows()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
titlebar.addEventListener('touchend', (ev) => {
|
titlebar.addEventListener('touchend', (ev) => {
|
||||||
const current = Date.now();
|
const current = Date.now();
|
||||||
const delta = current - lastTap;
|
const delta = current - lastTap;
|
||||||
|
|
||||||
if (delta < 300 && delta > 0) {
|
if (delta < 300 && delta > 0) {
|
||||||
// Double tap → maximize / restore
|
// Double tap → maximize / restore
|
||||||
|
titlebar.querySelector('.maximize').innerHTML = win.dataset.state === 'maximized' ? '🗗' : '▢';
|
||||||
toggleMaximize();
|
toggleMaximize();
|
||||||
}
|
}
|
||||||
lastTap = current;
|
lastTap = current;
|
||||||
@@ -668,7 +671,7 @@ function destroyWindow(win) {
|
|||||||
// 🔥 VOR dem Maximieren speichern
|
// 🔥 VOR dem Maximieren speichern
|
||||||
storeNormalGeometry(win);
|
storeNormalGeometry(win);
|
||||||
|
|
||||||
applyMaximized(win);
|
applyMaximized(win);
|
||||||
} else {
|
} else {
|
||||||
const prev = JSON.parse(win.dataset.normalGeometry || '{}');
|
const prev = JSON.parse(win.dataset.normalGeometry || '{}');
|
||||||
|
|
||||||
@@ -838,6 +841,7 @@ function makeDraggableWithSnap(win) {
|
|||||||
} else if (ev.clientY <= snapThreshold) {
|
} else if (ev.clientY <= snapThreshold) {
|
||||||
// Snap top (maximize)
|
// Snap top (maximize)
|
||||||
applySnap(win, 'top');
|
applySnap(win, 'top');
|
||||||
|
win.querySelector('.maximize').innerHTML = '🗗';
|
||||||
snapped = 'top';
|
snapped = 'top';
|
||||||
} else if (snapped && ev.clientX > snapThreshold * 2 && ev.clientX < screenW - snapThreshold * 2) {
|
} else if (snapped && ev.clientX > snapThreshold * 2 && ev.clientX < screenW - snapThreshold * 2) {
|
||||||
// Wegziehen vom Rand → Restore
|
// Wegziehen vom Rand → Restore
|
||||||
@@ -866,6 +870,7 @@ function makeDraggableWithSnap(win) {
|
|||||||
|
|
||||||
win.classList.add('max');
|
win.classList.add('max');
|
||||||
|
|
||||||
|
|
||||||
if (side === 'left') {
|
if (side === 'left') {
|
||||||
win.style.left = '0';
|
win.style.left = '0';
|
||||||
win.style.top = '8px';
|
win.style.top = '8px';
|
||||||
@@ -908,6 +913,8 @@ function makeDraggableWithSnap(win) {
|
|||||||
win.style.height = height + 'px';
|
win.style.height = height + 'px';
|
||||||
|
|
||||||
win.classList.remove('max');
|
win.classList.remove('max');
|
||||||
|
win.querySelector('.maximize').innerHTML = '▢';
|
||||||
|
|
||||||
win.dataset.state = 'normal';
|
win.dataset.state = 'normal';
|
||||||
win.dataset.snapped = '';
|
win.dataset.snapped = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
#desktop { position:relative; height:100vh; overflow:hidden; background-size:var(--theme-desktop-background-size); background-repeat: no-repeat; background-position: center; touch-action: none; }
|
#desktop { position:relative; height:100vh; overflow:hidden; background-size:var(--theme-desktop-background-size); background-repeat: no-repeat; background-position: center; touch-action: none; }
|
||||||
|
|
||||||
#windows { z-index: 1; position:absolute; inset:0; padding:8px; box-sizing:border-box; }
|
#windows { z-index: 1; position:absolute; inset:0; padding:8px; box-sizing:border-box; }
|
||||||
.window { position:absolute; width: 800px; height: 600px; border-radius:6px; box-shadow: 0 6px 20px rgba(0,0,0,0.4); overflow:hidden; top:50px; left:50px; display:flex; flex-direction:column; resize:both; }
|
.window { min-width:250px; min-height:250px; position:absolute; width: 800px; height: 600px; border-radius:6px; box-shadow: 0 6px 20px rgba(0,0,0,0.4); overflow:hidden; top:50px; left:50px; display:flex; flex-direction:column; resize:both; }
|
||||||
.window-titlebar { padding: 0 0 1px 0; height: 28px; display:flex; justify-content:space-between; align-items:center; }
|
.window-titlebar { padding: 0 0 1px 0; height: auto; display:flex; justify-content:space-between; align-items:center; }
|
||||||
.window-icon { height: 20px; background-size:contain; transform: translate(1px, -1px); background-repeat: no-repeat; background-position: left; background-color: rgb(144, 179, 144); padding:4px;border-radius: 8px;}
|
.window-titlebar .title { display: flex; align-items: center; min-width: 0; flex: 1; overflow: hidden; }
|
||||||
|
.window-titlebar .window-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-left:8px; }
|
||||||
|
|
||||||
|
.window-icon { height: 10px; background-size:contain; transform: translate(1px, -1px); background-repeat: no-repeat; background-position: left; background-color: rgb(144, 179, 144); padding:4px;border-radius: 8px;}
|
||||||
|
.window .controls { display: flex; flex-shrink: 0;}
|
||||||
|
.window .controls button { transition: background-color .3s, color .3s; padding: 2px 10px; border:none;}
|
||||||
.window-content { display: flex; flex-direction: column; flex:1; padding:8px; overflow: auto; }
|
.window-content { display: flex; flex-direction: column; flex:1; padding:8px; overflow: auto; }
|
||||||
.window .controls button { transition: background-color .3s, color .3s; padding: 6px 10px; border:none; }
|
|
||||||
.window[class="max"] .window-resize-handle { display: none; }
|
.window[class="max"] .window-resize-handle { display: none; }
|
||||||
.window-resize-handle { position:absolute; right:0; bottom:0; width:12px; height:12px; cursor:se-resize; z-index: 10; }
|
.window-resize-handle { position:absolute; right:0; bottom:0; width:12px; height:12px; cursor:se-resize; z-index: 10; }
|
||||||
.window-resize-n { top: -4px; left: 0; right: 0; height: 8px; cursor: n-resize; }
|
.window-resize-n { top: -4px; left: 0; right: 0; height: 8px; cursor: n-resize; }
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<div class="window" data-winid="{{name}}-{{@index}}" data-plugin="{{name}}">
|
<div class="window" data-winid="{{name}}-{{@index}}" data-plugin="{{name}}">
|
||||||
<div class="window-titlebar">
|
<div class="window-titlebar">
|
||||||
<div class="title">{{{viewLabel}}}</div>
|
<div class="title">
|
||||||
|
<div class="window-title">{{viewLabel}}</div>
|
||||||
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{#if this.printable}}
|
{{#if this.printable}}
|
||||||
<button data-tooltip="Drucken" id="printbutton">🖨</button>
|
<button data-tooltip="Drucken" id="printbutton">🖨</button>
|
||||||
@@ -13,4 +15,3 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="window-resize-handle"></div>
|
<div class="window-resize-handle"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
<div class="window" data-winid="{{appname}}-{{@index}}" data-plugin="{{appname}}">
|
<div class="window" data-winid="{{appname}}-{{@index}}" data-plugin="{{appname}}">
|
||||||
<div class="window-titlebar">
|
<div class="window-titlebar">
|
||||||
<img src="{{#equaler section "==" "Plugin"}}{{appname}}{{/equaler}}/images/{{icon}}" class="window-icon" />
|
<img src="{{#equaler section "==" "Plugin"}}{{appname}}{{/equaler}}/images/{{icon}}" class="window-icon" />
|
||||||
<div class="title">{{appname}} [{{label}}]</div>
|
<div class="title">
|
||||||
|
<div class="window-title">{{appname}} [{{label}}]</div>
|
||||||
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{#if tutorial}}
|
{{#if tutorial}}
|
||||||
<button class="tutorial" id="tutorial-{{appname}}-{{label}}" onclick="tutorial.start()" data-tooltip="Startet eine interaktive Einführung">💡</button>
|
<button class="tutorial" id="tutorial-{{appname}}-{{label}}" onclick="tutorial.start()" data-tooltip="Startet eine interaktive Einführung">💡</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<button class="minimize" style="transform:translateY(0px)">🗕</button>
|
<button class="minimize">🗕</button>
|
||||||
<button class="maximize">▢</button>
|
<button class="maximize">{{#equaler state "==" "maximized"}}🗗{{else}}▢{{/equaler}}</button>
|
||||||
<button class="close">✕</button>
|
<button class="close">✕</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,4 +18,3 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="window-resize-handle"></div>
|
<div class="window-resize-handle"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Reference in New Issue
Block a user