From c81d3f57e8ee3bee73e05d5fbdef4de949dbac75 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 2 Jan 2014 10:54:33 +0000 Subject: [PATCH] * add top bar buttons and handle maximize + close, minimize not handled yet (no place to go!), window icon needs rgb code * show window title * handle metadata updates * add missing "use strict" in functions * whitespace cleanup git-svn-id: https://xpra.org/svn/Xpra/trunk@5088 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/html5/include/protocol.js | 8 +- src/html5/include/window.js | 153 ++++++++++++++++++++++++++++++++-- src/html5/index.html | 56 +++++++++---- 3 files changed, 189 insertions(+), 28 deletions(-) diff --git a/src/html5/include/protocol.js b/src/html5/include/protocol.js index e6d29d5643..94e188730d 100644 --- a/src/html5/include/protocol.js +++ b/src/html5/include/protocol.js @@ -25,7 +25,7 @@ var api = {}, // Public API packet_handlers = {}, log_packets = true, no_log_packet_types = ["ping_echo", "key-action", "damage-sequence", - "map-window", "configure-window", + "map-window", "configure-window", "close-window", "desktop_size", "hello", "pointer-position", "button-action", "focus"]; @@ -75,15 +75,15 @@ function on_message(m) { //hook websock events using packet handlers: function on_open(m) { - debug("on_open("+m+")"); + //debug("on_open("+m+")"); process_packet(["open", m]); } function on_close(m) { - show("on_close("+m+")"); + //show("on_close("+m+")"); process_packet(["close", m]); } function on_error(m) { - show("on_error("+m+")"); + //show("on_error("+m+")"); process_packet(["error", m]); } diff --git a/src/html5/include/window.js b/src/html5/include/window.js index 8f8cf7f56f..228527b95d 100644 --- a/src/html5/include/window.js +++ b/src/html5/include/window.js @@ -7,6 +7,50 @@ * Based on shape.js */ +/** + * A simple button we use to decorate the window. + */ +function Button(canvas_state, name, x, y, w, h, fill, click_callback) { + "use strict"; + this.state = canvas_state; + this.name = name; + this.x = x || 0; + this.y = y || 0; + this.w = w || 1; + this.h = h || 1; + this.fill = fill || '#AAAAAA'; + this.click_callback = click_callback; +} +Button.prototype.draw_at = function(ctx, x, y) { + "use strict"; + ctx.fillStyle = this.fill; + ctx.fillRect(x+this.x, y+this.y, this.w, this.h); +}; +function rectangle_contains(rect, mx, my) { + "use strict"; + // All we have to do is make sure the Mouse X,Y fall in the area between + // the shape's X and (X + Height) and its Y and (Y + Height) + return (rect.x <= mx) && (rect.x + rect.w >= mx) && + (rect.y <= my) && (rect.y + rect.h >= my); +}; +Button.prototype.get_geometry = function() { + "use strict"; + return { x : this.x, y : this.y, w : this.w, h : this.h }; +}; +Button.prototype.contains = function(mx, my) { + "use strict"; + return rectangle_contains(this.get_geometry(), mx, my); +}; +/** + * toString allows us to identify buttons: + */ +Button.prototype.toString = function() { + "use strict"; + return "Button("+this.name+")"; +}; + + + /** * This is the class representing a window we draw on the canvas. * It has a geometry, it may have borders and a top bar. @@ -14,7 +58,7 @@ * when we receive pixels from the server. */ function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties, - geometry_cb, mouse_move_cb, mouse_click_cb) { + geometry_cb, mouse_move_cb, mouse_click_cb, window_closed) { "use strict"; //keep reference to the canvas: this.state = canvas_state; @@ -22,10 +66,11 @@ function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, this.geometry_cb = null; this.mouse_move_cb = null; this.mouse_click_cb = null; + this.window_closed_cb = null; //styling: this.borderColor = '#101028'; - this.topBarColor = '#A8A8B0'; + this.topBarColor = '#B8B8C0'; //the window "backing": this.image = null; @@ -50,6 +95,8 @@ function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, this.w = w; this.h = h; + this.buttons = []; + this.update_metadata(metadata); // the space taken by window decorations: @@ -70,16 +117,47 @@ function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, this.geometry_cb = geometry_cb || null; this.mouse_move_cb = mouse_move_cb || null; this.mouse_click_cb = mouse_click_cb || null; + this.window_closed_cb = window_closed || null; + //create the buttons: + if (!this.override_redirect) { + this.create_buttons(); + } //create the image holding the pixels (the "backing"): this.create_image_backing(); canvas_state.addShape(this); }; +/** + * Creates the minimize, maximize and close buttons. + */ +XpraWindow.prototype.create_buttons = function() { + "use strict"; + var w = 24; + var h = 24; + var self = this; + this.buttons.push(new Button(this.state, "icon", this.borderWidth, this.borderWidth, w, h, "yellow", null)); + this.buttons.push(new Button(this.state, "minimize", this.w-(w+2)*3, this.borderWidth, w, h, "red", function() { + //TODO! + })); + this.buttons.push(new Button(this.state, "maximize", this.w-(w+2)*2, this.borderWidth, w, h, "green", function() { + var m = !self.maximized; + self.client_properties["maximized"] = m; + self.set_maximized(m); + })); + this.buttons.push(new Button(this.state, "close", this.w-(w+2)*1, this.borderWidth, w, h, "blue", function() { + if (self.window_closed_cb) { + self.window_closed_cb(self); + } + })); +}; + + /** * toString allows us to identify windows by their unique window id. */ XpraWindow.prototype.toString = function() { + "use strict"; return "Window("+this.wid+")"; }; @@ -87,6 +165,7 @@ XpraWindow.prototype.toString = function() { * Allocates the image object containing the window's pixels. */ XpraWindow.prototype.create_image_backing = function() { + "use strict"; var previous_image = this.image; var img_geom = this.get_internal_geometry(); //show("createImageData: "+img_geom.toSource()); @@ -103,6 +182,7 @@ XpraWindow.prototype.create_image_backing = function() { * to the contents of the window. */ XpraWindow.prototype.calculate_offsets = function() { + "use strict"; if (this.override_redirect || this.fullscreen) { //no borders or top bar at all: this.borderWidth = 0; @@ -111,7 +191,7 @@ XpraWindow.prototype.calculate_offsets = function() { else { //regular borders and top bar: this.borderWidth = 2; - this.topBarHeight = 20; + this.topBarHeight = 24; } this.offsets = [this.borderWidth+this.topBarHeight, this.borderWidth, this.borderWidth, this.borderWidth]; }; @@ -121,6 +201,7 @@ XpraWindow.prototype.calculate_offsets = function() { * then call set_metadata with these new key-values. */ XpraWindow.prototype.update_metadata = function(metadata) { + "use strict"; //update our metadata cache with new key-values: for (var attrname in metadata) { this.metadata[attrname] = metadata[attrname]; @@ -132,6 +213,7 @@ XpraWindow.prototype.update_metadata = function(metadata) { * Apply new metadata settings. */ XpraWindow.prototype.set_metadata = function(metadata) { + "use strict"; if ("fullscreen" in metadata) { this.set_fullscreen(metadata["fullscreen"]==1); } @@ -140,6 +222,8 @@ XpraWindow.prototype.set_metadata = function(metadata) { } if ("title" in metadata) { this.title = metadata["title"]; + //redraw everything (a bit wasteful): + this.state.invalidate(); } }; @@ -148,6 +232,7 @@ XpraWindow.prototype.set_metadata = function(metadata) { * (ie: when un-maximizing or un-fullscreening) */ XpraWindow.prototype.save_geometry = function() { + "use strict"; if (this.x==undefined || this.y==undefined) return; this.saved_geometry = { @@ -162,6 +247,7 @@ XpraWindow.prototype.save_geometry = function() { * Restores the saved geometry (if it exists). */ XpraWindow.prototype.restore_geometry = function() { + "use strict"; if (this.saved_geometry==null) { return; } @@ -177,6 +263,7 @@ XpraWindow.prototype.restore_geometry = function() { * Maximize / unmaximizes the window. */ XpraWindow.prototype.set_maximized = function(maximized) { + "use strict"; //show("set_maximized("+maximized+")"); if (this.maximized==maximized) { return; @@ -190,6 +277,7 @@ XpraWindow.prototype.set_maximized = function(maximized) { * Fullscreen / unfullscreen the window. */ XpraWindow.prototype.set_fullscreen = function(fullscreen) { + "use strict"; //show("set_fullscreen("+fullscreen+")"); if (this.fullscreen==fullscreen) { return; @@ -206,6 +294,7 @@ XpraWindow.prototype.set_fullscreen = function(fullscreen) { * - or restore the geometry */ XpraWindow.prototype.max_save_restore = function(use_all_space) { + "use strict"; if (use_all_space) { this.save_geometry(); this.fill_canvas(); @@ -219,6 +308,7 @@ XpraWindow.prototype.max_save_restore = function(use_all_space) { * Use up all the canvas space */ XpraWindow.prototype.fill_canvas = function() { + "use strict"; this.x = 0; this.y = 0; this.w = this.state.width; @@ -232,6 +322,7 @@ XpraWindow.prototype.fill_canvas = function() { * - fire the geometry_cb */ XpraWindow.prototype.handle_resize = function() { + "use strict"; this.create_image_backing(); this.state.invalidate(); if (this.geometry_cb!=null) { @@ -244,6 +335,7 @@ XpraWindow.prototype.handle_resize = function() { * if it is fullscreen or maximized. */ XpraWindow.prototype.canvas_resized = function() { + "use strict"; if (this.fullscreen || this.maximized) { this.fill_canvas(); this.handle_resize(); @@ -279,7 +371,23 @@ XpraWindow.prototype.handle_mouse_click = function(button, pressed, mx, my, modi var igeom = this.get_internal_geometry(); if (this.mouse_click_cb!=null && rectangle_contains(igeom, mx, my)) { this.mouse_click_cb(this, button, pressed, mx, my, modifiers, buttons); + return; } + //maybe one of the buttons: + //(use relative coordinates) + var x = mx-this.x; + var y = my-this.y + for (var i in this.buttons) { + var button = this.buttons[i]; + if (button.contains(x, y)) { + show("clicked on button "+button.name); + var cb = button.click_callback; + if (cb) { + cb(); + } + return; + } + } }; /** @@ -294,6 +402,12 @@ XpraWindow.prototype.handle_mouse_move = function(mx, my, modifiers, buttons) { } }; + +XpraWindow.prototype.update_icon = function(w, h, pixel_format, data) { + "use strict"; + //TODO! +} + /** * Draws this window to the given context: * - draw the window frame (if not an OR window and not fullscreen) @@ -332,7 +446,20 @@ XpraWindow.prototype.draw_frame = function(ctx) { // draw top bar: ctx.fillStyle = this.topBarColor; - ctx.fillRect(this.x+this.borderWidth, this.y+this.borderWidth, this.w-this.borderWidth*2, this.topBarHeight-this.borderWidth); + ctx.fillRect(this.x+this.borderWidth, this.y+this.borderWidth, this.w-this.borderWidth*2, this.topBarHeight); + // draw title: + if (this.title) { + var size = 18; + ctx.font = ""+size+"px sans-serif"; + ctx.fillStyle = "#FFFFFF"; + ctx.fillText(this.title, this.x+32, this.y+this.borderWidth+this.topBarHeight-size/3); + } + + // draw buttons: + for (var i=0; i=(this.h-this.offsets[2]) || - x<=this.offsets[3] || x>=(this.w-this.offsets[1])) + if (!(y<=this.offsets[0] || my>=(this.h-this.offsets[2]) || + x<=this.offsets[3] || x>=(this.w-this.offsets[1]))) { + return false; + } + + // check that this isn't one of the buttons: + for (var i in this.buttons) { + var button = this.buttons[i]; + if (button.contains(x, y)) { + return false; + } + } + return true; }; diff --git a/src/html5/index.html b/src/html5/index.html index 286842d3f4..647e12f2e6 100644 --- a/src/html5/index.html +++ b/src/html5/index.html @@ -181,10 +181,11 @@ } function set_ui_message(msg, color) { + "use strict"; var m = document.getElementById('message'); m.style.color = color || "black"; m.innerHTML = msg; - show("set_ui_message("+msg+", "+color+")"); + //show("set_ui_message("+msg+", "+color+")"); } function process_startup_complete(packet) { @@ -234,7 +235,7 @@ } } } - show("alt="+alt_modifier+", meta="+meta_modifier); + //show("alt="+alt_modifier+", meta="+meta_modifier); } function process_disconnect(packet) { "use strict"; @@ -252,6 +253,14 @@ //TODO! } + //These are callbacks from XpraWindow to us + //to notify us of window clicks and events: + + function window_closed(win) { + "use strict"; + var wid = window_to_id[win]; + send(["close-window", wid]); + } function window_geometry_changed(win) { "use strict"; //show("window_geometry_changed("+win+") geometry="+win.get_window_geometry().toSource()); @@ -259,7 +268,7 @@ var wid = window_to_id[win]; if (!win.override_redirect) self.set_focus(wid); - send(["configure-window", wid, geom.x, geom.y, geom.w, geom.h, get_client_properties()]); + send(["configure-window", wid, geom.x, geom.y, geom.w, geom.h, get_client_properties(win)]); }; function mouse_move(win, x, y, modifiers, buttons) { "use strict"; @@ -299,19 +308,21 @@ function get_client_properties(win) { "use strict"; - return {"encodings.rgb_formats" : RGB_FORMATS}; + var cp = win.client_properties; + cp["encodings.rgb_formats"] = RGB_FORMATS; + return cp; }; function make_new_window(wid, x, y, w, h, metadata, override_redirect, client_properties) { "use strict"; var win = new XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties, - window_geometry_changed, mouse_move, mouse_click); + window_geometry_changed, mouse_move, mouse_click, window_closed); //show("make_new_window("+wid+", "+x+", "+y+", "+w+", "+h+", "+metadata+", "+override_redirect+", "+client_properties+")="+win); id_to_window[wid] = win; window_to_id[win] = wid; var geom = win.get_internal_geometry(); if (!override_redirect) { - send(["map-window", wid, geom.x, geom.y, geom.w, geom.h, get_client_properties()]); + send(["map-window", wid, geom.x, geom.y, geom.w, geom.h, get_client_properties(win)]); set_focus(wid); } } @@ -347,6 +358,24 @@ process_new_common(packet, true); } + function process_window_metadata(packet) { + "use strict"; + var wid = packet[1], + metadata = packet[2], + win = id_to_window[wid]; + win.update_metadata(metadata); + } + function process_window_icon(packet) { + "use strict"; + var wid = packet[1], + w = packet[2], + h = packet[3], + pixel_format = packet[4], + data = packet[5], + win = id_to_window[wid]; + if (win) + win.update_icon(w, h, pixel_format, data); + } function send_damage_sequence(wid, packet_sequence, width, height, decode_time) { "use strict"; @@ -403,12 +432,6 @@ function process_window_resized(packet) { //TODO } - function process_window_icon(packet) { - //TODO - } - function process_window_metadata(packet) { - //TODO - } function process_new_tray(packet) { //TODO } @@ -556,8 +579,7 @@ protocol.send(packet); } catch(e) { - show("error sending packet: "+e); - this.stop(); + error("error sending packet: "+e); } } } @@ -731,7 +753,7 @@ } } - + /** * Get params in and out of the form or URL: */ @@ -777,7 +799,7 @@ if (params["host"]!="" && params["port"]>0) do_start(params); } - + function new_popup() { "use strict"; var params = get_params_from_form(); @@ -862,7 +884,7 @@
-
+