Implement the contextMenu API for Puter.js

This commit is contained in:
Nariman Jelveh 2024-06-02 16:15:35 -07:00
parent cf8a07286a
commit a81f461b54
4 changed files with 162 additions and 65 deletions

View File

@ -89,8 +89,8 @@ export class Hydrator {
) {
const { id } = value;
return (...args) => {
console.log('sending message', { $SCOPE, id, args });
console.log('target', this.target);
// console.log('sending message', { $SCOPE, id, args });
// console.log('target', this.target);
this.target.postMessage({ $SCOPE, id, args }, '*');
};
} else if (Array.isArray(value)) {

View File

@ -429,6 +429,25 @@ class UI extends EventListener {
this.#lastBroadcastValue.set(name, data);
}
});
// We need to send the mouse position to the host environment
// This is important since a lot of UI elements depend on the mouse position (e.g. ContextMenus, Tooltips, etc.)
// and the host environment needs to know the mouse position to show these elements correctly.
// The host environment can't just get the mouse position since when the mouse is over an iframe it
// will not be able to get the mouse position. So we need to send the mouse position to the host environment.
document.addEventListener('mousemove', async (event)=>{
// Get the mouse position from the event object
this.mouseX = event.clientX;
this.mouseY = event.clientY;
// send the mouse position to the host environment
this.messageTarget?.postMessage({
msg: "mouseMoved",
appInstanceID: this.appInstanceID,
x: this.mouseX,
y: this.mouseY,
}, '*');
});
}
onWindowClose = function(callback) {
@ -659,6 +678,10 @@ class UI extends EventListener {
this.#postMessageWithObject('setMenubar', spec);
}
contextMenu = function(spec) {
this.#postMessageWithObject('contextMenu', spec);
}
/**
* Asynchronously extracts entries from DataTransferItems, like files and directories.
*

View File

@ -355,6 +355,75 @@ window.addEventListener('message', async (event) => {
}, '*');
}
//--------------------------------------------------------
// mouseMoved
//--------------------------------------------------------
else if(event.data.msg === 'mouseMoved'){
// Auth
if(!window.is_auth() && !(await UIWindowSignup({referrer: app_name})))
return;
// get x and y and sanitize
let x = parseInt(event.data.x);
let y = parseInt(event.data.y);
// get parent window
const el_window = window.window_for_app_instance(event.data.appInstanceID);
// get window position
const window_position = $(el_window).position();
// update mouse position
update_mouse_position(x + window_position.left, y + window_position.top + 25);
}
//--------------------------------------------------------
// contextMenu
//--------------------------------------------------------
else if(event.data.msg === 'contextMenu'){
// Auth
if(!window.is_auth() && !(await UIWindowSignup({referrer: app_name})))
return;
const hydrator = puter.util.rpc.getHydrator({
target: target_iframe.contentWindow,
});
let value = hydrator.hydrate(event.data.value);
// 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 => {
// Check if the item is just '-'
if (item === '-') {
return '-';
}
// Otherwise, proceed as before
return {
html: item.label,
onClick: () => {
if (item.action !== undefined) {
console.log('item.action', item.action);
item.action();
}
},
items: item.items ? sanitize_items(item.items) : undefined
};
});
};
items = sanitize_items(items);
// Open context menu
UIContextMenu({
items: items,
});
$(target_iframe).get(0).focus({preventScroll:true});
}
//--------------------------------------------------------
// setMenubar
//--------------------------------------------------------
else if(event.data.msg === 'setMenubar') {

View File

@ -1841,68 +1841,7 @@ window.initgui = async function(){
// update mouse position coordinates
$(document).mousemove(function(event){
window.mouseX = event.clientX;
window.mouseY = event.clientY;
// mouse in top-left corner of screen
if((window.mouseX < 150 && window.mouseY < window.toolbar_height + 20) || (window.mouseX < 20 && window.mouseY < 150))
window.current_active_snap_zone = 'nw';
// mouse in left edge of screen
else if(window.mouseX < 20 && window.mouseY >= 150 && window.mouseY < window.desktop_height - 150)
window.current_active_snap_zone = 'w';
// mouse in bottom-left corner of screen
else if(window.mouseX < 20 && window.mouseY > window.desktop_height - 150)
window.current_active_snap_zone = 'sw';
// mouse in right edge of screen
else if(window.mouseX > window.desktop_width - 20 && window.mouseY >= 150 && window.mouseY < window.desktop_height - 150)
window.current_active_snap_zone = 'e';
// mouse in top-right corner of screen
else if((window.mouseX > window.desktop_width - 150 && window.mouseY < window.toolbar_height + 20) || (window.mouseX > window.desktop_width - 20 && window.mouseY < 150))
window.current_active_snap_zone = 'ne';
// mouse in bottom-right corner of screen
else if(window.mouseX > window.desktop_width - 20 && window.mouseY >= window.desktop_height - 150)
window.current_active_snap_zone = 'se';
// mouse in top edge of screen
else if(window.mouseY < window.toolbar_height + 20 && window.mouseX >= 150 && window.mouseX < window.desktop_width - 150)
window.current_active_snap_zone = 'n';
// not in any snap zone
else
window.current_active_snap_zone = undefined;
// mouseover_window
var windows = document.getElementsByClassName("window");
let active_win;
if(windows.length > 0){
let highest_window_zindex = 0;
for(let i=0; i<windows.length; i++){
const rect = windows[i].getBoundingClientRect();
if( window.mouseX > rect.x && window.mouseX < (rect.x + rect.width) && window.mouseY > rect.y && window.mouseY < (rect.y + rect.height)){
if(parseInt($(windows[i]).css('z-index')) >= highest_window_zindex){
active_win = windows[i];
highest_window_zindex = parseInt($(windows[i]).css('z-index'));
}
}
}
}
window.mouseover_window = active_win;
// mouseover_item_container
var item_containers = document.getElementsByClassName("item-container");
let active_ic;
if(item_containers.length > 0){
let highest_window_zindex = 0;
for(let i=0; i<item_containers.length; i++){
const rect = item_containers[i].getBoundingClientRect();
if( window.mouseX > rect.x && window.mouseX < (rect.x + rect.width) && window.mouseY > rect.y && window.mouseY < (rect.y + rect.height)){
let active_container_zindex = parseInt($(item_containers[i]).closest('.window').css('z-index'));
if( !isNaN(active_container_zindex) && active_container_zindex >= highest_window_zindex){
active_ic = item_containers[i];
highest_window_zindex = active_container_zindex;
}
}
}
}
window.mouseover_item_container = active_ic;
update_mouse_position(event.clientX, event.clientY);
});
//--------------------------------------------------------
@ -2139,4 +2078,70 @@ $(document).on('contextmenu', '.disable-context-menu', function(e){
e.preventDefault();
return false;
}
})
})
window.update_mouse_position = function(x, y){
window.mouseX = x;
window.mouseY = y;
// mouse in top-left corner of screen
if((window.mouseX < 150 && window.mouseY < window.toolbar_height + 20) || (window.mouseX < 20 && window.mouseY < 150))
window.current_active_snap_zone = 'nw';
// mouse in left edge of screen
else if(window.mouseX < 20 && window.mouseY >= 150 && window.mouseY < window.desktop_height - 150)
window.current_active_snap_zone = 'w';
// mouse in bottom-left corner of screen
else if(window.mouseX < 20 && window.mouseY > window.desktop_height - 150)
window.current_active_snap_zone = 'sw';
// mouse in right edge of screen
else if(window.mouseX > window.desktop_width - 20 && window.mouseY >= 150 && window.mouseY < window.desktop_height - 150)
window.current_active_snap_zone = 'e';
// mouse in top-right corner of screen
else if((window.mouseX > window.desktop_width - 150 && window.mouseY < window.toolbar_height + 20) || (window.mouseX > window.desktop_width - 20 && window.mouseY < 150))
window.current_active_snap_zone = 'ne';
// mouse in bottom-right corner of screen
else if(window.mouseX > window.desktop_width - 20 && window.mouseY >= window.desktop_height - 150)
window.current_active_snap_zone = 'se';
// mouse in top edge of screen
else if(window.mouseY < window.toolbar_height + 20 && window.mouseX >= 150 && window.mouseX < window.desktop_width - 150)
window.current_active_snap_zone = 'n';
// not in any snap zone
else
window.current_active_snap_zone = undefined;
// mouseover_window
var windows = document.getElementsByClassName("window");
let active_win;
if(windows.length > 0){
let highest_window_zindex = 0;
for(let i=0; i<windows.length; i++){
const rect = windows[i].getBoundingClientRect();
if( window.mouseX > rect.x && window.mouseX < (rect.x + rect.width) && window.mouseY > rect.y && window.mouseY < (rect.y + rect.height)){
if(parseInt($(windows[i]).css('z-index')) >= highest_window_zindex){
active_win = windows[i];
highest_window_zindex = parseInt($(windows[i]).css('z-index'));
}
}
}
}
window.mouseover_window = active_win;
// mouseover_item_container
var item_containers = document.getElementsByClassName("item-container");
let active_ic;
if(item_containers.length > 0){
let highest_window_zindex = 0;
for(let i=0; i<item_containers.length; i++){
const rect = item_containers[i].getBoundingClientRect();
if( window.mouseX > rect.x && window.mouseX < (rect.x + rect.width) && window.mouseY > rect.y && window.mouseY < (rect.y + rect.height)){
let active_container_zindex = parseInt($(item_containers[i]).closest('.window').css('z-index'));
if( !isNaN(active_container_zindex) && active_container_zindex >= highest_window_zindex){
active_ic = item_containers[i];
highest_window_zindex = active_container_zindex;
}
}
}
}
window.mouseover_item_container = active_ic;
}