Skip to content

Commit bfd1f8f

Browse files
authored
Configurable taskbar position (#1372)
* feat(gui): Enhance taskbar functionality and positioning - Implement dynamic taskbar position management with options for left, bottom, and right placements. - Update desktop dimensions and window positioning based on taskbar position. - Add context menu options for changing taskbar position on desktop devices. - Ensure mobile devices always display the taskbar at the bottom. - Introduce CSS styles for taskbar positioning and adjust desktop padding accordingly. * feat(gui): Reinitialize tooltips based on taskbar position - Implement dynamic tooltip positioning for taskbar items based on the current taskbar location (left, right, bottom). - Ensure tooltips are destroyed and recreated when the taskbar position is updated, enhancing user experience and consistency. * feat(gui): Improve taskbar and tooltip positioning - Add support for dynamic tooltip positioning for the 'top' taskbar location. - Adjust CSS styles for arrow tooltips to enhance visual alignment and positioning. - Refine taskbar item positioning for left and right placements to ensure consistent appearance. * Improve popover positioning based on taskbar location - Improve `UIPopover` positioning logic to account for left and right taskbar placements. - Adjust Y position of popovers to ensure they appear correctly below toolbars for left/right taskbars. - Update CSS styles for arrow tooltips to enforce consistent positioning with !important declarations. * Refactor context menu positioning logic in UITaskbarItem - Introduce a helper function to dynamically calculate context menu position based on the taskbar's location (top, bottom, left, right). - Ensure context menus are positioned correctly relative to the taskbar item, improving usability across different taskbar placements. * Fix UITaskbar window height calculation for consistent positioning - Adjust the height calculation for windows in the UITaskbar to remove an unnecessary offset, ensuring accurate window sizing relative to the toolbar height. - This change improves the visual consistency of maximized windows across different taskbar placements. * Enhance window snapping and positioning based on taskbar location - Introduce a new function to calculate snap dimensions and positions dynamically based on the taskbar's location (left, right, bottom). - Update window snapping logic to ensure windows are positioned correctly relative to the taskbar, improving usability and visual consistency. - Adjust boundary checks for window placement to account for taskbar height and position, enhancing the overall user experience. * Update default taskbar position to 'left' and adjust CSS padding for desktop layout - Change the default taskbar position from 'bottom' to 'left' in UITaskbar. - Modify CSS to increase left padding for desktop taskbar positioning, enhancing layout consistency. * Improve CSS for desktop layout with height adjustments - Update CSS styles for input fields and desktop taskbar to set height to 100vh, ensuring consistent full-page layout across different screen sizes. - This change improves the visual consistency and usability of the interface. * Improve desktop selectable interactivity and taskbar tooltip behavior - Add functionality to mark the desktop as selectable active, improving user interaction with desktop elements. - Update tooltip display logic to only show when the desktop is not in a selectable state, enhancing usability. - Adjust CSS styles for desktop layout, ensuring consistent behavior and appearance when the desktop is active or inactive. * Update default taskbar position logic for first-time and existing users - Set the taskbar position to 'left' for first-time visitors and default to 'bottom' for returning users without a saved preference. - This change improves the user experience by providing a more intuitive initial layout for new users while maintaining consistency for existing users.
1 parent 5b46f0b commit bfd1f8f

File tree

6 files changed

+700
-141
lines changed

6 files changed

+700
-141
lines changed

src/gui/src/UI/UIDesktop.js

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,9 @@ async function UIDesktop(options) {
748748
// ---------------------------------------------------------------
749749
UITaskbar();
750750

751+
// Update desktop dimensions after taskbar is initialized with position
752+
window.update_desktop_dimensions_for_taskbar();
753+
751754
const el_desktop = document.querySelector('.desktop');
752755

753756
window.active_element = el_desktop;
@@ -1100,6 +1103,9 @@ async function UIDesktop(options) {
11001103

11011104
selection.clearSelection();
11021105
}
1106+
1107+
// mark desktop as selectable active
1108+
$('.desktop').addClass('desktop-selectable-active');
11031109
})
11041110
.on('move', ({ store: { changed: { added, removed } }, event }) => {
11051111
window.desktop_selectable_is_active = true;
@@ -1125,6 +1131,7 @@ async function UIDesktop(options) {
11251131
})
11261132
.on('stop', evt => {
11271133
window.desktop_selectable_is_active = false;
1134+
$('.desktop').removeClass('desktop-selectable-active');
11281135
});
11291136
}
11301137
// ----------------------------------------------------
@@ -1755,31 +1762,66 @@ $(document).on('contextmenu taphold', '.taskbar', function (event) {
17551762

17561763
event.preventDefault();
17571764
event.stopPropagation();
1765+
1766+
// Get current taskbar position
1767+
const currentPosition = window.taskbar_position || 'bottom';
1768+
1769+
// Create base menu items
1770+
let menuItems = [];
1771+
1772+
// Only show position submenu on desktop devices
1773+
if (!isMobile.phone && !isMobile.tablet) {
1774+
menuItems.push({
1775+
html: "Position",
1776+
items: [
1777+
{
1778+
html: "Left",
1779+
checked: currentPosition === 'left',
1780+
onClick: function() {
1781+
window.update_taskbar_position('left');
1782+
}
1783+
},
1784+
{
1785+
html: "Bottom",
1786+
checked: currentPosition === 'bottom',
1787+
onClick: function() {
1788+
window.update_taskbar_position('bottom');
1789+
}
1790+
},
1791+
{
1792+
html: "Right",
1793+
checked: currentPosition === 'right',
1794+
onClick: function() {
1795+
window.update_taskbar_position('right');
1796+
}
1797+
}
1798+
]
1799+
});
1800+
menuItems.push('-'); // divider
1801+
}
1802+
1803+
// Add the "Show open windows" option for all devices
1804+
menuItems.push({
1805+
html: "Show open windows",
1806+
onClick: function () {
1807+
$(`.window`).showWindow();
1808+
}
1809+
});
1810+
1811+
// Add the "Show the desktop" option for all devices
1812+
menuItems.push({
1813+
html: "Show the desktop",
1814+
onClick: function () {
1815+
$(`.window`).hideWindow();
1816+
}
1817+
});
1818+
17581819
UIContextMenu({
17591820
parent_element: $('.taskbar'),
1760-
items: [
1761-
//--------------------------------------------------
1762-
// Show open windows
1763-
//--------------------------------------------------
1764-
{
1765-
html: "Show open windows",
1766-
onClick: function () {
1767-
$(`.window`).showWindow();
1768-
}
1769-
},
1770-
//--------------------------------------------------
1771-
// Show the desktop
1772-
//--------------------------------------------------
1773-
{
1774-
html: "Show the desktop",
1775-
onClick: function () {
1776-
$(`.window`).hideWindow();
1777-
}
1778-
}
1779-
]
1821+
items: menuItems
17801822
});
17811823
return false;
1782-
})
1824+
});
17831825

17841826
// Toolbar context menu
17851827
$(document).on('contextmenu taphold', '.toolbar', function (event) {

src/gui/src/UI/UIPopover.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,19 @@ function UIPopover(options){
6262
// X position
6363
const popover_width = options.width ?? $(el_popover).width();
6464
if(options.center_horizontally){
65-
x_pos = window.innerWidth/2 - popover_width/2 - 15;
65+
// Check taskbar position to determine popover positioning
66+
const taskbar_position = window.taskbar_position || 'bottom';
67+
68+
if(taskbar_position === 'left'){
69+
// Position in top-left corner for left taskbar
70+
x_pos = window.taskbar_height + 10; // Just to the right of the taskbar
71+
}else if(taskbar_position === 'right'){
72+
// Position in top-right corner for right taskbar
73+
x_pos = window.innerWidth - popover_width - window.taskbar_height - 40; // Just to the left of the taskbar
74+
}else{
75+
// Default bottom taskbar behavior - center horizontally
76+
x_pos = window.innerWidth/2 - popover_width/2 - 15;
77+
}
6678
}else{
6779
if(options.position === 'bottom' || options.position === 'top')
6880
x_pos = options.left ?? ($(options.snapToElement).offset().left - (popover_width/ 2) + 10);
@@ -73,7 +85,16 @@ function UIPopover(options){
7385
// Y position
7486
const popover_height = options.height ?? $(el_popover).height();
7587
if(options.center_horizontally){
76-
y_pos = options.top ?? (window.innerHeight - (window.taskbar_height + popover_height + 10));
88+
// Check taskbar position to determine popover positioning
89+
const taskbar_position = window.taskbar_position || 'bottom';
90+
91+
if(taskbar_position === 'left' || taskbar_position === 'right'){
92+
// Position at top for left/right taskbars
93+
y_pos = window.toolbar_height + 10; // Just below the toolbar
94+
}else{
95+
// Default bottom taskbar behavior - position above taskbar
96+
y_pos = options.top ?? (window.innerHeight - (window.taskbar_height + popover_height + 10));
97+
}
7798
}else{
7899
y_pos = options.top ?? ($(options.snapToElement).offset().top + $(options.snapToElement).height() + 5);
79100
}

src/gui/src/UI/UITaskbar.js

Lines changed: 202 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@ async function UITaskbar(options){
2727
options = options ?? {};
2828
options.content = options.content ?? '';
2929

30+
// if first visit ever, set taskbar position to left
31+
if(window.first_visit_ever){
32+
await puter.kv.set('taskbar_position', 'left');
33+
}
34+
35+
// Load taskbar position preference from storage
36+
let taskbar_position = await puter.kv.get('taskbar_position');
37+
// if this is not first visit, set taskbar position to bottom since it's from a user that
38+
// used puter before customizing taskbar position was added and the taskbar position was set to bottom
39+
if (!taskbar_position) {
40+
taskbar_position = 'bottom'; // default position
41+
await puter.kv.set('taskbar_position', taskbar_position);
42+
}
43+
44+
// Force bottom position on mobile devices
45+
if (isMobile.phone || isMobile.tablet) {
46+
taskbar_position = 'bottom';
47+
// Update the stored preference to bottom for mobile devices
48+
await puter.kv.set('taskbar_position', taskbar_position);
49+
}
50+
51+
// Set global taskbar position
52+
window.taskbar_position = taskbar_position;
53+
3054
// get launch apps
3155
$.ajax({
3256
url: window.api_origin + "/get-launch-apps?icon_size=64",
@@ -42,10 +66,13 @@ async function UITaskbar(options){
4266
});
4367

4468
let h = '';
45-
h += `<div id="ui-taskbar_${window.global_element_id}" class="taskbar" style="height:${window.taskbar_height}px;">`;
69+
h += `<div id="ui-taskbar_${window.global_element_id}" class="taskbar taskbar-position-${taskbar_position}" style="height:${window.taskbar_height}px;">`;
4670
h += `<div class="taskbar-sortable" style="display: flex; justify-content: center; z-index: 99999;"></div>`;
4771
h += `</div>`;
4872

73+
if(taskbar_position === 'left' || taskbar_position === 'right'){
74+
$('.desktop').addClass(`desktop-taskbar-position-${taskbar_position}`);
75+
}
4976

5077
$('.desktop').append(h);
5178

@@ -242,12 +269,15 @@ async function UITaskbar(options){
242269
window.make_taskbar_sortable();
243270
}
244271

272+
//-------------------------------------------
273+
// Taskbar is sortable
274+
//-------------------------------------------
245275
window.make_taskbar_sortable = function(){
246-
//-------------------------------------------
247-
// Taskbar is sortable
248-
//-------------------------------------------
276+
const position = window.taskbar_position || 'bottom';
277+
const axis = position === 'bottom' ? 'x' : 'y';
278+
249279
$('.taskbar-sortable').sortable({
250-
axis: "x",
280+
axis: axis,
251281
items: '.taskbar-item-sortable:not(.has-open-contextmenu)',
252282
cancel: '.has-open-contextmenu',
253283
placeholder: "taskbar-item-sortable-placeholder",
@@ -304,4 +334,171 @@ window.make_taskbar_sortable = function(){
304334
});
305335
}
306336

337+
// Function to update taskbar position
338+
window.update_taskbar_position = async function(new_position) {
339+
// Prevent position changes on mobile devices - always keep bottom
340+
if (isMobile.phone || isMobile.tablet) {
341+
return;
342+
}
343+
344+
// Valid positions
345+
const valid_positions = ['left', 'bottom', 'right'];
346+
347+
if (!valid_positions.includes(new_position)) {
348+
return;
349+
}
350+
351+
// Store the new position
352+
await puter.kv.set('taskbar_position', new_position);
353+
window.taskbar_position = new_position;
354+
355+
// Remove old position classes and add new one
356+
$('.taskbar').removeClass('taskbar-position-left taskbar-position-bottom taskbar-position-right');
357+
$('.taskbar').addClass(`taskbar-position-${new_position}`);
358+
359+
// update desktop class, if left or right, add `desktop-taskbar-position-left` or `desktop-taskbar-position-right`
360+
$('.desktop').removeClass('desktop-taskbar-position-left');
361+
$('.desktop').removeClass('desktop-taskbar-position-right');
362+
$('.desktop').addClass(`desktop-taskbar-position-${new_position}`);
363+
364+
// Update desktop height/width calculations based on new position
365+
window.update_desktop_dimensions_for_taskbar();
366+
367+
// Update window positions if needed (for maximized windows)
368+
$('.window[data-is_maximized="1"]').each(function() {
369+
const el_window = this;
370+
window.update_maximized_window_for_taskbar(el_window);
371+
});
372+
373+
// Re-initialize sortable with correct axis
374+
$('.taskbar-sortable').sortable('destroy');
375+
window.make_taskbar_sortable();
376+
377+
// Reinitialize all taskbar item tooltips with new position
378+
$('.taskbar-item').each(function() {
379+
const $item = $(this);
380+
// Destroy existing tooltip
381+
if ($item.data('ui-tooltip')) {
382+
$item.tooltip('destroy');
383+
}
384+
385+
// Helper function to get tooltip position based on taskbar position
386+
function getTooltipPosition() {
387+
const taskbarPosition = window.taskbar_position || 'bottom';
388+
389+
if (taskbarPosition === 'bottom') {
390+
return {
391+
my: "center bottom-20",
392+
at: "center top"
393+
};
394+
} else if (taskbarPosition === 'top') {
395+
return {
396+
my: "center top+20",
397+
at: "center bottom"
398+
};
399+
} else if (taskbarPosition === 'left') {
400+
return {
401+
my: "left+20 center",
402+
at: "right center"
403+
};
404+
} else if (taskbarPosition === 'right') {
405+
return {
406+
my: "right-20 center",
407+
at: "left center"
408+
};
409+
}
410+
return {
411+
my: "center bottom-20",
412+
at: "center top"
413+
}; // fallback
414+
}
415+
416+
const tooltipPosition = getTooltipPosition();
417+
418+
// Reinitialize tooltip with new position
419+
$item.tooltip({
420+
items: ".taskbar:not(.children-have-open-contextmenu) .taskbar-item",
421+
position: {
422+
my: tooltipPosition.my,
423+
at: tooltipPosition.at,
424+
using: function( position, feedback ) {
425+
$( this ).css( position );
426+
$( "<div>" )
427+
.addClass( "arrow" )
428+
.addClass( feedback.vertical )
429+
.addClass( feedback.horizontal )
430+
.appendTo( this );
431+
}
432+
}
433+
});
434+
});
435+
};
436+
437+
// Function to update desktop dimensions based on taskbar position
438+
window.update_desktop_dimensions_for_taskbar = function() {
439+
const position = window.taskbar_position || 'bottom';
440+
441+
if (position === 'bottom') {
442+
$('.desktop').css({
443+
'height': `calc(100vh - ${window.taskbar_height + window.toolbar_height}px)`,
444+
'width': '100%',
445+
'left': '0',
446+
'top': `${window.toolbar_height}px`
447+
});
448+
} else if (position === 'left') {
449+
$('.desktop').css({
450+
'height': `calc(100vh - ${window.toolbar_height}px)`,
451+
'width': `calc(100% - ${window.taskbar_height}px)`,
452+
'left': `${window.taskbar_height}px`,
453+
'top': `${window.toolbar_height}px`
454+
});
455+
} else if (position === 'right') {
456+
$('.desktop').css({
457+
'height': `calc(100vh - ${window.toolbar_height}px)`,
458+
'width': `calc(100% - ${window.taskbar_height}px)`,
459+
'left': '0',
460+
'top': `${window.toolbar_height}px`
461+
});
462+
}
463+
};
464+
465+
// Function to update maximized window positioning based on taskbar position
466+
window.update_maximized_window_for_taskbar = function(el_window) {
467+
const position = window.taskbar_position || 'bottom';
468+
469+
// Handle fullpage mode differently
470+
if (window.is_fullpage_mode) {
471+
$(el_window).css({
472+
'top': window.toolbar_height + 'px',
473+
'left': '0',
474+
'width': '100%',
475+
'height': `calc(100% - ${window.toolbar_height}px)`,
476+
});
477+
return;
478+
}
479+
480+
if (position === 'bottom') {
481+
$(el_window).css({
482+
'top': window.toolbar_height + 'px',
483+
'left': '0',
484+
'width': '100%',
485+
'height': `calc(100% - ${window.taskbar_height + window.toolbar_height + 6}px)`,
486+
});
487+
} else if (position === 'left') {
488+
$(el_window).css({
489+
'top': window.toolbar_height + 'px',
490+
'left': window.taskbar_height + 'px',
491+
'width': `calc(100% - ${window.taskbar_height}px)`,
492+
'height': `calc(100% - ${window.toolbar_height}px)`,
493+
});
494+
} else if (position === 'right') {
495+
$(el_window).css({
496+
'top': window.toolbar_height + 'px',
497+
'left': '0',
498+
'width': `calc(100% - ${window.taskbar_height}px)`,
499+
'height': `calc(100% - ${window.toolbar_height}px)`,
500+
});
501+
}
502+
};
503+
307504
export default UITaskbar;

0 commit comments

Comments
 (0)