From 43244b53b3cfeeaef8049bf4de69cbf3f25e44e3 Mon Sep 17 00:00:00 2001 From: Brenden Tuck Date: Thu, 21 Mar 2019 08:44:46 -0400 Subject: [PATCH] Fix the popup close bugs and expand functionality to allow dual inputs --- .../static/webclient/css/goldenlayout.css | 15 +- .../static/webclient/js/plugins/default_in.js | 48 +++-- .../webclient/js/plugins/goldenlayout.js | 201 ++++++++++++------ .../static/webclient/js/plugins/history.js | 139 +++++------- .../static/webclient/js/webclient_gui.js | 18 ++ 5 files changed, 256 insertions(+), 165 deletions(-) diff --git a/evennia/web/webclient/static/webclient/css/goldenlayout.css b/evennia/web/webclient/static/webclient/css/goldenlayout.css index 54b5663e8a..bbb099e712 100644 --- a/evennia/web/webclient/static/webclient/css/goldenlayout.css +++ b/evennia/web/webclient/static/webclient/css/goldenlayout.css @@ -88,15 +88,26 @@ label { height: 100%; } -#inputsend { +.inputsend { position: absolute; right: 0; z-index: 1; + height: inherit; + width: 30px; } -#inputfield { +.inputfield { position: absolute; top: 0; + height: inherit; + width: calc(100% - 30px); + background-color: black; + color: white; +} + +.inputfield:focus { + background-color: black; + color: white; } .glbutton { diff --git a/evennia/web/webclient/static/webclient/js/plugins/default_in.js b/evennia/web/webclient/static/webclient/js/plugins/default_in.js index d6779c8cfb..115629a820 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/default_in.js +++ b/evennia/web/webclient/static/webclient/js/plugins/default_in.js @@ -8,17 +8,38 @@ let defaultin_plugin = (function () { // // handle the default key triggering onSend() var onKeydown = function (event) { - var inputfield = $("#inputfield"); - - // Enter Key without shift - if ( inputfield.is(":focus") && (event.which === 13) && (!event.shiftKey) ) { - var outtext = inputfield.val(); - var lines = outtext.trim().replace(/[\r]+/,"\n").replace(/[\n]+/, "\n").split("\n"); - for (var i = 0; i < lines.length; i++) { - plugin_handler.onSend( lines[i].trim() ); - } - inputfield.val(''); - event.preventDefault(); + // find where the key comes from + var inputfield = $(".inputfield:focus"); + + // check for important keys + switch (event.which) { + case 16: // ignore shift + case 17: // ignore alt + case 18: // ignore control + case 20: // ignore caps lock + case 144: // ignore num lock + break; + + case 13: // Enter key + var outtext = inputfield.val(); + if ( outtext && !event.shiftKey ) { // Enter Key without shift --> send Mesg + var lines = outtext.trim().replace(/[\r]+/,"\n").replace(/[\n]+/, "\n").split("\n"); + for (var i = 0; i < lines.length; i++) { + plugin_handler.onSend( lines[i].trim() ); + } + inputfield.val(''); + event.preventDefault(); + } + inputfield.blur(); + break; + + // Anything else, focus() a textarea if needed, and allow the default event + default: + // is anything actually focused? if not, focus the first .inputfield found in the DOM + if( !inputfield.hasClass('inputfield') ) { + // :first only matters if dual_input or similar multi-input plugins are in use + $('.inputfield:last').focus(); + } } return true; @@ -29,10 +50,11 @@ let defaultin_plugin = (function () { var init = function () { // Handle pressing the send button $("#inputsend") - .bind("click", function (event) { + .bind("click", function (evnt) { + // simulate a carriage return var e = $.Event( "keydown" ); e.which = 13; - $('#inputfield').trigger(e); + $('.inputfield:last').trigger(e); }); console.log('DefaultIn initialized'); diff --git a/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js b/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js index f0aa47b67c..0df9f42539 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js +++ b/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js @@ -14,25 +14,36 @@ plugin_handler.add('goldenlayout', (function () { content: [{ type: 'column', content: [{ - type: 'component', - componentName: 'Main', - isClosable: false, - componentState: { - types: 'untagged', - update_method: 'newlines', - } + type: 'row', + content: [{ + type: 'column', + content: [{ + type: 'component', + componentName: 'Main', + isClosable: false, + componentState: { + types: 'untagged', + update_method: 'newlines', + }, + }] + }], }, { type: 'component', componentName: 'input', id: 'inputComponent', - height: 15, + height: 12, + }, { + type: 'component', + componentName: 'input', + id: 'inputComponent', + height: 12, isClosable: false, }] }] }; - var newDragConfig = { + var newTabConfig = { title: 'Untitled', type: 'component', componentName: 'evennia', @@ -97,16 +108,26 @@ plugin_handler.add('goldenlayout', (function () { let content = element.find('.content'); let title = evnt.data.contentItem.config.title; let renamebox = document.getElementById('renamebox'); + + // check that no other popup is open + if( document.getElementById('typelist') || document.getElementById('updatelist') ) { + return; + } + if( !renamebox ) { renamebox = $('
'); renamebox.append(''); renamebox.insertBefore( content ); } else { let title = $('#renameboxin').val(); - evnt.data.setTitle( title ); - evnt.data.contentItem.setTitle( title ); - myLayout.emit('stateChanged'); - $('#renamebox').remove(); + + // check that the renamebox that is open is for this contentItem + if( $('#renamebox').parent()[0] === content.parent()[0] ) { + evnt.data.setTitle( title ); + evnt.data.contentItem.setTitle( title ); + myLayout.emit('stateChanged'); + $('#renamebox').remove(); + } } } @@ -140,9 +161,7 @@ plugin_handler.add('goldenlayout', (function () { // // - var commitCheckboxes = function (evnt) { - let element = $(evnt.data.contentItem.element); - let content = element.find('.content'); + var commitCheckboxes = function (evnt, content) { let checkboxes = $('#typelist :input'); let types = []; for (i=0; i') - .append( '
' ) - .append( '' ) - .appendTo( container.getElement() ); - }); - - myLayout.registerComponent( 'evennia', function (container, componentState) { - let div = $('
'); - initComponent(div, container, componentState, 'all', 'newlines'); - container.on('destroy', calculate_untagged_types); - }); - - // Make it go. + // + var postInit = function () { + // finish the setup and actually start GoldenLayout myLayout.init(); - // Event when client window changes + // Set the Event handler for when the client window changes size $(window).bind("resize", scrollAll); // Set Save State callback @@ -394,10 +415,64 @@ plugin_handler.add('goldenlayout', (function () { console.log('Golden Layout Plugin Initialized.'); } + + // + // required Init me + var init = function (options) { + // Set up our GoldenLayout instance built off of the default main-sub div + var savedState = localStorage.getItem( 'evenniaGoldenLayoutSavedState' ); + var mainsub = document.getElementById('main-sub'); + + if( savedState !== null ) { + config = JSON.parse( savedState ); + } + + myLayout = new GoldenLayout( config, mainsub ); + + $('#inputcontrol').remove(); // remove the cluttered, HTML-defined input divs + + // register our component and replace the default messagewindow with the Main component + myLayout.registerComponent( 'Main', function (container, componentState) { + let main = $('#messagewindow').addClass('content'); + initComponent(main, container, componentState, 'untagged', 'newlines' ); + }); + + // register our new input component + myLayout.registerComponent( 'input', function (container, componentState) { + var inputfield = $(''); + var button = $(''); + + $('
') + .append( button ) + .append( inputfield ) + .appendTo( container.getElement() ); + + button.bind('click', function (evnt) { + // focus our textarea + $( $(evnt.target).siblings('.inputfield')[0] ).focus(); + // fake a carriage return event + var e = $.Event('keydown'); + e.which = 13; + $( $(evnt.target).siblings('.inputfield')[0] ).trigger(e); + }); + + }); + + myLayout.registerComponent( 'evennia', function (container, componentState) { + let div = $('
'); + initComponent(div, container, componentState, 'all', 'newlines'); + container.on('destroy', calculate_untagged_types); + }); + } + + return { init: init, + postInit: postInit, + onKeydown: onKeydown, onText: onText, getGL: function () { return myLayout }, - initComponent: initComponent, + getConfig: function () { return config }, + setConfig: function (newconfig) { config = newconfig }, } })()); diff --git a/evennia/web/webclient/static/webclient/js/plugins/history.js b/evennia/web/webclient/static/webclient/js/plugins/history.js index 7860547440..c68d2b77a5 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/history.js +++ b/evennia/web/webclient/static/webclient/js/plugins/history.js @@ -6,72 +6,49 @@ let history_plugin = (function () { // Manage history for input line - var history_max = 20; - var history = {}; - var history_pos = {}; + var history_max = 21; + var history = new Array(); + var history_pos = 0; + + history[0] = ''; // the very latest input is empty for new entry. // - // Add a new textarea to track history for. - var track_history_for_id = function(id) { - if( ! history.hasOwnProperty( id ) ) { - history[id] = new Array; - history_pos[id] = -1; - } else { - console.log('IGNORED -- already tracking history for that DOM element!'); - } + // move back in the history + var back = function () { + // step backwards in history stack + history_pos = Math.min(++history_pos, history.length - 1); + return history[history.length - 1 - history_pos]; } // - // Return whichever inputfield (if any) is focused, out of the set we are tracking - var get_focused_input = function () { - let inputfield = $( document.activeElement ); - - // is the focused element one of the ones we are tracking history for? - if( history.hasOwnProperty( inputfield.attr('id') ) ) { - return inputfield; - } - return null; - } - - // - // move back from the history (to newer elements) - var back = function (id) { - // step back in history queue, to the most recently stored entry. - if( history_pos[id] >= 0 ) { - history_pos[id]--; - - // if we've stepped "before" the first element of our queue, return new, empty string - if( history_pos[id] == -1 ) { - return ''; - } - } - - return history[id][ history_pos[id] ]; - } - - // - // move forward into the history (to older elements) - var fwd = function (id) { - // step forward in history queue, restricted by bounds checking - if( history_pos[id] < Math.min( history[id].length - 1, history_max - 1 ) ) { - history_pos[id]++; - } - return history[id][ history_pos[id] ]; + // move forward in the history + var fwd = function () { + // step forwards in history stack + history_pos = Math.max(--history_pos, 0); + return history[history.length - 1 - history_pos]; } // // add a new history line - var add = function (id, input) { + var add = function (input) { // add a new entry to history, don't repeat latest - if (input && input != history[id][0]) { - // make sure to trim the history queue length to 'history_max' - if (history[id].length + 1 >= history_max) { - history[id].pop(); // remove oldest entry from queue + if (input && input != history[history.length-2]) { + if (history.length >= history_max) { + history.shift(); // kill oldest entry } - history[id].unshift(input); // add newest entry to beginning of queue + history[history.length-1] = input; + history[history.length] = ''; } - // reset the position to the beginning of the queue - history_pos[id] = -1; + history_pos = 0; + } + + // + // Add input to the scratch line + var scratch = function (input) { + // Put the input into the last history entry (which is normally empty) + // without making the array larger as with add. + // Allows for in-progress editing to be saved. + history[history.length-1] = input; } // Public @@ -79,52 +56,40 @@ let history_plugin = (function () { // // Handle up arrow and down arrow events. var onKeydown = function(event) { - var keycode = event.which; + var code = event.which; + var history_entry = null; + var inputfield = $('.inputfield:focus'); - // Is one of the two input fields focused? - let inputfield = get_focused_input(); - if( inputfield != null ) { - let id = inputfield.attr('id') - let history_entry = null; // check the keycode for up/down arrows - if (keycode === 40) { // Arrow down - history_entry = back(id); - } - else if (keycode === 38) { // Arrow up - history_entry = fwd(id); - } - - if (history_entry !== null) { - // Performing a history navigation - // replace the text in the input and move the cursor to the end of the new value - inputfield.blur().focus().val(history_entry); - event.preventDefault(); - return true; - } + // Only process up/down arrow if cursor is at the end of the line. + if (code === 38 && event.shiftKey) { // Arrow up + history_entry = back(); } + else if (code === 40 && event.shiftKey) { // Arrow down + history_entry = fwd(); + } + + // are we processing an up or down history event? + if (history_entry !== null) { + // Doing a history navigation; replace the text in the input and move the cursor to the end of the new value + inputfield.val(''); + inputfield.blur().focus().val(history_entry); + event.preventDefault(); + return true; + } + return false; } // // Listen for onSend lines to add to history var onSend = function (line) { - let inputfield = get_focused_input(); - if( inputfield != null ) { - add(inputfield.attr('id'), line); - } - return null; // we are not returning an altered input line + add(line); + return null; } // // Init function var init = function () { - track_history_for_id('inputfield'); // The default inputfield - - // check to see if the dual_input plugin is enabled. - if( !(typeof plugins['dual_input'] === "undefined") ) { - console.log('configuring history tracking for dual_input plugin'); - track_history_for_id('inputfield2'); - } - console.log('History Plugin Initialized.'); } diff --git a/evennia/web/webclient/static/webclient/js/webclient_gui.js b/evennia/web/webclient/static/webclient/js/webclient_gui.js index 146d4a5268..412827f153 100644 --- a/evennia/web/webclient/static/webclient/js/webclient_gui.js +++ b/evennia/web/webclient/static/webclient/js/webclient_gui.js @@ -228,6 +228,20 @@ var plugin_handler = (function () { } + // + // normally init() is all that is needed, but some cases may require a second + // pass to avoid chicken/egg dependencies between two plugins. + var postInit = function () { + // does this plugin need postInit() to be called? + for( let n=0; n < ordered_plugins.length; n++ ) { + let plugin = ordered_plugins[n]; + if( 'postInit' in plugin ) { + plugin.postInit(); + } + } + } + + return { add: add, onKeydown: onKeydown, @@ -241,6 +255,7 @@ var plugin_handler = (function () { onConnectionClose: onConnectionClose, onSend: onSend, init: init, + postInit: postInit, } })(); @@ -285,5 +300,8 @@ $(document).ready(function() { // Initialize all plugins plugin_handler.init(); + // Finish Initializing any plugins that need a second stage + plugin_handler.postInit(); + console.log("Completed Webclient setup"); });