From 8a697652d94a9f88e316bf33692c7956d19abf65 Mon Sep 17 00:00:00 2001 From: jelveh Date: Fri, 25 Jul 2025 23:59:29 -0700 Subject: [PATCH] Use regular `UIWindow` component in `UIWindowTaskManager` --- src/gui/src/UI/UIWindowTaskManager.js | 401 ++++++++------------------ src/gui/src/css/style.css | 90 ++++++ 2 files changed, 215 insertions(+), 276 deletions(-) diff --git a/src/gui/src/UI/UIWindowTaskManager.js b/src/gui/src/UI/UIWindowTaskManager.js index 177550658..d7c6ab4d7 100644 --- a/src/gui/src/UI/UIWindowTaskManager.js +++ b/src/gui/src/UI/UIWindowTaskManager.js @@ -19,8 +19,7 @@ import { END_HARD, END_SOFT } from "../definitions.js"; import UIAlert from "./UIAlert.js"; import UIContextMenu from "./UIContextMenu.js"; -import { Component, defineComponent } from '../util/Component.js'; -import UIComponentWindow from './UIComponentWindow.js'; +import UIWindow from './UIWindow.js'; const end_process = async (uuid, force) => { const svc_process = globalThis.services.get('process'); @@ -62,310 +61,122 @@ const end_process = async (uuid, force) => { process.signal(force ? END_HARD : END_SOFT); }; -class TaskManagerTable extends Component { - static ID = 'ui.component.TaskManagerTable'; - static PROPERTIES = { - tasks: { value: [] }, - }; +const calculate_indent_string = (indent_level, is_last_item_stack, is_last_item) => { + // Returns a string of '| ├└' + let result = ''; - 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; - } + 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; - table { - box-sizing: border-box; - border-collapse: collapse; - width: 100%; + if (has_trunk && has_branch) { + result += '├'; + } else if (has_trunk) { + result += '|'; + } else if (has_branch) { + result += '└'; + } else { + result += ' '; } - - thead th { - box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2); - backdrop-filter: blur(2px); - position: sticky; - z-index: 100; - padding: - calc(10 * var(--scale)) - calc(2.5 * var(--scale)) - calc(5 * var(--scale)) - calc(2.5 * var(--scale)); - top: 0; - background-color: hsla(0, 0%, 100%, 0.8); - text-align: left; - border-bottom: 1px solid #e0e0e0; - } - - thead th:not(:last-of-type) { - border-right: 1px solid #e0e0e0; - } - - tbody > tr > td { - border-bottom: 1px solid #e0e0e0; - padding: 0 calc(2.5 * var(--scale)); - vertical-align: middle; - } - `; - - #svc_process = globalThis.services.get('process'); - - create_template ({ template }) { - $(template).html(` - - - - - - - - - - -
${i18n('taskmgr_header_name')}${i18n('taskmgr_header_type')}${i18n('taskmgr_header_status')}
- `); } - on_ready ({ listen }) { - listen('tasks', tasks => { - const row_data = this.#iter_tasks(tasks, { indent_level: 0, is_last_item_stack: [] }); - const tbody = $(this.dom_).find('.taskmgr-taskarea'); - tbody.empty(); + return result; +}; - for (const data of row_data) { - const row = new TaskManagerRow(data); - row.attach(tbody[0]); - } - }); - } - - #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 += ' '; +const generate_task_rows = (items, { indent_level, is_last_item_stack }) => { + const svc_process = globalThis.services.get('process'); + let rows_html = ''; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const is_last_item = i === items.length - 1; + const indentation = calculate_indent_string(indent_level, is_last_item_stack, is_last_item); + + // Generate indentation HTML + let indentation_html = ''; + for (const c of indentation) { + indentation_html += `
`; + switch (c) { + case ' ': + break; + case '|': + indentation_html += `
`; + break; + case '└': + indentation_html += `
`; + break; + case '├': + indentation_html += `
`; + indentation_html += `
`; + break; } + indentation_html += `
`; } - 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({ - 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(TaskManagerTable); - -class TaskManagerRow extends Component { - static ID = 'ui.component.TaskManagerRow'; - - 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 = ` - -
-
-
-
- - - + rows_html += ` + + +
+
${indentation_html}
+
${item.name}
+
+ + ${i18n('process_type_' + item.type)} + ${i18n('process_status_' + item.status.i18n_key)} + `; - } - 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); - }, - }, - ], + const children = svc_process.get_children_of(item.uuid); + if (children) { + rows_html += generate_task_rows(children, { + indent_level: indent_level + 1, + is_last_item_stack: [ ...is_last_item_stack, is_last_item ], }); - }); + } } -} -defineComponent(TaskManagerRow); + + return rows_html; +}; const UIWindowTaskManager = async function UIWindowTaskManager () { const svc_process = globalThis.services.get('process'); - let task_manager_table = new TaskManagerTable({ - tasks: [svc_process.get_init()], - }); + let h = ''; - const interval = setInterval(() => { - const processes = [svc_process.get_init()]; - task_manager_table.set('tasks', processes); - }, 500); + h += `
`; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += ``; + h += `
${i18n('taskmgr_header_name')}${i18n('taskmgr_header_type')}${i18n('taskmgr_header_status')}
`; + h += `
`; - const w = await UIComponentWindow({ - component: task_manager_table, - window_class: 'window-task-manager', + const el_window = await UIWindow({ title: i18n('task_manager'), icon: globalThis.icons['cog.svg'], uid: null, is_dir: false, - message: 'message', single_instance: true, app: 'taskmgr', - // body_icon: options.body_icon, - // backdrop: options.backdrop ?? false, is_resizable: true, is_droppable: false, has_head: true, selectable_body: true, draggable_body: false, allow_context_menu: false, - // allow_native_ctxmenu: true, show_in_taskbar: true, dominant: true, - body_content: '', + body_content: h, width: 350, - // parent_uuid: options.parent_uuid, - // ...options.window_options, + window_class: 'window-task-manager', window_css:{ height: 'initial', }, @@ -379,17 +190,55 @@ const UIWindowTaskManager = async function UIWindowTaskManager () { 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); + padding: '0', }, }); + + const update_tasks = () => { + const processes = [svc_process.get_init()]; + const rows_html = generate_task_rows(processes, { indent_level: 0, is_last_item_stack: [] }); + $(el_window).find('.taskmgr-taskarea').html(rows_html); + }; + + // Set up context menu for task rows + $(el_window).on('contextmenu', '.task-row', function(e) { + e.preventDefault(); + const uuid = $(this).data('uuid'); + UIContextMenu({ + items: [ + { + html: i18n('close'), + onClick: () => { + end_process(uuid); + }, + }, + { + html: i18n('force_quit'), + onClick: () => { + end_process(uuid, true); + }, + }, + ], + }); + }); + + // Initial task update + update_tasks(); + + // Set up interval to refresh tasks + const interval = setInterval(update_tasks, 500); + + // Clean up interval when window is closed + $(el_window).on('close', () => { + clearInterval(interval); + }); + + return el_window; } export default UIWindowTaskManager; diff --git a/src/gui/src/css/style.css b/src/gui/src/css/style.css index d0f660cc8..b8cfff317 100644 --- a/src/gui/src/css/style.css +++ b/src/gui/src/css/style.css @@ -2525,6 +2525,96 @@ label { height: 40px; } +/***************************************************** + * Task Manager + *****************************************************/ + +.task-manager-container { + 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; +} + +.task-manager-container table { + box-sizing: border-box; + border-collapse: collapse; + width: 100%; +} + +.task-manager-container thead th { + box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2); + backdrop-filter: blur(2px); + position: sticky; + z-index: 100; + padding: calc(10 * var(--scale)) calc(2.5 * var(--scale)) calc(5 * var(--scale)) calc(2.5 * var(--scale)); + top: 0; + background-color: hsla(0, 0%, 100%, 0.8); + text-align: left; + border-bottom: 1px solid #e0e0e0; + padding: 5px; +} + +.task-manager-container thead th:not(:last-of-type) { + border-right: 1px solid #e0e0e0; +} + +.task-manager-container tbody > tr > td { + border-bottom: 1px solid #e0e0e0; + padding: 0 calc(2.5 * var(--scale)); + vertical-align: middle; + padding-left: 0; +} + +.task-manager-container 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)); +} + + #clock { display: none; color: white;