//This file pertains to the textwindow. //We use this for displaying a puzzle's starting instructions //and for choosing new puzzles. var tmOutsideYMargin=20; //How far from the top and bottom left blank var tmOutsideXMargin=20; //How far from right and left side left blank var tmBorderWidth=4; var tmMenuBarHight=20; var tmScrollBarWidth=20; var tmWindowBackground="grey"; var tmTextHeight; //Calculated when we build the page var tmLastHighlightedIndex=-1; var tmTextYGap=5; //The gap between lines var tmPuzzleSelectMenuLevel=0; var cachedTextMenuCanvas = null; //Print the textmenu function textMenuPrint(TextToPrint, selectedindex = -1, highlightedindex = -1) { //It would be nice to print it on top of whatever is currently there. //But we will do that later. PrintScreen(0); //Print the network, then we can lay over it if(cachedTextMenuCanvas == null) { cachedTextMenuCanvas = document.createElement('canvas'); cachedTextMenuCanvas.width = MainCanvas.width - (tmOutsideXMargin * 2); cachedTextMenuCanvas.height = MainCanvas.height - (tmOutsideYMargin * 2); } //If we get here, it is already created. Get the context var cTMCctx = cachedTextMenuCanvas.getContext('2d'); var rect; //Fill in the background cTMCctx.fillStyle = tmWindowBackground; cTMCctx.fillRect(0,0, cachedTextMenuCanvas.width, cachedTextMenuCanvas.height); //Put the X there so we can click on it rect = makeRectangle(cachedTextMenuCanvas.width - tmScrollBarWidth, 0, tmScrollBarWidth, tmMenuBarHight, tmOutsideXMargin, tmOutsideYMargin); cTMCctx.drawImage(imageFromName("x"), rect.sx, rect.sy, rect.deltax, rect.deltay); registerActionStruct("square", rect, null, textwindow_XClick); //Put the DownArrow there so we can click on it cTMCctx.drawImage(imageFromName("ArrowUp"),cachedTextMenuCanvas.width - tmScrollBarWidth,tmMenuBarHight,tmScrollBarWidth,tmMenuBarHight); //Put the X there so we can click on it cTMCctx.drawImage(imageFromName("ArrowDown"),cachedTextMenuCanvas.width - tmScrollBarWidth,cachedTextMenuCanvas.height - tmMenuBarHight,tmScrollBarWidth,tmMenuBarHight); //Create and Draw the menu bar cTMCctx.beginPath(); cTMCctx.moveTo(cachedTextMenuCanvas.width - tmScrollBarWidth,0); cTMCctx.lineTo(cachedTextMenuCanvas.width - tmScrollBarWidth,cachedTextMenuCanvas.height); cTMCctx.strokeStyle="white"; cTMCctx.stroke(); //horizontal line under the X cTMCctx.beginPath(); cTMCctx.moveTo(cachedTextMenuCanvas.width - tmScrollBarWidth,tmMenuBarHight); cTMCctx.lineTo(cachedTextMenuCanvas.width,tmMenuBarHight); cTMCctx.strokeStyle="white"; cTMCctx.stroke(); var cachedTextMenuTextCanvas = document.createElement('canvas'); //Figure out how much space we need. - start with a simple canvas (non expandable) cachedTextMenuTextCanvas.width = cachedTextMenuCanvas.width - tmScrollBarWidth; cachedTextMenuTextCanvas.height = cachedTextMenuCanvas.height; var cTMTCctx = cachedTextMenuTextCanvas.getContext('2d'); cTMTCctx.strokeStyle="black"; cTMTCctx.font = "20px serif"; var lines = []; if(typeof(TextToPrint) === "string") lines = fragmentTextIntoLines(cTMTCctx, TextToPrint, cachedTextMenuTextCanvas.width - 10); else lines = TextToPrint; //If we passed in a list of strings //Now we have the number of lines. var metrics = cTMTCctx.measureText("test"); var yHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent + tmTextYGap; //the hight of the default font and gap tmTextHeight = yHeight; //store it for use in highlighting //console.log("Height = "+yHeight); var totalHeight = (lines.length * yHeight) + tmTextYGap; if(cachedTextMenuTextCanvas.height < totalHeight) cachedTextMenuTextCanvas.height = totalHeight;// resize if needed //Highlight text if (highlightedindex >= 0) { //console.log("Showing hilighted index " + highlightedindex); //Fill in the area highlighted cTMTCctx.fillStyle = "white"; cTMTCctx.globalAlpha = 0.3; //mostly transparent cTMTCctx.fillRect(0,highlightedindex * yHeight + (yHeight/3), cachedTextMenuCanvas.width, yHeight); cTMTCctx.globalAlpha = 1.0; //reset } //Chosen text if (selectedindex >= 0) { //console.log("Showing selected index " + selectedindex + " " + yHeight); //Fill in the area highlighted cTMTCctx.fillStyle = "green"; cTMTCctx.globalAlpha = 0.4; //mostly transparent cTMTCctx.fillRect(0,selectedindex * yHeight + (yHeight/3), cachedTextMenuCanvas.width, yHeight); cTMTCctx.globalAlpha = 1.0; //reset } cTMTCctx.fillStyle = "black"; cTMTCctx.strokeStyle="black"; //Now, print text on the canvas. for (var i = 0; i < lines.length; i++) { cTMTCctx.fillText(lines[i], 5, ((i+1) * yHeight)); //console.log("printing text part: " + lines[i]); } //Write the text canvas on the main canvas. If we are scrolled up or down, do that. cTMCctx.drawImage(cachedTextMenuTextCanvas,0,0); //create and Draw the scroll-bar on the side //Then make the text if we have not done so //Finally print on top of the main canvas MainCanvas_ctx.globalAlpha = 0.9; //some transparancy MainCanvas_ctx.drawImage(cachedTextMenuCanvas,tmOutsideXMargin, tmOutsideYMargin) MainCanvas_ctx.globalAlpha = 1; //reset transparancy } function fragmentTextIntoLines(ctx, text, maxWidth) { var words = text.split(" "); var lines = []; var currentLine = words[0]; for (var i = 1; i < words.length; i++) { var word = words[i]; var width = ctx.measureText(currentLine + " " + word).width; if (width < maxWidth) { currentLine += " " + word; } else { lines.push(currentLine); currentLine = word; } } lines.push(currentLine); return lines; } function TextWindow_handleMouseUp(evt) { console.log("TextWindow Mouse Up"); //If we get here, it is a mouse-click event. See if we are on the X if(mouseDownLocation.pageX + tmScrollBarWidth >= cachedTextMenuCanvas.width - tmScrollBarWidth) { console.log("TextWindow Mouse Up - X fits"); //The X fits. Now, see which button, or scroll-bar we clicked on. if(mouseDownLocation.pageY - tmOutsideYMargin <= tmMenuBarHight && mouseDownLocation.pageY - tmOutsideYMargin >= 0) { console.log("TextWindow Mouse Up - Y fits"); //We clicked the X textwindow_XClick(); } } else { if(uiMode == 2) { //We are in puzzle-select mode and clicked somewhere. var levellist=networkNamesMatchingText("Level"+tmPuzzleSelectMenuLevel); if(tmLastHighlightedIndex>=0 && tmLastHighlightedIndex< levellist.length) { //We found a puzzle console.log("Clicked on puzzle: " + levellist[tmLastHighlightedIndex]); uiMode=1; switchPuzzle(levellist[tmLastHighlightedIndex]); } } } mouseDidMovement=false; //reset it after we raise the button } function textwindow_XClick(point, object) { //When the x is clicked, we do not care about the position or object. There is no object //Dispose of the text window uiMode = 0; //dispose of temp canvas; will recreate later if needed cachedTextMenuCanvas = null; cachedTextMenuTextCanvas = null; //Redraw the screen PrintScreen(); } function PrintPuzzleSelectMenu(level=0) { var levellist=networkNamesMatchingText("Level"+level); //console.log("list is this long: " + levellist.length); //textMenuPrint(levellist, -1, tmLastHighlightedIndex); //tmPuzzleSelectMenuLevel=level; // var local_menuitems = []; for (var i = 1; i < levellist.length; i++) { var item = new menuitem(null, levellist[i], levellist[i], null, null, textMenu_PuzzleClick) local_menuitems.push(item); } var local_menu = new menuclass(null); local_menu.items = local_menuitems; menuPrint(local_menu); } function textMenu_PuzzleClick(text, name, source, dest) { //If we get here, someone clicked on a puzzle name. Select it and move on //We are in puzzle-select mode and clicked somewhere. //if (text == null) { } //!== does not work properly //else { // console.log("Clicked on puzzle: " + text); // uiMode = 1; // switchPuzzle(text); //} } function textMenu_HandleMouseMove(evt) { //var highlighted_index = Math.floor(((evt.pageY - tmOutsideYMargin) - (tmTextHeight/3)) / tmTextHeight); //if(tmLastHighlightedIndex != highlighted_index) //{ // //the index has changed // console.log("index = " + highlighted_index); // tmLastHighlightedIndex = highlighted_index; // PrintPuzzleSelectMenu(tmPuzzleSelectMenuLevel); //} } class menuitem { constructor(rectangle, shownText, payloadName = null, payloadSource = null, payloadDest = null, clickFunc=null) { this.rectangle = rectangle this.shownText = shownText this.payloadName = payloadName //The command, such as 'delete', 'power-on', 'ping', etc this.payloadSource = payloadSource //The link, device, or original item. this.payloadDest = payloadDest //The destination device, etc. Often null unless we are pinging or tracert this.clickFunc = clickFunc } } //Our menuclass will have a menu. We need to be able to have cascading menus (one on top of the other) //so we can return to a past menu if we have moved on to a child menu. class menuclass { constructor(sourceItem) { this.SourceItem = sourceItem this.hmargin = 10 //horizontal margin on both left and right this.vmargin = 10 //virtical margin on both top and bottom this.backcolor = "grey" //The background color this.font = "20px serif" //font and size this.textcolor = "black" //Font color this.items = [] //An array of menuitems this.TextYGap = 3; //The gap to add to the size } } function menuPrint(menu, menutop = 0, highlightedindex = -1) { //the menu is the menuclass that holds everything //menutop is the top=most item. If we scroll up and down, this changes. //highlighted index is the item that has been moused-over. When this changes, re-draw the menu PrintScreen(0); //Print the network, then we can lay over it //This menu covers the entire place. cachedTextMenuCanvas = document.createElement('canvas'); cachedTextMenuCanvas.width = MainCanvas.width - (tmOutsideXMargin * 2); cachedTextMenuCanvas.height = MainCanvas.height - (tmOutsideYMargin * 2); //Get the context var cTMCctx = cachedTextMenuCanvas.getContext('2d'); var tw_rect; var tw_width=0; //the width of the menu alltogether var tw_height=0; //the height of the menu alltogether var tw_itemHeight=0; //the height of both the font and margin spacing var tw_rect; //the rectangle we end up using //We need to figure out the size of the menu. //Do we have an item to print? If so, add that at top if (menu.SourceItem == null) { //!== null does not work right } else { tw_height = imageSize + 10; } var tw_startY = tw_height; //Calculate the size of each text line //Add spacing. If we are larger than the alloted space, add arrows at top and bottom //Then we can fill in the area based on all of this. cTMCctx.strokeStyle = menu.textcolor; cTMCctx.font = menu.font; var metrics = cTMCctx.measureText("test"); var yHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent + menu.TextYGap; //the hight of the default font and gap var tw_TextHeight = yHeight; //store it for use var maxWidth = 0; //determine max width (width of longest text line) for (var index = 0; index < menu.items.length; index++) { var tmetrics = cTMCctx.measureText(menu.items[index].shownText); var xWidth = tmetrics.width; if (xWidth > maxWidth) maxWidth = xWidth; } maxWidth = maxWidth + 20 + menu.hmargin; if (maxWidth > cachedTextMenuCanvas.width) maxWidth = cachedTextMenuCanvas.width; tw_width = maxWidth; //determine height (if we have more items than fit, figure out what will fit) tw_height += menu.items.length * yHeight; if(tw_height > cachedTextMenuCanvas.height) tw_height = cachedTextMenuCanvas.height; //Now that we have the width and height, center it var x = (cachedTextMenuCanvas.width - tw_width) / 2; var y = (cachedTextMenuCanvas.height - tw_height) / 2; //make a rectangle of the correct size //Use that rectangle to place the closing X at the top tw_rect = makeRectangle(x, y, tw_width, tw_height); //fill in the whole thing with white and opaque drawshape("square", makeRectangle(0, 0, cachedTextMenuCanvas.width, cachedTextMenuCanvas.height), "white", .5, cTMCctx); //Fill in the background, nearly fully but showing a very little behind drawshape("square", tw_rect, menu.backcolor, .9, cTMCctx); //Put the X there so we can click on it rect = makeRectangle(cachedTextMenuCanvas.width - tmScrollBarWidth, y, tmScrollBarWidth, tmMenuBarHight, tmOutsideXMargin, tmOutsideYMargin); cTMCctx.drawImage(imageFromName("x"), rect.sx, rect.sy, rect.deltax, rect.deltay); registerActionStruct("square", rect, null, textwindow_XClick); //Put the UpArrow there so we can click on it cTMCctx.drawImage(imageFromName("ArrowUp"), cachedTextMenuCanvas.width - tmScrollBarWidth, tmMenuBarHight, tmScrollBarWidth, tmMenuBarHight); //Put the DownArrow there so we can click on it cTMCctx.drawImage(imageFromName("ArrowDown"), cachedTextMenuCanvas.width - tmScrollBarWidth, cachedTextMenuCanvas.height - tmMenuBarHight, tmScrollBarWidth, tmMenuBarHight); //Create and Draw the menu bar cTMCctx.beginPath(); cTMCctx.moveTo(cachedTextMenuCanvas.width - tmScrollBarWidth, 0); cTMCctx.lineTo(cachedTextMenuCanvas.width - tmScrollBarWidth, cachedTextMenuCanvas.height); cTMCctx.strokeStyle = "white"; cTMCctx.stroke(); //horizontal line under the X cTMCctx.beginPath(); cTMCctx.moveTo(cachedTextMenuCanvas.width - tmScrollBarWidth, tmMenuBarHight); cTMCctx.lineTo(cachedTextMenuCanvas.width, tmMenuBarHight); cTMCctx.strokeStyle = "white"; cTMCctx.stroke(); var oldfill = cTMCctx.fillStyle; var oldstroke = cTMCctx.strokeStyle; cTMCctx.font = menu.font; cTMCctx.fillStyle = menu.textcolor; cTMCctx.strokeStyle = menu.textcolor; for (var index = menutop; index < menu.items.length; index++) { var ty = tw_startY + (tw_TextHeight * (index - menutop)); //x is already defined cTMCctx.fillText(menu.items[index].shownText, x, ty); metrics = cTMCctx.measureText(menu.items[index].shownText); var trect = makeRectangle(x + 20, ty, metrics.width, tw_TextHeight); menu.items[index].rectangle = trect; registerActionStruct("square", trect, menu.items[index], puzzle_clickOn, null, generic_mouseoverHighlight); } cTMCctx.fillStyle = oldfill; cTMCctx.strokeStyle = oldstroke; MainCanvas_ctx.globalAlpha = 0.9; //some transparancy MainCanvas_ctx.drawImage(cachedTextMenuCanvas, tmOutsideXMargin, tmOutsideYMargin) MainCanvas_ctx.globalAlpha = 1; //reset transparancy } function puzzle_clickOn(point, actionrec) { console.log("clicking on puzzle:" + actionrec.theObject.shownText); uiMode = 1; switchPuzzle(actionrec.theObject.shownText); }