=== added file 'src/com/goldencode/p2j/ui/client/chui/driver/web/res/p2j.virtual_desktop.js' --- src/com/goldencode/p2j/ui/client/chui/driver/web/res/p2j.virtual_desktop.js 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/chui/driver/web/res/p2j.virtual_desktop.js 2015-09-06 23:04:05 +0000 @@ -0,0 +1,15 @@ +/* +** Module : p2j.virtual_desktop.js +** Abstract : CHUI-specific virtual desktop module. +** +** Copyright (c) 2014-2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- ------------------------------Description---------------------------------- +** 001 SBI 20150906 The virtual desktop empty module. +*/ + +"use strict"; === modified file 'src/com/goldencode/p2j/ui/client/driver/web/index.html' --- src/com/goldencode/p2j/ui/client/driver/web/index.html 2015-09-05 16:09:44 +0000 +++ src/com/goldencode/p2j/ui/client/driver/web/index.html 2015-09-06 21:12:01 +0000 @@ -12,6 +12,7 @@ + === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer.js' --- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer.js 2015-09-05 16:09:44 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.canvas_renderer.js 2015-09-06 22:04:21 +0000 @@ -14,6 +14,18 @@ "use strict"; +/** + * Creates the canvas renderer. + * + * @param {HtmlCanvasElement} canvas + * The html canvas element. + * @param {CanvasRenderingContext2D} ctx + * The canvas 2D graphics context on which to draw. + * @param {LinesStroke} strokesManager + * The stroke manager + * @param {Object} logger + * The p2j logger. + */ function CanvasRenderer(canvas, ctx, strokesManager, logger) { this.canvas = canvas; === modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js' --- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js 2015-09-05 16:09:44 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js 2015-09-06 23:31:23 +0000 @@ -17,6 +17,7 @@ ** added a drawing image operation. ** 004 GES 20150818 Added more GUI support including fonts, cursors, z-order, mouse events. * Fixed many problems with the initial implementation. Implemented SET_ICON. +* Added the draggable task bar implementation. */ "use strict"; @@ -132,6 +133,15 @@ var strokesManager = new LineStrokes(); /** + * Holds the virtual desktop with task bar and bottom and right drop zones. + */ + var desktop; + + /** + * Holds the task bar. + */ + var taskBar; + /** * Constructor for a Window class that represents a Progress 4GL window instance. * * @param wid @@ -694,6 +704,7 @@ loadedImages.set(key, message.subarray(imgDataOffset, imgDataOffset + pixelsInBytes)); } this.canvasRenderer.drawImage(this.ctx, x, y, width, height, imgData, imgDataOffset); + taskBar.draw(); break; case ops.FILL_ROUND_RECT: x = p2j.socket.readInt32BinaryMessage(message, idx + 1); @@ -813,6 +824,7 @@ case ops.SET_TITLE: var title = p2j.socket.readStringBinaryMessage(message, idx + 1); this.title = title; + taskBar.draw(); // TODO: force taskbar to repaint break; case ops.SET_ICON: @@ -833,7 +845,7 @@ this.iconWidth = iconWidth; this.iconHeight = iconHeight; } - + taskBar.setIconForTask(this.id, loadedImages.get(this.iconId)); break; default: if (typeof type !== "undefined") @@ -948,7 +960,7 @@ */ Window.prototype.iconify = function() { - // TODO: implement this + taskBar.iconify(this.id); }; /** @@ -960,22 +972,23 @@ // TODO: this message needs to be sent by the taskbar, when the window is being deiconified // this API should just perform whatever actions are required (i.e. show the window, etc) - var message = new Uint8Array(6); - - // message type - message[0] = 0x10; - offset = offset + 1; - - // 0. the window ID - p2j.socket.writeInt32BinaryMessage(message, offset, wThis.id); - offset = offset + 4; - - // 1. the iconification state - message[offset] = 0; - offset = offset + 1; - - // send the mouse event - p2j.socket.send(message); +// var message = new Uint8Array(6); +// +// // message type +// message[0] = 0x10; +// offset = offset + 1; +// +// // 0. the window ID +// p2j.socket.writeInt32BinaryMessage(message, offset, this.id); +// offset = offset + 4; +// +// // 1. the iconification state +// message[offset] = 0; +// offset = offset + 1; +// +// // send the mouse event +// p2j.socket.send(message); + taskBar.deiconify(this.id); }; /** @@ -1324,6 +1337,9 @@ // add to the z-order list addZOrderEntry(wid, newwin); + // creates new task icon widget for new window + taskBar.addTaskIcon(newwin); + return newwin; }; @@ -1545,6 +1561,23 @@ }; /** + * Sends the active window state to the server. + * + * @param {Number} windowId + * The target windowId + */ + function sendWindowStateActive(windowId) + { + var message = new Uint8Array(6); + + message[0] = 0x10; + p2j.socket.writeInt32BinaryMessage(message, 1, windowId); + // 1. the iconification state + message[5] = 0; + p2j.socket.send(message); + } + + /** * Initialize module. * * @param {object} cfg configuration. @@ -1556,6 +1589,13 @@ // enable all OS events me.enableOsEvents(true); + + desktop = new VirtualDesktop( + sendWindowStateActive, + [194, 194, 194], + [128, 128, 128], + p2j.logger); + taskBar = desktop.getTaskBar(); }; /** === added file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.virtual_desktop.js' --- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.virtual_desktop.js 1970-01-01 00:00:00 +0000 +++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.virtual_desktop.js 2015-09-06 23:44:56 +0000 @@ -0,0 +1,593 @@ +/* +** Module : p2j.virtual_desktop.js +** Abstract : GUI-specific virtual desktop to create task bar and drop zones +** +** Copyright (c) 2014-2015, Golden Code Development Corporation. +** ALL RIGHTS RESERVED. Use is subject to license terms. +** +** Golden Code Development Corporation +** CONFIDENTIAL +** +** -#- -I- --Date-- ------------------------------Description---------------------------------- +** 001 SBI 20150906 The virtual desktop to create task bar and drop zones. +*/ + +"use strict"; + + + + + + +/** + * Creates the virtual desktop with dragged task bar and two drop zones: the bottom and the right + * according to this css style: + * + *.right { + * position: fixed; + * top: 0px; + * right: 0px; + * height: 100%; + * width: 40px; + *} + *.bottom { + * position: fixed; + * bottom: 0px; + * height: 40px; + * width: 100%; + *} + *.virtualDesktop { + * position: fixed; + * left: 0px; + * top: 0px; + *} + *.canvas { + * position: relative; + * width: 100%; + * height: 100%; + *} + *.dock { + *} + * + * @param {Function} taskIconOnClickCallback + * The 1-argument function to send the active window state request to the server. + * @param {Array} bg + * The background task bar color. It is represented by the 3-elements array + * filled with red, green and blue values from 0 to 255. + * @param {Array} fg + * The foreground task bar color for task icons. It is represented by the 3-elements array + * filled with red, green and blue values from 0 to 255. + * @param {Object} logger + * The p2j global application logger. + */ +function VirtualDesktop(taskIconOnClickCallback, bg, fg, logger) +{ + /** task bar id*/ + var TASK_BAR_ID = "taskBar"; + /** drop zone class name*/ + var DOCK_CLASS_NAME = "dock"; + /** task bar side */ + var TASK_BAR_WIDTH = 42; + + + /** the parent element for the virtual desktop */ + var desktop = document.createElement("div"); + + desktop.style["position"] = "fixed"; + desktop.style["top"] = "0px"; + desktop.style["left"] = "0px"; + var body = document.body; + body.appendChild(desktop); + var bottom = createBottomDropZone(DOCK_CLASS_NAME, TASK_BAR_WIDTH); + var taskBarPeer = createTaskBarPeer(TASK_BAR_ID); + bottom.appendChild(taskBarPeer); + desktop.appendChild(bottom); + desktop.appendChild(createRightDropZone(DOCK_CLASS_NAME, TASK_BAR_WIDTH)); + + var taskBar = new TaskBar(taskBarPeer, bg, fg, logger); + + /** + * Creates the bottom drop zone. + * + * @param {String} dockClassName + * The drop zone class. + * @param {Number} height + * The bottom drop zone height. + * + * @return {HtmlElement} + * The parent element for the bottom drop zone. + */ + function createBottomDropZone(dockClassName, height) + { + var bottom = document.createElement("div"); + bottom.className = dockClassName; + + bottom.style["position"] = "fixed"; + + bottom.style["bottom"] = "0px"; + + bottom.style["height"] = height + "px"; + bottom.style["width"] = "100%"; + return bottom; + }; + + /** + * Creates the right drop zone. + * + * @param {String} dockClassName + * The drop zone class. + * @param {Number} width + * The right drop zone width. + * + * @return {HtmlElement} + * The parent element for the right drop zone. + */ + function createRightDropZone(dockClassName, width) + { + var right = document.createElement("div"); + right.className = dockClassName; + right.style["position"] = "fixed"; + + right.style["right"] = "0px"; + right.style["top"] = "0px"; + + right.style["height"] = "100%"; + right.style["width"] = width + "px"; + return right; + }; + + /** + * Creates the html element for the draggable task bar. + * + * @param {String} id + * The task bar peer element id. + * + * @return {HtmlElement} + * The task bar peer element. + */ + function createTaskBarPeer(id) + { + var peer = document.createElement("div"); + peer.id = id; + peer.style["position"] = "relative"; + + peer.style["height"] = "100%"; + peer.style["width"] = "100%"; + peer.draggable = true; + return peer; + }; + + /** + * Gets the task bar. + * + * @return {TaskBar} + * The task bar. + */ + this.getTaskBar = function() + { + return taskBar; + }; + + + /** + * Creates the task bar for the given peer element with the provided background and + * foreground colors. + * + * @param {HtmlElement} peer + * The task bar peer element. + * @param {Array} bg + * The background task bar color. It is represented by the 3-elements array + * filled with red, green and blue values from 0 to 255. + * @param {Array} fg + * The foreground task bar color for task icons. It is represented by the 3-elements array + * filled with red, green and blue values from 0 to 255. + * @param {Object} logger + * The p2j global application logger. + */ + function TaskBar(peer, bg, fg, logger) + { + var DRAGGED_OPACITY = 0.3; + var TOP_PADDING = 2; + var LEFT_PADDING = 2; + var TASK_BAR_TITLE = "Task Bar"; + var TASK_ICON_WIDTH = 40; + var TASK_ICON_HEIGHT = 32; + + var SHIFT = 2; + + var dragged; + var canvas = document.createElement('canvas'); + canvas.className="canvas"; + canvas.style["height"] = "100%"; + canvas.style["width"] = "100%"; + + peer.appendChild(canvas); + var ctx = canvas.getContext('2d', {alpha : true}); +// console.log("peer width = " + peer.offsetWidth); +// console.log("peer height = " + peer.offsetHeight); + var strokesManager = new LineStrokes(); + var canvasRenderer = new CanvasRenderer(canvas, ctx, strokesManager, logger); + + var taskIcons = []; + + var foreground = fg; + var background = bg; + + if (peer) + { + /** events fired on the draggable target */ + peer.addEventListener("dragstart", startDragging); + peer.addEventListener("dragend", dragEnded); + peer.title = "Test"; + //peer.addEventListener("drag", drag); + } + + /** + * Layouts task icons according to the current task bar position: + * vertical or horizontal. + * + * @param {Number} width + * The task bar width in pixels. + * @param {Number} height + * The task bar height in pixels. + */ + function doLayout(width, height) { + canvas.width = width; + canvas.height = height; +// console.log("canvas width = " + canvas.width); +// console.log("canvas height = " + canvas.height); + + canvasRenderer.draw3DRect(ctx, 0, 0, width, height, background, true, true); + var horizontalLayout; + if (width > height) + { + horizontalLayout = true; + } + else + { + horizontalLayout = false; + } + var x = LEFT_PADDING; + var y = TOP_PADDING; + for (var i = 0; i < taskIcons.length; i++) + { + var taskIcon = taskIcons[i]; + taskIcon.setLocation(x, y); + taskIcon.draw(); + if (horizontalLayout) + { + x += (SHIFT + taskIcon.width); + } + else + { + y += (SHIFT + taskIcon.height); + } + } + } + + /** + * Draws the task bar on the canvas. + */ + this.draw = function() + { + doLayout(peer.offsetWidth, peer.offsetHeight); + } + + /** + * Adds new task to the task bar. + * + * @param {Window} win + * The window to be represented by a task in the task bar. + * + * @return {TaskIcon} + * The task icon that represents the target window. + */ + function addTaskIcon(win) + { + var taskIcon = new TaskIcon(win, null, TASK_ICON_WIDTH, TASK_ICON_HEIGHT, foreground); + taskIcons.push(taskIcon); + return taskIcon; + }; + + /** + * Sets an icon for the task that represents the target window. + * + * @param {Number} windowId + * The target window id. + * @param {Array} iconData + * It is the binary icon to set. The pixels array with 4-bytes per a pixel that + * are red, green, blue and alpha bytes. + * + * @return {TaskIcon} + * The task that represents the target window. + */ + function setIconForTask(windowId, iconData) + { + var taskIcon = findTaskIcon(windowId); + taskIcon.iconData = iconData; + return taskIcon; + }; + + this.addTaskIcon = addTaskIcon; + this.setIconForTask = setIconForTask; + + /** + * Find the window task that represents the target window. + * + * @param {Number} windowId + * The target window id. + * + * @return {TaskIcon} + * The task that represents the target window. + */ + function findTaskIcon(windowId) + { + var taskIcon; + for (var i = 0; i < taskIcons.length; i++) + { + var taskIcon = taskIcons[i]; + if (taskIcon.win.id === windowId) + { + break; + } + } + return taskIcon; + }; + + /** + * Collapses the given window. Sets the corresponding task icon to be inactive. + * + * @param {Number} windowId + * The target window id. + */ + this.iconify = function(windowId) + { + var taskIcon = findTaskIcon(windowId); + if (taskIcon === undefined) + { + return; + } + taskIcon.setActive(false); + taskIcon.draw(); + }; + + /** + * Restores the window. Sets the corresponding task icon to be active. + * + * @param {Number} windowId + * The target window id. + */ + this.deiconify = function(windowId) + { + var taskIcon = findTaskIcon(windowId); + if (taskIcon === undefined) + { + return; + } + taskIcon.setActive(true); + taskIcon.draw(); + }; + + function startDragging(event) { + event.dataTransfer.setData('text/plain',null); + dragged = event.target; + event.target.style.opacity = DRAGGED_OPACITY; + } + + function dragEnded(event) { + event.target.style.opacity = 1; + } + + function drag( event ) { + } + + /* events fired on the drop targets */ + document.addEventListener("dragover", + function( event ) + { + event.preventDefault(); + }, false); + + document.addEventListener("dragenter", + function( event ) + { + var cls = event.target.className; + if ( cls.indexOf(DOCK_CLASS_NAME) >= 0) { + event.target.style["background"] = getTransparentColor(background, 0.5);; + } + }, false); + + document.addEventListener("dragleave", + function( event ) + { + var cls = event.target.className; + if ( cls.indexOf(DOCK_CLASS_NAME) >= 0 ) { + event.target.style["background"] = getTransparentColor(background, 0.0);; + } + }, false); + + document.addEventListener("drop", + function( event ) + { + var cls = event.target.className; + if ( cls.indexOf(DOCK_CLASS_NAME) >= 0) { + var parent = dragged.parentNode; + parent.removeChild(dragged); + try { + event.target.appendChild(dragged); + parent.style["background"] = getTransparentColor(background, 0.0); + } + catch(e) + { + parent.addChild(dragged); + } + doLayout(peer.offsetWidth, peer.offsetHeight); + } + event.preventDefault(); + }, false); + + /** + * Returns rgba(r,g,b,a) string representation for the target color + * with the target transparency. + * + * @param {Number[]} c + * Array of 3 integer values between 0 and 255 inclusive, representing an RGB color. + * @param {Number} transparency + * The transparency from 0.0 to 1.0. + * + * @return {String} + * The 'rgba(r,g,b,a)' string representation. + */ + function getTransparentColor(c, transparency) + { + var parameters = []; + Array.prototype.push.apply(parameters, c); + parameters.push(transparency); + return "rgba(" + parameters.join() + ")"; + } + + /** + * Creates the task icon widget that represents the target window on the task bar. + * + * @param {Window} win + * The window to be represented by a task in the task bar. + * @param {Array} iconData + * It is the binary icon to set. The pixels array with 4-bytes per a pixel that + * are red, green, blue and alpha bytes. + * @param {Number} w + * The task icon widget width in pixels. + * @param {Number} h + * The task icon widget height in pixels. + * @param {Number[]} c + * The task icon widget color. Array of 3 integer values between 0 + * and 255 inclusive, representing an RGB color. + */ + function TaskIcon(win, iconData, w, h, c) + { + this.win = win; + this.iconData = iconData; + this.width = w; + this.height = h; + this.color = c; + var that = this; + var active = false; + this.x = 0; + this.y = 0; + + this.setLocation = setLocation; + this.setActive = setActive; + this.isActive = isActive; + this.draw = draw; + + /** + * Sets the widget location. + */ + function setLocation(x, y) + { + this.x = x; + this.y = y; + }; + + /** + * Sets an active state. + */ + function setActive(value) + { + active = value; + }; + + /** + * Is active? + */ + function isActive() + { + return active; + }; + + /** + * Draw the widget on the task bar canvas. + */ + function draw() + { + var c; + if (active) + { + c = canvasRenderer.lightenColor(that.color); + } + else + { + c = that.color; + } + canvasRenderer.draw3DRect(ctx, that.x, that.y, that.width, that.height, c, true, !active); + if (that.iconData) + { + var iconWidth = that.win.iconWidth; + var iconHeight = that.win.iconHeight; + canvasRenderer.drawImage(ctx, that.x, that.y + (that.height - iconHeight) / 2, + iconWidth, iconHeight, that.iconData, 0); + } + }; + + /** + * Tests the mouse pointer inside the widgets bounds. + */ + function testMousePointerInside(event) + { + var rect = peer.getBoundingClientRect(); + var x_rel = event.clientX - rect.left; + var y_rel = event.clientY - rect.top; + return isInOrder(that.x, x_rel, that.x + that.width) + && isInOrder(that.y, y_rel, that.y + that.height); + }; + + /** + * Returns true iff a <= b <= c + */ + function isInOrder(a, b, c) + { + if (a > b || b > c) + { + return false; + } + return true; + }; + + /** + * Adds the "on click" widget action. If window is inactive, sets the active state and + * sends the request to server in order to change the window state. + */ + peer.addEventListener("click", function(event) { + if (testMousePointerInside(event) && !that.isActive()) + { + that.setActive(true); + if (taskIconOnClickCallback) + { + taskIconOnClickCallback(that.win.id); + } + that.draw(); + } + }); + peer.addEventListener("mouseenter", function(event) { + if (testMousePointerInside(event)) + { + peer.title = (that.win.title !== null && that.win.title !== udefined) + ? that.win.title : " "; + } + }); +// peer.addEventListener("mouseover", function(event) { +// if (testMousePointerInside(event)) +// { +// peer.title = that.win.title; +// } +// }); + peer.addEventListener("mouseleave", function(event) { + if (testMousePointerInside(event)) + { + peer.title = TASK_BAR_TITLE; + } + }); + } + } +} + +