// EditLayer.tjs - 一行編集用コントロールレイヤ // Copyright (C)2001-2003, W.Dee 改変・配布は自由です /* 単一行の編集コントロールとして動作するレイヤです。 書式 new EditLayer(, ) */ class EditLayer extends KAGLayer { var Edit_text = ""; // 編集中のテキスト var Edit_selStart = 0; // カーソル位置 var Edit_selLength = 0; // 選択長さ var Edit_scrollPos = 0; // 表示されている左端の文字位置 var Edit_caretLayer; // キャレット用レイヤ var Edit_color = clWindow; // 背景色 var Edit_antialiased = true; // アンチエイリアスをかけるか var Edit_opacity = 192; // 不透明度 var Edit_maxChars = 0; // 最大文字数 ( 0 = 無制限 ) var Edit_textColor = clWindowText; var Edit_blinkTimer; var Edit_vertical = false; function EditLayer(win, par, vert = false) { super.KAGLayer(win, par); Edit_vertical = vert; hitType = htMask; hitThreshold = 0; // 全域でマウスイベントは不透明 focusable = true; // フォーカスは受け取る useAttention = true; // 注視ポイントを使用する Edit_caretLayer = new global.Layer(win, this); Edit_caretLayer.hitType = htMask; Edit_caretLayer.hitThreshold = 256; // 完全にマウスイベントは透明 resizeCaret(); // 点滅用タイマを用意 Edit_blinkTimer = new Timer(onBlink, ''); cursor = vert ? crHBeam : crIBeam; update(); } function finalize() { invalidate Edit_caretLayer; invalidate Edit_blinkTimer; super.finalize(...); } function assign(src) { assignImages(src, true); Edit_text = src.Edit_text; Edit_selStart = src.Edit_selStart; Edit_selLength = src.Edit_selLength; Edit_scrollPos = src.Edit_scrollPos; Edit_color = src.Edit_color; Edit_textColor = src.Edit_textColor; Edit_antialiased = src.Edit_antialiased; Edit_opacity = src.Edit_opacity; Edit_vertical = src.Edit_vertical; Edit_maxChars = src.Edit_maxChars; var f = font; var sf = src.font; f.face = sf.face; f.angle = sf.angle; f.bold = sf.bold; f.italic = sf.italic; f.height = sf.height; cursor = src.cursor; resizeCaret(); update(); } function resizeCaret() { // キャレットのサイズの初期化 if(Edit_vertical) { var h = 2; // 2 固定 var w = font.height; w = -w if(w<0); Edit_caretLayer.setImageSize(w, h); Edit_caretLayer.setSizeToImageSize(); Edit_caretLayer.colorRect(0, 0, w, h, Edit_textColor); } else { var w = 2; // 2 固定 var h = font.height; h = -h if(h<0); Edit_caretLayer.setImageSize(w, h); Edit_caretLayer.setSizeToImageSize(); Edit_caretLayer.colorRect(0, 0, w, h, Edit_textColor); } } function getCaretXPos() { // キャレットの位置を取得 var bch = Edit_text.substring(0, Edit_selStart); // キャレット以前の文字列 var bchw = font.getTextWidth(bch); var sch = Edit_text.substring(0, Edit_scrollPos); // 表示以前の文字 var schw = font.getTextWidth(sch); return bchw - schw + 3; } function ensureCaretVisible(step = 1) { // キャレットが確実に表示される位置までスクロールする var sp_save = Edit_scrollPos; var txtlen = Edit_text.length; if(Edit_selStart == txtlen) while(Edit_scrollPos >= 0 && getCaretXPos() <= 3) Edit_scrollPos-=step; else while(Edit_scrollPos >= 0 && getCaretXPos() < 3) Edit_scrollPos-=step; var lim = Edit_vertical ? imageHeight : imageWidth; while(Edit_scrollPos < txtlen && getCaretXPos() >= lim -3) Edit_scrollPos+=step; if(Edit_scrollPos < 0) Edit_scrollPos = 0; if(Edit_scrollPos > txtlen) Edit_scrollPos = txtlen; if(sp_save != Edit_scrollPos) update(); } function setCaretLayerPos() { resizeCaret(); var xpos = getCaretXPos() -1; if(Edit_vertical) { Edit_caretLayer.top = xpos; Edit_caretLayer.left = imageWidth - 3 - Edit_caretLayer.imageWidth; setAttentionPos(imageWidth - 3 , Edit_caretLayer.top); } else { Edit_caretLayer.top = 3; Edit_caretLayer.left = xpos; setAttentionPos(xpos, 3); } } function showCaret() { setCaretLayerPos(); Edit_caretLayer.visible = true; Edit_blinkTimer.interval = 500; Edit_blinkTimer.enabled = true; } function hideCaret() { Edit_blinkTimer.enabled = false; Edit_caretLayer.visible = false; } function onBlink() { Edit_caretLayer.visible = ! Edit_caretLayer.visible; } function stepLeft() { // キャレットを左へ if(Edit_selLength) Edit_selLength = 0, update(); if(Edit_selStart > 0) Edit_selStart --, ensureCaretVisible(3); showCaret(); } function stepRight() { // キャレットを右へ if(Edit_selLength) Edit_selLength = 0, update(); if(Edit_selStart < Edit_text.length) Edit_selStart ++, ensureCaretVisible(3); showCaret(); } function getCaretPosFromX(x) { // クリックされた位置を割り出す x -= 3; var text = Edit_text.substring(Edit_scrollPos); var i = 0; for(;;) { var tx = text.substring(0, i); var cw; if((cw = font.getTextWidth(tx)) > x) { i --; var cc = text.substring(i, 1); var ccw = font.getTextWidth(cc); cw -= ccw; if(x > cw + (ccw>>1)) i++; if(i < 0) i = 0; if(i > text.length) i = text.length; return i + Edit_scrollPos; } i++; if(i > text.length) return text.length + Edit_scrollPos; } } function onMouseDown(x, y) { // クリックされた if(x >= 3 && y >= 3 && x < imageWidth -3 && y < imageHeight - 3) { if(Edit_vertical) Edit_selStart = getCaretPosFromX(y); else Edit_selStart = getCaretPosFromX(x); ensureCaretVisible(); showCaret(); } focus(); super.onMouseDown(...); } function insertCharacter(ch) { // ch をカーソル位置に挿入 var bcr = Edit_text.substring(0, Edit_selStart); // 選択領域以前 var acr = Edit_text.substring(Edit_selStart + Edit_selLength); // 選択領域以後 Edit_text = bcr + ch + acr; Edit_selStart += ch.length; ensureCaretVisible(); showCaret(); update(); } function deleteBeforeCaret() { // キャレットの手前の文字を一文字削除する if(Edit_selLength) return deleteSelection(); if(Edit_selStart > 0) { var bcr = Edit_text.substring(0, Edit_selStart-1); var acr = Edit_text.substring(Edit_selStart + Edit_selLength); Edit_text = bcr + acr; Edit_selStart --; ensureCaretVisible(5); showCaret(); update(); } } function deleteAfterCaret() { // キャレットの次の一文字を削除する if(Edit_selLength) return deleteSelection(); if(Edit_selStart < Edit_text.length) { var bcr = Edit_text.substring(0, Edit_selStart); var acr = Edit_text.substring(Edit_selStart + Edit_selLength + 1); Edit_text = bcr + acr; ensureCaretVisible(); showCaret(); update(); } } function onKeyPress(key) { // キーが押された if(#key >= 32) { // 普通の文字 if(!Edit_maxChars || Edit_text.length < Edit_maxChars) insertCharacter(key); // マウスカーソルを一時的に非表示にする window.hideMouseCursor(); } else super.onKeyPress(...); } function onKeyDown(key) { if(key == (Edit_vertical?VK_UP:VK_LEFT)) stepLeft(); else if(key == (Edit_vertical?VK_DOWN:VK_RIGHT)) stepRight(); else if(key == VK_DELETE) deleteAfterCaret(); else if(key == VK_BACK) deleteBeforeCaret(); else super.onKeyDown(...); } function onFocus() { // フォーカスを得た super.onFocus(...); showCaret(); } function onBlur() { // フォーカスを失った super.onBlur(...); hideCaret(); } function onPaint() { // 描画の直前に呼ばれる // 内容を描画する // とりあえずクリア var vert = Edit_vertical; var imwidth = vert?imageHeight:imageWidth; fillRect(0, 0, imageWidth, imageHeight, 0); colorRect(0, 0, imageWidth, imageHeight, Edit_color, Edit_opacity); // 文字を描画 // グローバル変数や、オブジェクト内変数はアクセスが遅いので、 // よく使う変数はローカルに持ってくる var h = font.height; h = -h if(h<0); var x = vert ? imageWidth - 3 : 3; var y = 3; var chpos = Edit_scrollPos; var text = Edit_text; var textlen = text.length; var selstart = Edit_selStart; var selend = Edit_selLength + selstart; var highlightbg = 0xff000000 | clHighlight; var highlighttext = clHighlightText; var anti = Edit_antialiased; var textcolor = Edit_textColor; var tx = ""; if(vert) { for(;;) { y = 3 + font.getTextWidth(tx); if(y > imwidth) break; if(chpos >= textlen) break; var ch = text[chpos++]; var chw = font.getTextWidth(ch); if(chpos >= selstart && chpos < selend) { // 選択領域内 // 背景を塗る fillRect(x, y, h, chw, highlightbg); // 文字を書く drawText(x, y, ch, highlighttext, 255, anti); } else { // 選択領域外 drawText(x, y, ch, textcolor, 255, anti); } tx += ch; } } else { for(;;) { x = 3 + font.getTextWidth(tx); if(x > imwidth) break; if(chpos >= textlen) break; var ch = text[chpos++]; var chw = font.getTextWidth(ch); if(chpos >= selstart && chpos < selend) { // 背景を塗る fillRect(x, y, chw, h, highlightbg); // 文字を書く drawText(x, y, ch, highlighttext, 255, anti); } else { // 選択領域外 drawText(x, y, ch, textcolor, 255, anti); } tx += ch; } } // 文字領域外をもう一度クリア if(vert) { fillRect(0, imageHeight - 3, imageWidth, 3, 0); colorRect(0, imageHeight - 3, imageWidth, 3, Edit_color, Edit_opacity); } else { fillRect(imageWidth - 3, 0, 3, imageHeight, 0); colorRect(imageWidth - 3, 0, 3, imageHeight, Edit_color, Edit_opacity); } // 枠を描画 colorRect(0, 0, imageWidth, 1, 0x000000, 128); colorRect(0, 1, 1, imageHeight-2, 0x000000, 128); colorRect(imageWidth-1, 1, 1, imageHeight-1, 0xffffff, 128); colorRect(1, imageHeight-1, imageWidth-2, 1, 0xffffff, 128); } property text { setter(x) { x = "" if x === void; Edit_text = string x; Edit_selStart = 0; Edit_selLength = 0; Edit_scrollPos = 0; setCaretLayerPos(); update(); } getter { return Edit_text; } } property maxChars { setter(x) { Edit_maxChars = int x; Edit_selStart = 0; Edit_selLength = 0; Edit_scrollPos = 0; if(Edit_maxChars && Edit_text.length >= Edit_maxChars) Edit_text = Edit_text.substring(0, Edit_maxChars); setCaretLayerPos(); update(); } getter { return Edit_maxChars; } } property width { setter(x) { super.width = x; imageWidth = x; update(); } getter { return super.width; } } property height { setter(x) { super.height = x; imageHeight = x; update(); } getter { return super.height; } } function setSize(w, h) { super.setSize(w, h); setImageSize(w, h); update(); } property vertical { setter(x) { Edit_vertical = int x; update(); } getter { return Edit_vertical; } } property color { setter(x) { Edit_color = int x; update(); } getter { return Edit_color; } } property textColor { setter(x) { Edit_textColor = int x; update(); } getter { return Edit_textColor; } } property antialiased { setter(x) { Edit_antialiased = int x; update(); } getter { return Edit_antialiased; } } property bgOpacity { setter(x) { Edit_opacity = int x; update(); } getter { return Edit_opacity; } } }