From 8a0709757379a1b9af0523bd0db1b7c9e5a8eabf Mon Sep 17 00:00:00 2001 From: Jeremy Maitin-Shepard Date: Thu, 10 Apr 2008 12:58:34 -0400 Subject: [PATCH] minibuffer: better support for input methods The minibuffer no longer relies on keypress event handlers to add characters to the minibuffer. Instead, text is normally added to the minibuffer input field using the default handlers, which is what is done unconditionally by input methods anyway. That way, a single handler for the "input" event can be used to detect changes to the minibuffer text by the default Mozilla key handler. --- modules/bindings/default/hints.js | 5 +- modules/bindings/default/isearch.js | 8 +-- modules/bindings/default/minibuffer.js | 2 +- modules/find.js | 42 +++++------- modules/hints.js | 115 ++++++++++++++++++--------------- modules/minibuffer-read.js | 6 +- modules/minibuffer.js | 62 +++++++++--------- 7 files changed, 122 insertions(+), 118 deletions(-) diff --git a/modules/bindings/default/hints.js b/modules/bindings/default/hints.js index 01922f2..dd57128 100644 --- a/modules/bindings/default/hints.js +++ b/modules/bindings/default/hints.js @@ -2,7 +2,10 @@ require("bindings/default/global.js"); define_keymap("hint_keymap", $parent = default_base_keymap); -define_key(hint_keymap, kbd(match_any_unmodified_key), "hints-handle-character"); +define_key(hint_keymap, match_any_unmodified_key, null, $fallthrough); +for (let i = 0; i <= 9; ++i) + define_key(hint_keymap, String.fromCharCode("0".charCodeAt(0) + i), "hints-handle-number"); + define_key(hint_keymap, "back_space", "hints-backspace"); define_key(hint_keymap, "tab", "hints-next"); define_key(hint_keymap, "right", "hints-next"); diff --git a/modules/bindings/default/isearch.js b/modules/bindings/default/isearch.js index 72af64c..56a8251 100644 --- a/modules/bindings/default/isearch.js +++ b/modules/bindings/default/isearch.js @@ -5,8 +5,8 @@ define_keymap("isearch_keymap", $parent = default_base_keymap); define_key(isearch_keymap, "back_space", "isearch-backspace"); define_key(isearch_keymap, "C-r", "isearch-continue-backward"); define_key(isearch_keymap, "C-s", "isearch-continue-forward"); -define_key(isearch_keymap, "C-g", "isearch-abort"); -define_key(isearch_keymap, "escape", "isearch-abort"); +define_key(isearch_keymap, "C-g", "minibuffer-abort"); +define_key(isearch_keymap, "escape", "minibuffer-abort"); -define_key(isearch_keymap, match_any_unmodified_key, "isearch-add-character"); -define_key(isearch_keymap, "return", "isearch-done"); +define_key(isearch_keymap, match_any_unmodified_key, null, $fallthrough); +define_key(isearch_keymap, match_any_key, "isearch-done"); diff --git a/modules/bindings/default/minibuffer.js b/modules/bindings/default/minibuffer.js index a3ad585..08aa890 100644 --- a/modules/bindings/default/minibuffer.js +++ b/modules/bindings/default/minibuffer.js @@ -40,7 +40,7 @@ define_key(minibuffer_base_keymap, "C-space", "minibuffer-set-mark"); // Nasty keys define_key(minibuffer_base_keymap, "C-r", "minibuffer-cmd_redo"); -define_key(minibuffer_base_keymap, match_any_unmodified_key, "minibuffer-insert-character"); +define_key(minibuffer_base_keymap, match_any_unmodified_key, null, $fallthrough); // }}} diff --git a/modules/find.js b/modules/find.js index 5e53f0c..25091ef 100644 --- a/modules/find.js +++ b/modules/find.js @@ -254,6 +254,21 @@ isearch_session.prototype = { } } } while ((node = node.parentNode)); + }, + + handle_input : function (m) { + m._set_selection(); + this.find(m._input_text, this.top.direction, this.top.point); + this.restore_state(); + }, + + done : false, + + destroy : function () { + if (!this.done) { + this.frame.scrollTo(this.states[0].screenx, this.states[0].screeny); + this._clear_selection(); + } } }; @@ -290,31 +305,6 @@ function isearch_backspace (window) } interactive("isearch-backspace", function (I) {isearch_backspace(I.window);}); -/* FIXME: do this stuff in .destroy instead */ -function isearch_abort (window) -{ - var s = window.minibuffer.current_state; - if (!(s instanceof isearch_session)) - throw "Invalid minibuffer state"; - window.minibuffer.pop_state(); - s.frame.scrollTo(s.states[0].screenx, s.states[0].screeny); - s._clear_selection(); -} -interactive("isearch-abort", function(I) {isearch_abort(I.window);}); - - -function isearch_add_character (window, event) -{ - var s = window.minibuffer.current_state; - if (!(s instanceof isearch_session)) - throw "Invalid minibuffer state"; - var str = s.top.search_str; - str += String.fromCharCode(event.charCode); - s.find(str, s.top.direction, s.top.point); - s.restore_state(); -} -interactive("isearch-add-character", function (I) {isearch_add_character(I.window, I.event);}); - function isearch_done (window) { var s = window.minibuffer.current_state; @@ -326,6 +316,8 @@ function isearch_done (window) window.minibuffer.saved_focused_element = null; window.minibuffer.saved_focused_window = null; + s.done = true; + window.minibuffer.pop_state(); window.isearch_last_search = s.top.search_str; s.focus_link(); diff --git a/modules/hints.js b/modules/hints.js index 571bd3e..7f8d087 100644 --- a/modules/hints.js +++ b/modules/hints.js @@ -307,6 +307,7 @@ function hints_minibuffer_state(continuation, buffer) { keywords(arguments, $keymap = hint_keymap, $auto); basic_minibuffer_state.call(this, $prompt = arguments.$prompt); + this.original_prompt = arguments.$prompt; this.continuation = continuation; this.keymap = arguments.$keymap; this.auto_exit = arguments.$auto ? true : false; @@ -338,70 +339,76 @@ hints_minibuffer_state.prototype = { }, destroy : function () { if (this.auto_exit_timer_ID) { - window.clearTimeout(this.auto_exit_timer_ID); + this.manager.window.clearTimeout(this.auto_exit_timer_ID); this.auto_exit_timer_ID = null; } this.manager.remove(); }, - update_minibuffer : function (window) { - var str = this.typed_string; - if (this.typed_number.length > 0) { - str += " #" + this.typed_number; - } - window.minibuffer._input_text = str; - window.minibuffer._set_selection(); + update_minibuffer : function (m) { + if (this.typed_number.length > 0) + m.prompt = this.original_prompt + " #" + this.typed_number; + else + m.prompt = this.original_prompt; + }, + + handle_auto_exit : function (m) { + let window = m.window; + + if (this.auto_exit_timer_ID) + window.clearTimeout(this.auto_exit_timer_ID); + let s = this; + this.auto_exit_timer_ID = window.setTimeout(function() { hints_exit(window, s); }, + hints_auto_exit_delay); + }, + + handle_input : function (m) { + m._set_selection(); + + var auto_exit = false; + this.typed_number = ""; + this.typed_string = m._input_text; + this.manager.current_hint_string = this.typed_string; + this.manager.current_hint_number = -1; + this.manager.update_valid_hints(); + if (this.auto_exit && this.manager.valid_hints.length == 1) + this.handle_auto_exit(m); + this.update_minibuffer(m); } }; define_variable("hints_auto_exit_delay", 500, "Delay (in milliseconds) after the most recent key stroke before a sole matching element is automatically selected. If this is set to 0, automatic selection is disabled."); -function hints_handle_character(window, s, e) { - /* Check for numbers */ - var ch = String.fromCharCode(e.charCode); +interactive("hints-handle-number", function (I) { + let s = I.minibuffer.check_state(hints_minibuffer_state); + var ch = String.fromCharCode(I.event.charCode); var auto_exit = false; /* TODO: implement number escaping */ - if (e.charCode >= 48 && e.charCode <= 57) { - // Number entered - s.typed_number += ch; - s.manager.select_hint(parseInt(s.typed_number)); - var num = s.manager.current_hint_number; - if (s.auto_exit) { - if (num > 0 && num <= s.manager.valid_hints.length && num * 10 > s.manager.valid_hints.length) - auto_exit = true; - if (num == 0) { - if (!s.multiple) { - hints_exit(window, s); - return; - } - auto_exit = true; + // Number entered + s.typed_number += ch; + + s.manager.select_hint(parseInt(s.typed_number)); + var num = s.manager.current_hint_number; + if (s.auto_exit) { + if (num > 0 && num <= s.manager.valid_hints.length && num * 10 > s.manager.valid_hints.length) + auto_exit = true; + if (num == 0) { + if (!s.multiple) { + hints_exit(I.window, s); + return; } - } - } else { - s.typed_number = ""; - s.typed_string += ch; - s.manager.current_hint_string = s.typed_string; - s.manager.current_hint_number = -1; - s.manager.update_valid_hints(); - if (s.auto_exit && s.manager.valid_hints.length == 1) auto_exit = true; - } - if (auto_exit) { - if (this.auto_exit_timer_ID) { - window.clearTimeout(this.auto_exit_timer_ID); } - this.auto_exit_timer_ID = window.setTimeout(function() { hints_exit(window, s); }, - hints_auto_exit_delay); } - s.update_minibuffer(window); -} -interactive("hints-handle-character", function (I) { - hints_handle_character(I.window, I.minibuffer.check_state(hints_minibuffer_state), I.event); + if (auto_exit) + s.handle_auto_exit(I.minibuffer); + s.update_minibuffer(I.minibuffer); }); function hints_backspace(window, s) { - if (this.auto_exit_timer_ID) { - window.clearTimeout(this.auto_exit_timer_ID); - this.auto_exit_timer_ID = null; + let m = window.minibuffer; + if (s.auto_exit_timer_ID) { + window.clearTimeout(s.auto_exit_timer_ID); + s.auto_exit_timer_ID = null; } if (s.typed_number.length > 0) { s.typed_number = s.typed_number.substring(0, s.typed_number.length - 1); @@ -409,20 +416,22 @@ function hints_backspace(window, s) { s.manager.select_hint(num); } else if (s.typed_string.length > 0) { s.typed_string = s.typed_string.substring(0, s.typed_string.length - 1); + m._input_text = s.typed_string; + m._set_selection(); s.manager.current_hint_string = s.typed_string; s.manager_current_hint_number = -1; s.manager.update_valid_hints(); } - s.update_minibuffer(window); + s.update_minibuffer(m); } interactive("hints-backspace", function (I) { hints_backspace(I.window, I.minibuffer.check_state(hints_minibuffer_state)); }); function hints_next(window, s, count) { - if (this.auto_exit_timer_ID) { - window.clearTimeout(this.auto_exit_timer_ID); - this.auto_exit_timer_ID = null; + if (s.auto_exit_timer_ID) { + window.clearTimeout(s.auto_exit_timer_ID); + s.auto_exit_timer_ID = null; } s.typed_number = ""; var cur = s.manager.current_hint_number - 1; @@ -445,9 +454,9 @@ interactive("hints-previous", function (I) { function hints_exit(window, s) { - if (this.auto_exit_timer_ID) { - window.clearTimeout(this.auto_exit_timer_ID); - this.auto_exit_timer_ID = null; + if (s.auto_exit_timer_ID) { + window.clearTimeout(s.auto_exit_timer_ID); + s.auto_exit_timer_ID = null; } var cur = s.manager.current_hint_number; var elem = null; diff --git a/modules/minibuffer-read.js b/modules/minibuffer-read.js index 859f21f..c66f3a5 100644 --- a/modules/minibuffer-read.js +++ b/modules/minibuffer-read.js @@ -183,7 +183,7 @@ text_entry_minibuffer_state.prototype = { this.continuation.throw(abort()); }, - handle_input_changed : function () { + handle_input : function () { if (!this.completer) return; this.completions_valid = false; @@ -208,6 +208,10 @@ text_entry_minibuffer_state.prototype = { s.update_completions_display(); }, + ran_minibuffer_command : function () { + this.handle_input(); + }, + update_completions_display : function () { var m = this.window.minibuffer; diff --git a/modules/minibuffer.js b/modules/minibuffer.js index 6a968ff..545fe5c 100644 --- a/modules/minibuffer.js +++ b/modules/minibuffer.js @@ -22,11 +22,17 @@ define_builtin_commands( m._restore_normal_state(); var e = m.input_element; var c = e.controllers.getControllerForCommand(command); - if (c && c.isCommandEnabled(command)) - c.doCommand(command); + try { + m.ignore_input_events = true; + if (c && c.isCommandEnabled(command)) + c.doCommand(command); + } finally { + m.ignore_input_events = false; + } var s = m.current_state; - if ((s instanceof text_entry_minibuffer_state)) - s.handle_input_changed(); + let x = s.ran_minibuffer_command; + if (x) + x(command); } } catch (e) { @@ -40,33 +46,6 @@ define_builtin_commands( function (I) I.minibuffer.current_state.mark_active ); -function minibuffer_insert_character(window, n, event) -{ - var m = window.minibuffer; - var s = m.current_state; - if (!(s instanceof basic_minibuffer_state)) - throw "Invalid minibuffer state"; - m._restore_normal_state(); - var val = m._input_text; - var sel_start = m._selection_start; - var sel_end = m._selection_end; - var insert = String.fromCharCode(event.charCode); - var out = val.substr(0, sel_start); - for (var i = 0; i < n; ++i) - out += insert; - out += val.substr(sel_end); - m._input_text = out; - var new_sel = sel_end + n; - m._set_selection(new_sel, new_sel); - - if (s instanceof text_entry_minibuffer_state) - s.handle_input_changed(); -} -interactive("minibuffer-insert-character", function (I) { - minibuffer_insert_character(I.window, I.p, I.event); -}); - - function minibuffer_state(keymap, use_input_mode) { this.keymap = keymap; @@ -165,10 +144,27 @@ function minibuffer (window) { window.setTimeout( function(){ - m.input_element.mInputField.focus(); + m.input_element.inputField.focus(); }, 0); } }, false); + this.input_element.addEventListener("input", function(e) { + if (m.ignore_input_events || !m._input_mode_enabled) + return; + var s = m.current_state; + if (s) { + if (s.handle_input) + s.handle_input(m); + } + }, true); + + // Ensure that the input area will have focus if a message is + // currently being flashed so that the default handler for key + // events will properly add text to the input area. + window.addEventListener("keydown", function (e) { + if (m._input_mode_enabled && m._showing_message) + m._restore_normal_state(); + }, true); this.window = window; this.last_message = ""; this.states = []; @@ -314,7 +310,7 @@ minibuffer.prototype = { _switch_to_input_mode : function () { this.element.setAttribute("minibuffermode", "input"); - this.input_element.focus(); + this.input_element.inputField.focus(); }, _switch_to_message_mode : function () { -- 2.11.4.GIT