TextEditor TextFieldと同じ使い方で以下の機能を追加 ・改行入力を受け付ける ・タブ入力を受け付ける ・オートインデント ・アンドゥ/リドゥ tepe forked:0favorite:0lines:195license : MIT License modified : 2012-02-19 13:54:21 Embed Tweet package { import flash.text.TextField; import flash.display.Sprite; import flash.events.*; public class FlashTest extends Sprite { private var t1:TextEditor = new TextEditor(); public function FlashTest() { // write as3 code here.. t1.border = true; addChild(t1); t1.type = "input"; t1.width = 456; t1.height = 200; } } } //////////////////////////////////////////////////////////////////////// // TextEditor class //////////////////////////////////////////////////////////////////////// /* --TextFieldへの追加機能-- ・改行入力 ・タブ入力 ・オートインデント ・アンドゥ/リドゥ */ import flash.text.TextField; import flash.events.*; class TextEditor extends TextField { private var prevText:String = "";//text変更前の状態 private var indentStr:String; private var sc:StringComparator = new StringComparator(); private var historyManager:HistoryManager = new HistoryManager(); private var comparator:StringComparator = new StringComparator(); public function TextEditor():void { this.type = "input";//入力可能 //this.multiline = true;//マルチライン this.addEventListener(KeyboardEvent.KEY_DOWN, onKey); this.addEventListener(FocusEvent.KEY_FOCUS_CHANGE,focusChangeListener);//タブキーによるフォーカス変更をキャンセル this.addEventListener(Event.CHANGE,onChange);//textの変更 } private var sw:Boolean; private function onChange(e:Event):void{ if(sw==false)addHistory(); if(sw2==true){ this.multiline = false; sw2=false; } prevText = this.text; sw=false; } private function focusChangeListener(e:FocusEvent):void{ e.preventDefault(); } //入力履歴更新 private function addHistory():void{ comparator.compare(prevText, this.text); var entry:HistoryEntry = new HistoryEntry(comparator.commonPrefixLength); entry.removeText = prevText.substring(comparator.commonPrefixLength, prevText.length - comparator.commonSuffixLength); entry.addText = this.text.substring(comparator.commonPrefixLength, this.text.length - comparator.commonSuffixLength); historyManager.appendEntry(entry); prevText = this.text; } //キー入力 private var sw2:Boolean; private function onKey(e:KeyboardEvent):void { // Ctrl+Z : UNDO if (e.keyCode == 90 && e.ctrlKey) { sw=true; undo(); return; } // Ctrl+Y : REDO if (e.keyCode == 89 && e.ctrlKey) { sw=true; redo(); return; } //ペースト if(e.keyCode == 86 && e.ctrlKey){ this.multiline = true;//改行を含むコピーに対応 sw2=true; } //キャレット位置に改行文字を挿入 if(e.keyCode == 13 || e.keyCode == 108){ lineFeed(); return; } //tab if (e.keyCode == 9) { tab(); return; } } //改行 private function lineFeed():void{ //sw2=true; var str1:String; var str2:String; var scrV:int = this.scrollV; str1 = this.text.substring(0, this.caretIndex); str2 = this.text.substring(this.caretIndex, this.length); this.text = str1; this.appendText("\n");//キャレット位置で改行 //インデント構造(タブ&スペースの構成)を調べる var indent:int=0; var prevReturn:int = this.text.lastIndexOf('\r', this.caretIndex-1);//前回の改行位置 //一つ前の改行直後に続くタブコードの数=インデント深度 for (var j:int = prevReturn+1; j < this.caretIndex; j++) { if (this.text.charAt(j) == '\t' || this.text.charAt(j) == ' ' ) indent++; else break; } //上の行のインデントに従う var iStr:String = this.text.slice(prevReturn + 1, prevReturn + 1 + indent); indentStr = iStr; this.appendText(iStr);//インデント this.text += str2;//結合 //キャレット位置をインクリメント this.scrollV = scrV; this.setSelection(this.caretIndex +indent + 1, this.caretIndex +indent + 1); this.dispatchEvent(new Event(Event.CHANGE)); } //タブ private function tab():void{ var str1:String; var str2:String; str1 = this.text.substring(0, this.caretIndex); str2 = this.text.substring(this.caretIndex, this.length); this.text = str1 + '\t' + str2; //キャレット位置をインクリメント this.setSelection(this.caretIndex + 1, this.caretIndex + 1); this.dispatchEvent(new Event(Event.CHANGE)); } //アンドゥ public function undo():void { if (historyManager.canBack) { var entry:HistoryEntry = historyManager.back(); //テキストセット this.replaceText(entry.index, entry.index + entry.addText.length, entry.removeText); //キャレット位置セット this.setSelection(entry.index + entry.removeText.length, entry.index + entry.removeText.length); } this.dispatchEvent(new Event(Event.CHANGE)); } //リドゥ public function redo():void { if (historyManager.canForward) { var entry:HistoryEntry = historyManager.forward(); //テキストセット this.replaceText(entry.index, entry.index + entry.removeText.length, entry.addText); //キャレット位置セット this.setSelection(entry.index + entry.addText.length, entry.index + entry.addText.length); } this.dispatchEvent(new Event(Event.CHANGE)); } } import __AS3__.vec.Vector; class HistoryManager { public var currentIndex:int = 0; public var length:int=0; private var entries:Vector.<HistoryEntry>; public function HistoryManager() { entries = new Vector.<HistoryEntry>(); } //履歴追記 public function appendEntry(entry:HistoryEntry):void { entries.length = currentIndex; length = entries.length; entries.push(entry); currentIndex++;// = entries.length; } //履歴削除 public function clear():void { currentIndex = 0; entries.length = 0; } //リドゥ可能 public function get canForward():Boolean { return currentIndex < entries.length; } //アンドゥ可能 public function get canBack():Boolean { return currentIndex > 0; } //リドゥ public function forward():HistoryEntry { return entries[currentIndex++]; } //アンドゥ public function back():HistoryEntry { return entries[--currentIndex]; } } class HistoryEntry { public var index:int; public var removeText:String; public var addText:String; public function HistoryEntry(index:int=0, remove:String="", add:String="") { this.index = index;//文字列先頭位置 this.removeText = remove;//消した文字列 this.addText = add;//追加した文字列 } } // 文字列の左右一致を数える class StringComparator { // 左側の共通文字列長 public var commonPrefixLength:int; // 右側の共通文字列長 public var commonSuffixLength:int; /** * 2つの文字列を比較し、commonPrefixLengthとcommonSuffixLengthをセットする * * @param str1 比較する文字列の一方 * @param str2 比較する文字列の他方 */ public function compare(str1:String, str2:String):void { var minLength:int = Math.min(str1.length, str2.length); var step:int, l:int, r:int; step = Math.pow(2, Math.floor(Math.log(minLength) / Math.log(2))); for (l=0; l<minLength; ) { if (str1.substr(0, l + step) != str2.substr(0, l + step)) { if (step == 1) { break; } step >>= 1; } else { l += step; } } l = Math.min(l, minLength); minLength -= l; step = Math.pow(2, Math.floor(Math.log(minLength) / Math.log(2))); for (r=0; r<minLength; ) { if (str1.substr(-r - step) != str2.substr(-r - step)) { if (step == 1) { break; } step >>= 1; } else { r += step; } } r = Math.min(r, minLength); commonPrefixLength = l; commonSuffixLength = r; } } Code Fullscreen Preview Fullscreen TextField text caretIndex setSelection Event.CHANGE keyCode dispatchEvent ctrlKey replaceText Math.log scrollV addEventListener FocusEvent.KEY_FOCUS_CHANGE text preventDefault substr charAt multiline lastIndexOf FocusEvent appendText substring