From 72641c66a29934cd573c112b3bcf3032db27895a Mon Sep 17 00:00:00 2001 From: Nariman Jelveh Date: Wed, 12 Jun 2024 19:18:24 -0700 Subject: [PATCH] Support the modification of individual items withing a menu bar --- packages/puter-js/src/modules/UI.js | 20 ++++++++++++ src/IPC.js | 50 ++++++++++++++++++++++++++--- src/UI/UIContextMenu.js | 16 ++++++--- src/UI/UIWindow.js | 11 ++++++- src/globals.js | 1 + src/helpers.js | 15 +++++++++ 6 files changed, 103 insertions(+), 10 deletions(-) diff --git a/packages/puter-js/src/modules/UI.js b/packages/puter-js/src/modules/UI.js index c84b45da..08b0839f 100644 --- a/packages/puter-js/src/modules/UI.js +++ b/packages/puter-js/src/modules/UI.js @@ -678,6 +678,26 @@ class UI extends EventListener { this.#postMessageWithObject('setMenubar', spec); } + disableMenuItem = function(item_id) { + this.#postMessageWithObject('disableMenuItem', {id: item_id}); + } + + enableMenuItem = function(item_id) { + this.#postMessageWithObject('enableMenuItem', {id: item_id}); + } + + setMenuItemIcon = function(item_id, icon) { + this.#postMessageWithObject('setMenuItemIcon', {id: item_id, icon: icon}); + } + + setMenuItemIconActive = function(item_id, icon) { + this.#postMessageWithObject('setMenuItemIconActive', {id: item_id, icon: icon}); + } + + setMenuItemChecked = function(item_id, checked) { + this.#postMessageWithObject('setMenuItemChecked', {id: item_id, checked: checked}); + } + contextMenu = function(spec) { this.#postMessageWithObject('contextMenu', spec); } diff --git a/src/IPC.js b/src/IPC.js index f00b6a13..b0c1e0b6 100644 --- a/src/IPC.js +++ b/src/IPC.js @@ -393,7 +393,6 @@ window.addEventListener('message', async (event) => { // get parent window const el_window = window.window_for_app_instance(event.data.appInstanceID); - let items = value.items ?? []; const sanitize_items = items => { return items.map(item => { @@ -432,6 +431,36 @@ window.addEventListener('message', async (event) => { $(target_iframe).get(0).focus({preventScroll:true}); } + // -------------------------------------------------------- + // disableMenuItem + // -------------------------------------------------------- + else if(event.data.msg === 'disableMenuItem'){ + set_menu_item_prop(window.menubars[event.data.appInstanceID], event.data.value.id, 'disabled', true); + } + // -------------------------------------------------------- + // enableMenuItem + // -------------------------------------------------------- + else if(event.data.msg === 'enableMenuItem'){ + set_menu_item_prop(window.menubars[event.data.appInstanceID], event.data.value.id, 'disabled', false); + } + //-------------------------------------------------------- + // setMenuItemIcon + //-------------------------------------------------------- + else if(event.data.msg === 'setMenuItemIcon'){ + set_menu_item_prop(window.menubars[event.data.appInstanceID], event.data.value.id, 'icon', event.data.value.icon); + } + //-------------------------------------------------------- + // setMenuItemIconActive + //-------------------------------------------------------- + else if(event.data.msg === 'setMenuItemIconActive'){ + set_menu_item_prop(window.menubars[event.data.appInstanceID], event.data.value.id, 'icon_active', event.data.value.icon_active); + } + //-------------------------------------------------------- + // setMenuItemChecked + //-------------------------------------------------------- + else if(event.data.msg === 'setMenuItemChecked'){ + set_menu_item_prop(window.menubars[event.data.appInstanceID], event.data.value.id, 'checked', event.data.value.checked); + } //-------------------------------------------------------- // setMenubar //-------------------------------------------------------- @@ -452,6 +481,9 @@ window.addEventListener('message', async (event) => { e.preventDefault(); }); + if(!window.menubars[event.data.appInstanceID]) + window.menubars[event.data.appInstanceID] = value.items; + const sanitize_items = items => { return items.map(item => { // Check if the item is just '-' @@ -461,6 +493,10 @@ window.addEventListener('message', async (event) => { // Otherwise, proceed as before return { html: item.label, + disabled: item.disabled, + checked: item.checked, + icon: item.icon ? `` : undefined, + icon_active: item.icon_active ? `` : undefined, action: item.action, items: item.items ? sanitize_items(item.items) : undefined }; @@ -489,8 +525,8 @@ window.addEventListener('message', async (event) => { // Open the context menu const ctxMenu = UIContextMenu({ - delay, - parent_element, + delay: delay, + parent_element: parent_element, position: {top: pos.top + 30, left: pos.left}, css: { 'box-shadow': '0px 2px 6px #00000059' @@ -517,9 +553,13 @@ window.addEventListener('message', async (event) => { const item = items[i]; const label = html_encode(item.label); const el_item = $(`
${label}
`); - const parent_element = el_item.parent()[0]; + const parent_element = el_item.get(0); el_item.on('mousedown', (e) => { + // check if it has has-open-context-menu class + if ( el_item.hasClass('has-open-contextmenu') ) { + return; + } if ( state_open ) { state_open = false; current && current.cancel({ meta: 'menubar' }); @@ -563,7 +603,7 @@ window.addEventListener('message', async (event) => { menubar_buttons.push(el_item); } }; - add_items($menubar, value.items); + add_items($menubar, window.menubars[event.data.appInstanceID]); } //-------------------------------------------------------- // setWindowWidth diff --git a/src/UI/UIContextMenu.js b/src/UI/UIContextMenu.js index 0a12e523..a3a10aca 100644 --- a/src/UI/UIContextMenu.js +++ b/src/UI/UIContextMenu.js @@ -21,7 +21,6 @@ function UIContextMenu(options){ $('.window-active .window-app-iframe').css('pointer-events', 'none'); const menu_id = window.global_element_id++; - let h = ''; h += `
`; // icon - h += `${options.items[i].icon ?? ''}`; - h += `${options.items[i].icon_active ?? (options.items[i].icon ?? '')}`; + if(options.items[i].checked === true){ + h += ``; + h += ``; + }else{ + h += `${options.items[i].icon ?? ''}`; + h += `${options.items[i].icon_active ?? (options.items[i].icon ?? '')}`; + } // label h += `${options.items[i].html}`; h += `${options.items[i].html_active ?? options.items[i].html}`; @@ -275,7 +279,11 @@ function UIContextMenu(options){ if(options.parent_element){ $(options.parent_element).parent().removeClass('children-have-open-contextmenu'); - $(options.parent_element).css('overflow', 'scroll'); + // if the parent element is not a window, make it scrollable again + if (!$(options.parent_element).hasClass('window-body')) { + $(options.parent_element).css('overflow', 'scroll'); + } + $(options.parent_element).removeClass('has-open-contextmenu'); if($(options.parent_element).hasClass('taskbar-item')){ window.make_taskbar_sortable() diff --git a/src/UI/UIWindow.js b/src/UI/UIWindow.js index f8356b20..4f90c1a0 100644 --- a/src/UI/UIWindow.js +++ b/src/UI/UIWindow.js @@ -2807,16 +2807,25 @@ $.fn.close = async function(options) { $(this).each(async function() { const el_iframe = $(this).find('.window-app-iframe'); const app_uses_sdk = el_iframe.length > 0 && el_iframe.attr('data-appUsesSDK') === 'true'; - // tell child app that this window is about to close, get its response + if(app_uses_sdk){ + // get appInstanceID + const appInstanceID = el_iframe.closest('.window').attr('data-element_uuid'); + // tell child app that this window is about to close, get its response if(!options.bypass_iframe_messaging){ const resp = await window.sendWindowWillCloseMsg(el_iframe.get(0)); if(!resp.msg){ return false; } } + // remove the menubar from the window.menubars array + if(appInstanceID){ + delete window.menubars[appInstanceID]; + window.app_instance_ids.delete(appInstanceID); + } } + if ( this.on_before_exit ) { if ( ! await this.on_before_exit() ) return false; } diff --git a/src/globals.js b/src/globals.js index ed5d40fd..37afdc7b 100644 --- a/src/globals.js +++ b/src/globals.js @@ -26,6 +26,7 @@ window.progress_tracker = []; window.upload_item_global_id = 0; window.app_instance_ids = new Set(); +window.menubars = []; window.download_progress = []; window.download_item_global_id = 0; diff --git a/src/helpers.js b/src/helpers.js index 65f1c155..3c023250 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -3587,3 +3587,18 @@ window.check_password_strength = (password) => { report: criteria_report, }; } + +window.set_menu_item_prop = (items, item_id, prop, val) => { + // iterate over items + for (const item of items) { + // find the item with the given item_id + if (item.id === item_id) { + // set the property value + item[prop] = val; + break; + } + else if(item.items){ + set_menu_item_prop(item.items, item_id, prop, val); + } + } +}; \ No newline at end of file