diff --git a/src/UI/UIWindowTaskManager.js b/src/UI/UIWindowTaskManager.js
index 3b4ab301..636f1132 100644
--- a/src/UI/UIWindowTaskManager.js
+++ b/src/UI/UIWindowTaskManager.js
@@ -2,11 +2,291 @@ import { END_HARD, END_SOFT } from "../definitions.js";
import UIAlert from "./UIAlert.js";
import UIContextMenu from "./UIContextMenu.js";
import UIWindow from "./UIWindow.js";
+import { Component, defineComponent } from '../util/Component.js';
+import UIComponentWindow from './UIComponentWindow.js';
+import Table from './Components/Table.js';
+import Placeholder from '../util/Placeholder.js';
+import TestView from './Components/TestView.js';
+
+const end_process = async (uuid, force) => {
+ const svc_process = globalThis.services.get('process');
+ const process = svc_process.get_by_uuid(uuid);
+ if (!process) {
+ console.warn(`Can't end process with uuid='${uuid}': does not exist`);
+ return;
+ }
+
+ let confirmation;
+ if ( process.is_init() ) {
+ if ( ! force ) {
+ confirmation = i18n('close_all_windows_confirm');
+ } else {
+ confirmation = i18n('restart_puter_confirm');
+ }
+ } else if ( force ) {
+ confirmation = i18n('end_process_force_confirm');
+ }
+
+ if ( confirmation ) {
+ const alert_resp = await UIAlert({
+ message: confirmation,
+ buttons:[
+ {
+ label: i18n('yes'),
+ value: true,
+ type: 'primary',
+ },
+ {
+ label: i18n('no'),
+ value: false,
+ },
+ ]
+ })
+ if ( ! alert_resp ) return;
+ }
+
+ process.signal(force ? END_HARD : END_SOFT);
+};
+
+class TaskManagerTable extends Component {
+ static PROPERTIES = {
+ tasks: { value: [] },
+ };
+
+ static CSS = /*css*/`
+ :host {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ background-color: rgba(255,255,255,0.8);
+ border: 2px inset rgba(127, 127, 127, 0.3);
+ overflow: auto;
+ }
+ `;
+
+ #svc_process = globalThis.services.get('process');
+
+ create_template ({ template }) {
+ $(template).html(`
+
+ `);
+ }
+
+ on_ready ({ listen }) {
+ this.table = new Table({
+ headings: [
+ i18n('taskmgr_header_name'),
+ i18n('taskmgr_header_type'),
+ i18n('taskmgr_header_status'),
+ ]
+ });
+ this.table.attach(this.dom_.querySelector('.taskmgr-taskarea'));
+
+ listen('tasks', tasks => {
+ // TODO: Update DOM instead of replacing the entire table
+ this.table.set('rows', this.#iter_tasks(tasks, { indent_level: 0, is_last_item_stack: [] }));
+ });
+ }
+
+ #calculate_indent_string (indent_level, is_last_item_stack, is_last_item) {
+ // Returns a string of '| ├└'
+ let result = '';
+
+ for ( let i=0; i < indent_level; i++ ) {
+ const last_cell = i === indent_level - 1;
+ const has_trunk = (last_cell && ( ! is_last_item )) ||
+ (!last_cell && !is_last_item_stack[i+1]);
+ const has_branch = last_cell;
+
+ if (has_trunk && has_branch) {
+ result += '├';
+ } else if (has_trunk) {
+ result += '|';
+ } else if (has_branch) {
+ result += '└';
+ } else {
+ result += ' ';
+ }
+ }
+
+ return result;
+ }
+
+ #iter_tasks (items, { indent_level, is_last_item_stack }) {
+ const rows = [];
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i];
+ const is_last_item = i === items.length - 1;
+ rows.push(new TaskManagerRow({
+ name: item.name,
+ uuid: item.uuid,
+ process_type: item.type,
+ process_status: item.status.i18n_key,
+ indentation: this.#calculate_indent_string(indent_level, is_last_item_stack, is_last_item),
+ }));
+
+ const children = this.#svc_process.get_children_of(item.uuid);
+ if (children) {
+ rows.push(...this.#iter_tasks(children, {
+ indent_level: indent_level + 1,
+ is_last_item_stack:
+ [ ...is_last_item_stack, is_last_item ],
+ }));
+ }
+ }
+ return rows;
+ };
+}
+defineComponent('c-task-manager-table', TaskManagerTable);
+
+class TaskManagerRow extends Component {
+ static PROPERTIES = {
+ name: {},
+ uuid: {},
+ process_type: {},
+ process_status: {},
+ indentation: { value: '' },
+ };
+
+ static CSS = /*css*/`
+ :host {
+ display: table-row;
+ }
+
+ td > span {
+ padding: 0 calc(2.5 * var(--scale));
+ }
+
+ .task {
+ display: flex;
+ height: calc(10 * var(--scale));
+ line-height: calc(10 * var(--scale));
+ }
+
+ .task-name {
+ flex-grow: 1;
+ padding-left: calc(2.5 * var(--scale));
+ }
+
+ .task-indentation {
+ display: flex;
+ }
+
+ .indentcell {
+ position: relative;
+ align-items: right;
+ width: calc(10 * var(--scale));
+ height: calc(10 * var(--scale));
+ }
+
+ .indentcell-trunk {
+ position: absolute;
+ top: 0;
+ left: calc(5 * var(--scale));
+ width: calc(5 * var(--scale));
+ height: calc(10 * var(--scale));
+ border-left: 2px solid var(--line-color);
+ }
+
+ .indentcell-branch {
+ position: absolute;
+ top: 0;
+ left: calc(5 * var(--scale));
+ width: calc(5 * var(--scale));
+ height: calc(5 * var(--scale));
+ border-left: 2px solid var(--line-color);
+ border-bottom: 2px solid var(--line-color);
+ border-radius: 0 0 0 calc(2.5 * var(--scale));
+ }
+ `;
+
+ create_template ({ template }) {
+ template.innerHTML = `
+
+
+ |
+ |
+ |
+ `;
+ }
+
+ on_ready ({ listen }) {
+ listen('name', name => {
+ $(this.dom_).find('.task-name').text(name);
+ });
+ listen('uuid', uuid => {
+ this.setAttribute('data-uuid', uuid);
+ });
+ listen('process_type', type => {
+ $(this.dom_).find('.process-type').text(i18n('process_type_' + type));
+ });
+ listen('process_status', status => {
+ $(this.dom_).find('.process-status').text(i18n('process_status_' + status));
+ });
+ listen('indentation', indentation => {
+ const el = $(this.dom_).find('.task-indentation');
+ let h = '';
+ for (const c of indentation) {
+ h += ``;
+ switch (c) {
+ case ' ':
+ break;
+ case '|':
+ h += `
`;
+ break;
+ case '└':
+ h += `
`;
+ break;
+ case '├':
+ h += `
`;
+ h += `
`;
+ break;
+ }
+ h += `
`;
+ }
+ el.html(h);
+ });
+
+ $(this).on('contextmenu', () => {
+ const uuid = this.get('uuid');
+ UIContextMenu({
+ items: [
+ {
+ html: i18n('close'),
+ onClick: () => {
+ end_process(uuid);
+ },
+ },
+ {
+ html: i18n('force_quit'),
+ onClick: () => {
+ end_process(uuid, true);
+ },
+ },
+ ],
+ });
+ });
+ }
+}
+defineComponent('c-task-manager-row', TaskManagerRow);
const UIWindowTaskManager = async function UIWindowTaskManager () {
const svc_process = globalThis.services.get('process');
- const w = await UIWindow({
+ let task_manager_table = new TaskManagerTable({
+ tasks: [svc_process.get_init()],
+ });
+
+ const interval = setInterval(() => {
+ const processes = [svc_process.get_init()];
+ task_manager_table.set('tasks', processes);
+ }, 500);
+
+ const w = await UIComponentWindow({
+ component: task_manager_table,
title: i18n('task_manager'),
icon: globalThis.icons['cog.svg'],
uid: null,
@@ -40,220 +320,18 @@ const UIWindowTaskManager = async function UIWindowTaskManager () {
var(--primary-lightness),
var(--primary-alpha))`,
'backdrop-filter': 'blur(3px)',
-
- }
+ 'box-sizing': 'border-box',
+ // could have been avoided with box-sizing: border-box
+ height: 'calc(100% - 30px)',
+ display: 'flex',
+ 'flex-direction': 'column',
+ '--scale': '2pt',
+ '--line-color': '#6e6e6ebd',
+ },
+ on_close: () => {
+ clearInterval(interval);
+ },
});
- const w_body = w.querySelector('.window-body');
- w_body.classList.add('taskmgr');
-
- const Indent = ({ has_trunk, has_branch }) => {
- const el = document.createElement('div');
- el.classList.add('taskmgr-indentcell');
- if ( has_trunk ) {
- // Add new child element
- const el_indentcell_child = document.createElement('div');
- el_indentcell_child.classList.add('taskmgr-indentcell-trunk');
- el.appendChild(el_indentcell_child);
- }
- if ( has_branch ) {
- const el_indentcell_child = document.createElement('div');
- el_indentcell_child.classList.add('taskmgr-indentcell-branch');
- el.appendChild(el_indentcell_child);
- }
-
- return {
- appendTo (parent) {
- parent.appendChild(el);
- return this;
- }
- };
- };
-
- const Task = ({ placement, name }) => {
- const {
- indent_level, last_item,
- parent_last_item,
- } = placement;
-
- const el = document.createElement('div');
- el.classList.add('taskmgr-task');
-
- for ( let i=0; i < indent_level; i++ ) {
- const last_cell = i === indent_level - 1;
- Indent({
- has_trunk: (last_cell && ( ! last_item )) ||
- (!last_cell && !parent_last_item[i+1]),
- has_branch: last_cell
- }).appendTo(el);
- }
-
- const el_title = document.createElement('div');
- el_title.classList.add('taskmgr-task-title');
- el_title.innerText = name;
- el.appendChild(el_title);
-
- return {
- el () { return el; },
- appendTo (parent) {
- parent.appendChild(el);
- return this;
- }
- };
- }
-
- // https://codepen.io/fomkin/pen/gOgoBVy
- const Table = ({ headings }) => {
- const el_table = $(`
-
-
-
- ${headings.map(heading =>
- `${heading} | `).join('')}
-
-
-
-
- `)[0];
-
- const el_tbody = el_table.querySelector('tbody');
-
- return {
- el () { return el_table; },
- add (el) {
- if ( typeof el.el === 'function' ) el = el.el();
- el_tbody.appendChild(el);
- return this;
- },
- clear () {
- el_tbody.innerHTML = '';
- }
- };
- };
-
- const Row = () => {
- const el_tr = document.createElement('tr');
- return {
- attach (parent) {
- parent.appendChild(el_tr);
- return this;
- },
- el () { return el_tr; },
- add (el) {
- if ( typeof el.el === 'function' ) el = el.el();
- const el_td = document.createElement('td');
- el_td.appendChild(el);
- el_tr.appendChild(el_td);
- return this;
- }
- };
- };
-
- const el_taskarea = document.createElement('div');
- el_taskarea.classList.add('taskmgr-taskarea');
-
- const tasktable = Table({
- headings: [
- i18n('taskmgr_header_name'),
- i18n('taskmgr_header_type'),
- i18n('taskmgr_header_status'),
- ]
- });
-
- el_taskarea.appendChild(tasktable.el());
-
- const end_process_ = async (process, force) => {
- let confirmation;
-
- if ( process.is_init() ) {
- if ( ! force ) {
- confirmation = i18n('close_all_windows_confirm');
- } else {
- confirmation = i18n('restart_puter_confirm');
- }
- } else if ( force ) {
- confirmation = i18n('end_process_force_confirm');
- }
-
- if ( confirmation ) {
- const alert_resp = await UIAlert({
- message: confirmation,
- buttons:[
- {
- label: i18n('yes'),
- value: true,
- type: 'primary',
- },
- {
- label: i18n('no'),
- value: false,
- },
- ]
- })
- if ( ! alert_resp ) return;
- }
-
- process.signal(force ? END_HARD : END_SOFT);
- }
-
- const iter_tasks = (items, { indent_level, parent_last_item }) => {
- for ( let i=0 ; i < items.length; i++ ) {
- const row = Row();
- const item = items[i];
- const last_item = i === items.length - 1;
- row.add(Task({
- placement: {
- parent_last_item,
- indent_level,
- last_item,
- },
- name: item.name
- }));
- row.add($(`${i18n('process_type_' + item.type)}`)[0])
- row.add($(`${i18n('process_status_' + item.status.i18n_key)}`)[0])
- tasktable.add(row);
-
- $(row.el()).on('contextmenu', () => {
- UIContextMenu({
- parent_element: $(el_taskarea),
- items: [
- {
- html: i18n('close'),
- onClick: () => {
- end_process_(item);
- }
- },
- {
- html: i18n('force_quit'),
- onClick: () => {
- end_process_(item, true);
- }
- }
- ]
- });
- })
-
- const children = svc_process.get_children_of(item.uuid);
- if ( children ) {
- iter_tasks(children, {
- indent_level: indent_level + 1,
- parent_last_item:
- [...parent_last_item, last_item],
- });
- }
- }
- };
-
- const interval = setInterval(() => {
- tasktable.clear();
- const processes = [svc_process.get_init()];
- iter_tasks(processes, { indent_level: 0, parent_last_item: [] });
- }, 500)
-
- w.on_close = () => {
- clearInterval(interval);
- }
-
- w_body.appendChild(el_taskarea);
}
export default UIWindowTaskManager;
diff --git a/src/css/style.css b/src/css/style.css
index a2657158..2c272663 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -3830,113 +3830,3 @@ fieldset[name=number-code] {
gap: 10px;
justify-content: flex-end;
}
-
-.taskmgr {
- box-sizing: border-box;
- /* could have been avoided with box-sizing: border-box */
- height: calc(100% - 30px);
- display: flex;
- flex-direction: column;
-
- --scale: 2pt;
- --line-color: #6e6e6ebd;
-}
-
-.taskmgr * {
- box-sizing: border-box;
-}
-
-.taskmgr table {
- border-collapse: collapse;
-}
-
-.taskmgr-taskarea {
- flex-grow: 1;
- display: flex;
- flex-direction: column;
- background-color: rgba(255,255,255,0.8);
- border: 2px inset rgba(127, 127, 127, 0.3);
- overflow: auto;
-}
-
-.taskmgr-taskarea table thead {
-}
-
-.taskmgr-taskarea table th {
- -webkit-box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
- box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
- backdrop-filter: blur(2px);
- position: sticky;
- z-index: 100;
- padding: 0;
- top: 0;
- background-color: hsla(0, 0%, 100%, 0.8);
- text-align: left;
-}
-
-.taskmgr-taskarea table th > span {
- display: inline-block;
- width: 100%;
- /* we set borders on this span because */
- /* borders fly away from sticky headers */
- border-bottom: 1px solid #e0e0e0;
-
- /* padding order: top right bottom left */
- padding:
- calc(10 * var(--scale))
- calc(2.5 * var(--scale))
- calc(5 * var(--scale))
- calc(2.5 * var(--scale));
-}
-
-.taskmgr-taskarea table th:not(:last-of-type) > span {
- /* we set borders on this span because */
- /* borders fly away from sticky headers */
- border-right: 1px solid #e0e0e0;
-}
-
-.taskmgr-taskarea table td {
- border-bottom: 1px solid #e0e0e0;
-}
-
-.taskmgr-taskarea table td > span {
- padding: 0 calc(2.5 * var(--scale));
-}
-
-.taskmgr-indentcell {
- position: relative;
- align-items: right;
- width: calc(10 * var(--scale));
- height: calc(10 * var(--scale));
-}
-
-.taskmgr-indentcell-trunk {
- position: absolute;
- top: 0;
- left: calc(5 * var(--scale));
- width: calc(5 * var(--scale));
- height: calc(10 * var(--scale));
- border-left: 2px solid var(--line-color);
-}
-
-.taskmgr-indentcell-branch {
- position: absolute;
- top: 0;
- left: calc(5 * var(--scale));
- width: calc(5 * var(--scale));
- height: calc(5 * var(--scale));
- border-left: 2px solid var(--line-color);
- border-bottom: 2px solid var(--line-color);
- border-radius: 0 0 0 calc(2.5 * var(--scale));
-}
-
-.taskmgr-task {
- display: flex;
- height: calc(10 * var(--scale));
- line-height: calc(10 * var(--scale));
-}
-
-.taskmgr-task-title {
- flex-grow: 1;
- padding-left: calc(2.5 * var(--scale));
-}