--- nono/wx/wxsoftkey.cpp 2026/04/29 17:05:07 1.1.1.10 +++ nono/wx/wxsoftkey.cpp 2026/04/29 17:05:48 1.1.1.16 @@ -4,10 +4,15 @@ // Licensed under nono-license.txt // +// +// ソフトウェアキーボード +// + #include "wxsoftkey.h" +#include "fontmanager.h" #include "wxcolor.h" +#include "wxmainframe.h" #include "wxmainview.h" -#include "wxtextscreen.h" #include "wxuimessage.h" #include "sram.h" @@ -28,7 +33,6 @@ wxBEGIN_EVENT_TABLE(WXSoftKeyPanel, inhe EVT_LEFT_UP(WXSoftKeyPanel::OnLeftUp) EVT_LEFT_DCLICK(WXSoftKeyPanel::OnLeftDClick) EVT_RIGHT_DOWN(WXSoftKeyPanel::OnRightDown) - EVT_PAINT(WXSoftKeyPanel::OnPaint) EVT_TIMER(wxID_ANY, WXSoftKeyPanel::OnTimer) wxEND_EVENT_TABLE() @@ -38,12 +42,15 @@ WXSoftKeyPanel::WXSoftKeyPanel(wxWindow : inherited(parent) , keydata(keydata_) { + // デバイスを取得 + keyboard = GetKeyboard(); + timer.SetOwner(this); u_width = u_width_; u_height = u_height_; - DoSize(); + FontChanged(); // トグル動作にするキー toggle.emplace(KC_SHIFT, 0); @@ -57,16 +64,23 @@ WXSoftKeyPanel::WXSoftKeyPanel(wxWindow std::fill(persistence.begin(), persistence.end(), 0); // このパネルへのキー入力はメインビューへのキー入力と同じ。 + auto mainframe = dynamic_cast(GetParent()->GetParent()); + auto mainview = mainframe->GetMainView(); Connect(wxEVT_CHAR, wxCharEventHandler(WXMainView::OnChar), NULL, - gMainView); + mainview); Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL, - gMainView); + mainview); Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL, - gMainView); + mainview); // VM からの通知を受け取る WXUIMessage::Connect(UIMessage::KEYBOARD, this, wxCommandEventHandler(WXSoftKeyPanel::OnKeyChanged)); + + // 初期タイトルを保存しといて.. + title = dynamic_cast(parent)->GetTitle(); + // キーボードの接続状況でタイトルを更新する + UpdateTitle(); } // デストラクタ @@ -83,16 +97,34 @@ WXSoftKeyPanel::~WXSoftKeyPanel() timer.Stop(); - WXUIMessage::Disconnect(UIMessage::KEYBOARD, + WXUIMessage::Disconnect(UIMessage::KEYBOARD, this, wxCommandEventHandler(WXSoftKeyPanel::OnKeyChanged)); } +// キーボードの接続状況によって親フレームのタイトルを更新する。 +// ついでにメンバ変数を更新する。 +void +WXSoftKeyPanel::UpdateTitle() +{ + auto parent = dynamic_cast(GetParent()); + assert(parent); + + // 次回比較用にメンバ変数に覚えておく + connected = keyboard->IsConnected(); + + if (connected) { + parent->SetTitle(title); + } else { + parent->SetTitle(title + _(" (disconnected)")); + } +} + // color.Keytop の初期化(補間)。 // num はキートップの種類の数。1 か 2 void WXSoftKeyPanel::InitColorKeytop(int num) { - wxColour pressed = UD_BLUE; + Color pressed = UD_BLUE; auto f = [](int a, int b, int x) { return a * x / 100 + b * (100 - x) / 100; @@ -103,38 +135,19 @@ WXSoftKeyPanel::InitColorKeytop(int num) for (int i = 0; i < num; i++) { for (int j = 1; j < 10; j++) { int n = j * 100 / 9; - color.Keytop[i][j] = wxColour( - f(pressed.Red(), color.Keytop[i][0].Red(), n), - f(pressed.Green(), color.Keytop[i][0].Green(), n), - f(pressed.Blue(), color.Keytop[i][0].Blue(), n)); + color.Keytop[i][j] = Color( + f(pressed.r, color.Keytop[i][0].r, n), + f(pressed.g, color.Keytop[i][0].g, n), + f(pressed.b, color.Keytop[i][0].b, n)); } } } -// フォントサイズ変更 (外部インタフェース) +// フォントサイズ変更 void -WXSoftKeyPanel::SetFontSize(FontId new_fontid) +WXSoftKeyPanel::FontChanged() { - // 同じなら何もしない - if (fontid == new_fontid) { - return; - } - - // フォントを更新 - UpdateFont(new_fontid); - - // サイズ変更処理 - DoSize(); -} - -// サイズ変更処理 -// サイズを変更したい時に呼ぶ (サイズが変更されたらではない)。 -// ここではサイズは fontsize にのみ依存しているので、フォントサイズが -// 変化/確定した時に呼び出すこと。 -void -WXSoftKeyPanel::DoSize() -{ - assert(fontid != FontId::None); + inherited::FontChanged(); // このパネル内の描画の基本単位 unit = font_height / 4; @@ -148,9 +161,9 @@ WXSoftKeyPanel::DoSize() // | | | | // --+---+---+--(A)--+------(B) // | | | - // | [ | | - // | | Enter | - // --+---+---+--(F)-(E) | + // | [ | Enter | + // | | | + // --+---+---+--(F)-(E) - - (G) // | | | | // | : | ] | | // | | | | @@ -158,32 +171,29 @@ WXSoftKeyPanel::DoSize() // | | // _ | SHIFT | // + // Enter キーの上半分 A-B-G-F を enter_top。F-G の線分を含む。 + // Enter キーの下半分 E-G-C-D を enter_btm。E-G の線分を含む。 const keydata_t& keybs = FindKeydata(KC_BS); const keydata_t& keyleft = FindKeydata(KC_bracketleft); const keydata_t& keyright = FindKeydata(KC_bracketright); const keydata_t& keyscore = FindKeydata(KC_underscore); - // 各点のピクセル座標 - enter_points = { - { keyleft.right() * unit, keyleft.y * unit }, // A - { keybs.right() * unit, keyleft.y * unit }, // B - { keybs.right() * unit, keyscore.y * unit }, // C - { keyright.right() * unit, keyscore.y * unit }, // D - { keyright.right() * unit, keyright.y * unit }, // E - { keyleft.right() * unit, keyright.y * unit }, // F - }; - // 当たり判定用のリージョン - enter_region = wxRegion(enter_points.size(), enter_points.data()); + enter_top.SetLTRB( + keyleft.right() * unit, keyleft.y * unit, + keybs.right() * unit, keyright.y * unit); + enter_btm.SetLTRB( + keyright.right() * unit, keyright.y * unit, + keybs.right() * unit, keyscore.y * unit); - // コントロールの大きさを変更 - wxSize size(u_width * unit, u_height * unit); - SetClientSize(size); - SetMinClientSize(size); + // アイコンを変更。参照順序の関係で下のほうにおいてある…。 + InitIcon(); - // ビットマップ作成 - bitmap.Create(size.x, size.y); + // バックバッファの下限を固定する + wxSize size(u_width * unit, u_height * unit); + SetMinBitmapSize(size); - // 再描画を指示 - Refresh(); + // コントロールの大きさを変更 + SetSize(size); + SetMinSize(size); } void @@ -223,32 +233,22 @@ WXSoftKeyPanel::OnKeyChanged(wxCommandEv // ソフトウェアキーボードの KeyUp() で開放された場合のどちらも // ここでクリア。 for (auto& k : toggle) { - if (gKeyboard->IsPressed(k.first) == false) { + if (keyboard->IsPressed(k.first) == false) { k.second = 0; } } - Refresh(); -} + // 接続状況が変わっていたらタイトルを更新。 + if (keyboard->IsConnected() != connected) { + UpdateTitle(); + } -// 描画イベント (UI スレッドから呼ばれる) -void -WXSoftKeyPanel::OnPaint(wxPaintEvent& event) -{ - // メモリ DC に描画 - wxMemoryDC memDC; - memDC.SelectObject(bitmap); - Draw(memDC); - - // 実画面 DC にコピー - wxPaintDC dstDC(this); - wxSize size = bitmap.GetSize(); - dstDC.Blit(0, 0, size.x, size.y, &memDC, 0, 0); + Refresh(); } // 描画本体 void -WXSoftKeyPanel::Draw(wxDC& dc) +WXSoftKeyPanel::Draw() { bool timer_start = false; @@ -260,19 +260,21 @@ WXSoftKeyPanel::Draw(wxDC& dc) if ((2 < persistence[KC_SHIFT_L] && persistence[KC_SHIFT_L] < 10) || (2 < persistence[KC_SHIFT_R] && persistence[KC_SHIFT_R] < 10) || (2 < persistence[KC_SHIFT] && persistence[KC_SHIFT] < 10) || - gKeyboard->IsShift()) + keyboard->IsShift()) { modifier = ModShift; } else { modifier = ModNormal; } - dc.SetPen(wxPen(color.Background)); - dc.SetBrush(wxBrush(color.Background)); - dc.DrawRectangle(wxPoint(0, 0), GetSize()); + auto text_color = color.Text; + + // キーボードが接続されていないときは刻印をグレーにしておく + if (keyboard->IsConnected() == false) { + text_color = UD_GREY; + } + SetTextForeColor(text_color); - dc.SetPen(color.Text); - SetTextForeColor(color.Text); for (const auto& key : keydata) { int c = persistence[key.keycode]; if (0 < c && c < 10) { @@ -281,7 +283,7 @@ WXSoftKeyPanel::Draw(wxDC& dc) timer_start = true; } else { // そうでない場合に限り、現在の状態を問い合わせる - bool is_pressed = gKeyboard->IsPressed(key.keycode); + bool is_pressed = keyboard->IsPressed(key.keycode); if (is_pressed) { // 押されている c = 10; @@ -298,13 +300,14 @@ WXSoftKeyPanel::Draw(wxDC& dc) } SetTextBackColor(color.Keytop[key.coltype][c]); - dc.SetBrush(color.Keytop[key.coltype][c]); - // エンターキーだけ枠の描き方が違うので別処理 - if (__predict_false(key.x < 0)) { - DrawEnterKey(dc); + // エンターキーとブザー表示は描き方が違うので別処理 + if (__predict_false(key.keycode == KC_enter)) { + DrawEnterKey(); + } else if (__predict_false(key.keycode == KC_buzzer)) { + DrawBuzzer(key); } else { - DrawKey(dc, key); + DrawKey(key); } } @@ -316,15 +319,16 @@ WXSoftKeyPanel::Draw(wxDC& dc) } // 指定のキーを一つ (枠から刻印まで) 描画する。 +// palette[FG] はテキスト色 (キーボード非接続ならグレー)。 +// palette[BG] はこのキーの背景色 (押されているか、減光中か、 +// 押されていない時の二色あるかも知れないキートップ色)。 void -WXSoftKeyPanel::DrawKey(wxDC& dc, const keydata_t& key) +WXSoftKeyPanel::DrawKey(const keydata_t& key) { uint attr = TA::Normal; // 枠 - wxPoint pt(key.x * unit, key.y * unit); - wxSize sz(key.w * unit + 1, key.h * unit + 1); - dc.DrawRectangle(pt, sz); + DrawKeyBox(key, palette[0], color.Text); // 刻印を選択 const char *disp = key.disp[modifier]; @@ -342,7 +346,9 @@ WXSoftKeyPanel::DrawKey(wxDC& dc, const // Shift_JIS に変換 wxString utfstr(disp, wxConvUTF8); - const char *sjis = (const char *)utfstr.mb_str(conv); + // mb_str() をキャストせずに一度保持しておく必要がある (scan-build 対策) + auto mbstr = utfstr.mb_str(conv); + const char *sjis = (const char *)mbstr; // 改行があれば2行に分割 char sjis1[8] {}; char sjis2[8] {}; @@ -356,89 +362,210 @@ WXSoftKeyPanel::DrawKey(wxDC& dc, const } else { // 2行に分割。センタリングは1行目の長さだけで調べる。 strncpy(sjis1, sjis, lf - sjis); - strcpy(sjis2, lf + 1); + strlcpy(sjis2, lf + 1, sizeof(sjis2)); sjislen = strlen(sjis1); row = 2; } - // センタリング - int x = key.x + (key.w - sjislen * 2) / 2; - int y = key.y + (key.h - row * 4) / 2; + // X 方向のセンタリング。 + // unit 単位の計算では精度が足りないのでドット単位で計算する。 + int x = key.x * unit + (key.w - sjislen * 2) * unit / 2 + 1; + // Y 方向のセンタリングは、端数が出ないのと LED 用オフセットが + // unit 単位なので、unit 単位のまま計算して、後でドット単位にする。 + int y = key.y + (key.h - row * 4) / 2; // LED を描画 (機種によって色々違うので個別クラス側で処理) if (key.led >= 0) { - const wxPen oldpen = dc.GetPen(); - const wxBrush oldbrush = dc.GetBrush(); - - y += DrawLED(dc, key); - - dc.SetPen(oldpen); - dc.SetBrush(oldbrush); + y += DrawLED(key); } + y = y * unit + 1; // 文字を描画 - pt = wxPoint(x * unit + 1, y * unit + 1); if (strcmp(disp, EnterMark) == 0) { - DrawEnterMark(dc, pt); + bitmap.DrawBitmapI1(x, y, *enter_icon, palette); } else { if (lf == NULL) { // 1行の場合 - DrawStringSJIS(dc, pt.x, pt.y, sjis, attr); + DrawStringSJIS(x, y, sjis, attr); } else { // 2行の場合 - DrawStringSJIS(dc, pt.x, pt.y, sjis1); - pt.y += 4 * unit; - DrawStringSJIS(dc, pt.x, pt.y, sjis2); + DrawStringSJIS(x, y, sjis1); + y += 4 * unit; + DrawStringSJIS(x, y, sjis2); } } } +// キーの枠と背景色を描画する。 +void +WXSoftKeyPanel::DrawKeyBox(const keydata_t& key, Color bg, Color bd) +{ + Rect rect( + key.x * unit, key.y * unit, + key.w * unit + 1, key.h * unit + 1); + bitmap.FillRect(bg, rect); + bitmap.DrawRect(bd, rect); +} + // エンターキー void -WXSoftKeyPanel::DrawEnterKey(wxDC& dc) +WXSoftKeyPanel::DrawEnterKey() { - // エンターキーを描画 - dc.DrawPolygon(enter_points.size(), enter_points.data()); + // 矩形の内側を背景色で塗りつぶす。 + bitmap.FillRect(palette[0], enter_top); + bitmap.FillRect(palette[0], enter_btm); + + // 周囲の線分 + // (A)-(B) + bitmap.DrawLineH(color.Text, + enter_top.x, enter_top.y, enter_top.GetRight()); + // (B)-(C) + bitmap.DrawLineV(color.Text, + enter_top.GetRight(), enter_top.y, enter_btm.GetBottom()); + // (C)-(D) + bitmap.DrawLineH(color.Text, + enter_btm.GetRight(), enter_btm.GetBottom(), enter_btm.x); + // (D)-(E) + bitmap.DrawLineV(color.Text, + enter_btm.x, enter_btm.GetBottom(), enter_btm.y); + // (E)-(F) + bitmap.DrawLineH(color.Text, + enter_btm.x, enter_top.GetBottom(), enter_top.x); + // (F)-(A) + bitmap.DrawLineV(color.Text, + enter_top.x, enter_top.GetBottom(), enter_top.y); // エンター記号を描画 - DrawEnterMark(dc, wxPoint( - (enter_points[0].x + enter_points[1].x) / 2 - (4 / 2), - (enter_points[1].y + enter_points[2].y) / 2 - (4 / 2))); -} - -// 指定の位置にエンター記号を描画する (エンターキー全体の描画ではない)。 -// メインキーとテンキーの両方で同じ図柄を使う。 -// 大きさは (3unit, 4unit) なので実質全角1文字分くらい。 -void -WXSoftKeyPanel::DrawEnterMark(wxDC& dc, const wxPoint& basep) -{ - // エンター記号は幅 3 unit、高さ 4 unit。 - // +0 +1 +2 +3 - // +0 .. .. .. 1. - // .. .. .. 1. - // +1 .. .. .. 1. - // .. .. .. 1. - // +2 .. 1. .. 1. - // .1 .. .. 1. - // +3 11 11 11 1. - // .1 .. .. .. - // +4 .. 1. .. .. - // .. .. .. .. - static const std::vector> entermark { - { 3, 0 }, - { 3, 3 }, // │ - { 0, 3 }, // ─ - { 1, 2 }, // / - { 0, 3 }, // (一筆書きなので戻る) - { 1, 4 }, // \ - }; - for (int i = 1; i < entermark.size(); i++) { - auto start = entermark[i - 1]; - auto end = entermark[i]; - wxPoint startp(start.first * unit, start.second * unit); - wxPoint endp(end.first * unit, end.second * unit); - dc.DrawLine(basep + startp, basep + endp); + bitmap.DrawBitmapI1( + (enter_top.x + enter_top.GetRight()) / 2 - (4 / 2), + (enter_top.y + enter_btm.GetBottom()) / 2 - (4 / 2), + *enter_icon, palette); +} + +// ブザー表示(キーに相当)を描画 +void +WXSoftKeyPanel::DrawBuzzer(const keydata_t& key) +{ + Color fg; + + // 枠。キーではないので薄くしておく。 + DrawKeyBox(key, palette[0], UD_GREY); + + // テキスト色は本来呼び出し元の DrawKey() で設定してあるが、 + // ここはキーではないので非発声時の文字色だけ薄くしたい。 + // 発声時はキー押下と同じにする。 + fg = GetTextForeColor(); + if (persistence[key.keycode] == 0) { + SetTextForeColor(UD_GREY); } + + // 刻印 (モディファイヤの影響を受けないと分かっているので常に [0]) + const char *disp = key.disp[0]; + assert(disp); + + // センタリング + int x = key.x + (key.w - strlen(disp) * 2) / 2; + int y = key.y + (key.h - 4) / 2; + + // 文字を描画 + x = x * unit + 1; + y = y * unit + 1; + DrawStringSJIS(x, y, disp, TA::Normal); + + // 前景色を元に戻す + SetTextForeColor(fg); +} + +#define B(x) (0b##x) +#define B2(x) (B(x) >> 8), (B(x) & 0xff) +#define B3(x) (B(x) >> 16), ((B(x) >> 8) & 0xff), (B(x) & 0xff) + +static const uint8 EnterIcon12[] = { // 12x12 + B2(000000000000'0000), // 0 + B2(000000001110'0000), // 1 + B2(000000001010'0000), // 2 + B2(000000001010'0000), // 3 + B2(000000001010'0000), // 4 + B2(001100001010'0000), // 5 + B2(010111110010'0000), // 6 + B2(100000000010'0000), // 7 + B2(010111111100'0000), // 8 + B2(001100000000'0000), // 9 + B2(000000000000'0000), // a + B2(000000000000'0000), // b +}; + +static const uint8 EnterIcon16[] = { // 16x16 + B2(0000000000111110), // 0 + B2(0000000000100010), // 1 + B2(0000000000100010), // 2 + B2(0000000000100010), // 3 + B2(0000000000100010), // 4 + B2(0000000000100010), // 5 + B2(0000000000100010), // 6 + B2(0000110000100010), // 7 + B2(0001010000100010), // 8 + B2(0010011111000010), // 9 + B2(0100000000000010), // a + B2(1000000000000010), // b + B2(0100000000000010), // c + B2(0010011111111100), // d + B2(0001010000000000), // e + B2(0000110000000000), // f +}; + +static const uint8 EnterIcon24[] = { // 24x24 + B3(00000000'00000001'11111100), // 0 + B3(00000000'00000001'11111100), // 1 + B3(00000000'00000001'10001100), // 2 + B3(00000000'00000001'10001100), // 3 + B3(00000000'00000001'10001100), // 4 + B3(00000000'00000001'10001100), // 5 + B3(00000000'00000001'10001100), // 6 + B3(00000000'00000001'10001100), // 7 + B3(00000000'00000001'10001100), // 8 + B3(00000000'11000001'10001100), // 9 + B3(00000001'11000001'10001100), // 10 + B3(00000011'11000001'10001100), // 11 + B3(00000110'11000001'10001100), // 12 + B3(00001100'11111111'10001100), // 13 + B3(00011000'11111111'10001100), // 14 + B3(00110000'00000000'00001100), // 15 + B3(01100000'00000000'00001100), // 16 + B3(00110000'00000000'00001100), // 17 + B3(00011000'11111111'11111100), // 18 + B3(00001100'11111111'11111000), // 19 + B3(00000110'11000000'00000000), // 20 + B3(00000011'11000000'00000000), // 21 + B3(00000001'11000000'00000000), // 22 + B3(00000000'11000000'00000000), // 23 +}; + +// アイコンを初期化する。FontChanged() から呼ばれる。 +// ソース先頭のほうにデータの羅列を置きたくないが、 +// 容量不詳の配列の解決が面倒なのでこれだけ下のほうのここに置いてある。 +void +WXSoftKeyPanel::InitIcon() +{ + const int width = font_width; + const int height = font_height; + const uint8 *icon; + + switch (height) { + case 12: + icon = EnterIcon12; + break; + case 16: + icon = EnterIcon16; + break; + case 24: + icon = EnterIcon24; + break; + default: + PANIC("Unexpected font_height=%d", height); + } + + enter_icon.reset(new BitmapI1(icon, width * 2, height)); } // keycode に対応する keydata を返す @@ -451,7 +578,7 @@ WXSoftKeyPanel::FindKeydata(uint keycode } } // 見付からないはずはない - assertmsg(0, "keycode %d not found", keycode); + PANIC("keycode %d not found", keycode); } // マウス左クリック (Down) イベント @@ -475,7 +602,7 @@ WXSoftKeyPanel::DoLeftDown(uint code) // トグルキーはここで完結。LeftUp では何もしない。 // second のクリアは KeyUp() からコールバックされる OnKeyChanged() // で行っている。 - if (gKeyboard->IsPressed(code)) { + if (keyboard->IsPressed(code)) { KeyUp(code); } else { KeyDown(code); @@ -544,7 +671,7 @@ WXSoftKeyPanel::OnRightDown(wxMouseEvent if (key != NULL) { uint code = key->keycode; - if (gKeyboard->IsPressed(code)) { + if (keyboard->IsPressed(code)) { // 押されていたら開放 KeyUp(code); } else { @@ -560,7 +687,7 @@ const keydata_t * WXSoftKeyPanel::GetKeydataFromPoint(const wxPoint& pt) const { // エンターキーだけ先に別判定 - if (enter_region.Contains(pt) == wxInRegion) { + if (enter_top.Contains(pt.x, pt.y) || enter_btm.Contains(pt.x, pt.y)) { return &FindKeydata(KC_enter); } @@ -586,7 +713,7 @@ void WXSoftKeyPanel::KeyDown(uint code) { // VM に通知 (Keyboard がホストに通知する) - gKeyboard->MakeKey(code); + keyboard->MakeKey(code); } // このキーが放された処理 @@ -594,27 +721,23 @@ void WXSoftKeyPanel::KeyUp(uint code) { // VM に通知 (Keyboard がホストに通知する) - gKeyboard->BreakKey(code); + keyboard->BreakKey(code); } - -// -// LUNA ソフトウェアキーボード (パネル) -// - -// コンストラクタ -WXLunaSoftKeyPanel::WXLunaSoftKeyPanel(wxWindow *parent) - : inherited(parent, keydata, 226, 54) +// LUNA-I、LUNA-88K のコンストラクタ共通部分。 +// クラス階層を増やすのも手間なので。 +void +WXSoftKeyPanel::InitLunaCommon() { // LUNA では // BS キーの左隣 (0x5c) は円記号ではなくバックスラッシュ。 // それを SHIFT したやつ(0x7c) は破線でないパイプ。 // そのもう一つ左隣 (0x7e) はオーバーラインではなくチルダ。 - SetGlyph5C(true); - SetGlyph7E(true); + gFontManager->SetGlyph5C(true); + gFontManager->SetGlyph7E(true); // 白黒モデル共通 - color.Border = *wxBLACK; + color.Border = UD_BLACK; color.LED[0] = UD_RED; // キートップの濃淡はユニバーサルデザインの推奨色ではないけど @@ -622,73 +745,107 @@ WXLunaSoftKeyPanel::WXLunaSoftKeyPanel(w if (1) { // 白モデル color.Background = UD_LIGHT_GREY; // 現物新品は何色だったんだろう - color.Text = *wxBLACK; - color.Keytop[0][0] = *wxWHITE; - color.Keytop[1][0] = wxColour(200, 200, 203); + color.Text = UD_BLACK; + color.Keytop[0][0] = UD_WHITE; + color.Keytop[1][0] = Color(200, 200, 203); } else { // 黒モデル - color.Background = *wxBLACK; - color.Text = *wxWHITE; - color.Keytop[0][0] = wxColour(50, 50, 53); - color.Keytop[1][0] = *wxBLACK; + color.Background = UD_BLACK; + color.Text = UD_WHITE; + color.Keytop[0][0] = Color(50, 50, 53); + color.Keytop[1][0] = UD_BLACK; } InitColorKeytop(2); + + SetBitmapBGColor(color.Background); + Fill(); +} + + +// +// LUNA-I ソフトウェアキーボード (パネル) +// + +// コンストラクタ +WXLuna1SoftKeyPanel::WXLuna1SoftKeyPanel(wxWindow *parent) + : inherited(parent, keydata, 226, 54) +{ + InitLunaCommon(); } // デストラクタ -WXLunaSoftKeyPanel::~WXLunaSoftKeyPanel() +WXLuna1SoftKeyPanel::~WXLuna1SoftKeyPanel() { } // LED を描画して刻印描画のための y offset を返す。 int -WXLunaSoftKeyPanel::DrawLED(wxDC& dc, const keydata_t& key) +WXLuna1SoftKeyPanel::DrawLED(const keydata_t& key) { - const std::vector& led = gKeyboard->GetLED(); + const std::vector& led = keyboard->GetLED(); + Color fg; + Color bg; if (led[key.led]) { // 点灯時は赤、枠は黒 - dc.SetPen(color.Border); - dc.SetBrush(color.LED[0]); + fg = color.Border; + bg = color.LED[0]; } else { // 消灯時は元のキートップの色で、枠は薄めにしたい // (枠の色は今の所全モデル共通なので ColorPalette に含めていない) - dc.SetPen(UD_GREY); - dc.SetBrush(color.Keytop[key.coltype][0]); + fg = UD_GREY; + bg = color.Keytop[key.coltype][0]; } // LED を左肩に描く - wxPoint pt = wxPoint(key.x * unit + 2, key.y * unit + 2); - wxSize sz = wxSize(4 * unit - 2, 3 * unit - 2); - dc.DrawRectangle(pt, sz); + Rect rect(key.x * unit + 2, key.y * unit + 2, 4 * unit - 2, 3 * unit - 2); + bitmap.FillRect(bg, rect); + bitmap.DrawRect(fg, rect); // そのため文字は少し下げる return +1; } -// キーデータ +// LUNA-I のファンクションキーは ESC 行との間が少し空いている。 +// LUNA-88K のファンクションキー行は ESC 行とぴったりついていて、キーの +// 縦の長さも他のキーと同じ感じ。 +// X68030 のファンクションキーは ESC 行との間が空いていて、キーの長さは +// 他より短い。 +// +// 2 - +--------+ +// 4 - | LUNA-I | +-------+ +--------+ +// 6 - | | | 88K | | X68030 | +// 8 - | | | | | | +// 10 - +--------+ | | +--------+ +// 12 - +--------+ +-------+ +--------+ <- ESC 行の上辺 +// 14 - | ESC | | 1 | | 2 | +// #define H (8) // キーの高さ -#define L0 (2) // ファンクションキーの行 +#define LH (2) // ファンクションキーの行 (ESC 行との間に空き) +#define L0 (4) // ファンクションキーは少し低い(横長い) #define L1 (12) // ESC の行 #define L2 (20) // TAB の行 #define L3 (28) // CTRL の行 #define L4 (36) // SHIFT の行 #define L5 (44) // space の行 + +// キーデータ /*static*/ std::vector -WXLunaSoftKeyPanel::keydata { +WXLuna1SoftKeyPanel::keydata { // +---- キートップの色(1:濃) // | +- 0 以上なら LED 番号 // X Y W H C L KeyCode 通常 SHIFT - { 2, L0, 15, H, 1,-1, KC_F1, { "PF1" } }, - { 17, L0, 15, H, 1,-1, KC_F2, { "PF2" } }, - { 32, L0, 15, H, 1,-1, KC_F3, { "PF3" } }, - { 47, L0, 15, H, 1,-1, KC_F4, { "PF4" } }, - { 62, L0, 15, H, 1,-1, KC_F5, { "PF5" } }, - { 81, L0, 15, H, 1,-1, KC_F6, { "PF6" } }, - { 96, L0, 15, H, 1,-1, KC_F7, { "PF7" } }, - { 111, L0, 15, H, 1,-1, KC_F8, { "PF8" } }, - { 126, L0, 15, H, 1,-1, KC_F9, { "PF9" } }, - { 141, L0, 15, H, 1,-1, KC_F10, { "PF10" } }, + { 2, LH, 15, H, 1,-1, KC_F1, { "PF1" } }, + { 17, LH, 15, H, 1,-1, KC_F2, { "PF2" } }, + { 32, LH, 15, H, 1,-1, KC_F3, { "PF3" } }, + { 47, LH, 15, H, 1,-1, KC_F4, { "PF4" } }, + { 62, LH, 15, H, 1,-1, KC_F5, { "PF5" } }, + { 81, LH, 15, H, 1,-1, KC_F6, { "PF6" } }, + { 96, LH, 15, H, 1,-1, KC_F7, { "PF7" } }, + { 111, LH, 15, H, 1,-1, KC_F8, { "PF8" } }, + { 126, LH, 15, H, 1,-1, KC_F9, { "PF9" } }, + { 141, LH, 15, H, 1,-1, KC_F10, { "PF10" } }, + { 210, LH+1, 14, H-2, 1,-1, KC_buzzer, { "Buzzer" } }, { 2, L1, 12, H, 1,-1, KC_ESC, { "ESC" } }, { 14, L1, 10, H, 0,-1, KC_1, { "1", "!" } }, @@ -797,6 +954,214 @@ WXLunaSoftKeyPanel::keydata { // +// LUNA-88K ソフトウェアキーボード (パネル) +// + +// コンストラクタ +WXLuna88kSoftKeyPanel::WXLuna88kSoftKeyPanel(wxWindow *parent) + : inherited(parent, keydata, 226, 54) +{ + InitLunaCommon(); +} + +// デストラクタ +WXLuna88kSoftKeyPanel::~WXLuna88kSoftKeyPanel() +{ +} + +// LED を描画する。 +// 独立 LED なので戻り値は使わないので 0 でいい。 +int +WXLuna88kSoftKeyPanel::DrawLED(const keydata_t& basekey) +{ + const std::vector& led = keyboard->GetLED(); + Color fg_backup; + Color bg_backup; + Color fg; + TA attr; + + fg_backup = GetTextForeColor(); + bg_backup = GetTextBackColor(); + + if (led[basekey.led]) { + // 点灯時、文字は LED 色でボールド。 + fg = color.LED[0]; + attr = TA::Bold; + } else { + // 消灯時、文字はグレーで通常体。 + fg = UD_GREY; + attr = TA::Normal; + } + SetTextForeColor(fg); + // 背景はキーの押し下げ状態ではなく常にキー開放色のままとする。 + SetTextBackColor(color.Keytop[1][0]); + + // LUNA-88K キーボードは LED 入りキーではなく、別のところに + // LED だけがあるタイプ。(今どきの PC キーボードに近い) + static const keydata_t led_caps = { + 184, L0+1, 10, H-2, 1, -1, 0, { "Caps" } + }; + static const keydata_t led_kana = { + 194, L0+1, 10, H-2, 1, -1, 0, { "Kana" } + }; + const keydata_t& key = (basekey.keycode == KC_CAPS) + ? led_caps + : led_kana; + + // 枠を描画。 + // 2つの LED で枠が重なるため状態によって色を変えられない。 + DrawKeyBox(key, color.Keytop[1][0], UD_GREY); + + // ラベルをセンタリング。 + const char *disp = key.disp[0]; + int x = key.x + (key.w - strlen(disp) * 2) / 2; + int y = key.y + (key.h - 4) / 2; + + // ラベルを描画。 + x = x * unit + 1; + y = y * unit + 1; + DrawStringSJIS(x, y, disp, attr); + + // 前景色、背景色を元に戻す。 + SetTextForeColor(fg_backup); + SetTextBackColor(bg_backup); + + // 元のキーの文字描画オフセットは無し。 + return 0; +} + + +// キーデータ +/*static*/ std::vector +WXLuna88kSoftKeyPanel::keydata { + // +---- キートップの色(1:濃) + // | +- 0 以上なら LED 番号 + // X Y W H C L KeyCode 通常 SHIFT + { 2, L0, 10, H, 1,-1, KC_F1, { "PF1" } }, + { 12, L0, 10, H, 1,-1, KC_F2, { "PF2" } }, + { 22, L0, 10, H, 1,-1, KC_F3, { "PF3" } }, + { 32, L0, 10, H, 1,-1, KC_F4, { "PF4" } }, + { 42, L0, 10, H, 1,-1, KC_F5, { "PF5" } }, + { 52, L0, 10, H, 1,-1, KC_F6, { "PF6" } }, + { 62, L0, 10, H, 1,-1, KC_F7, { "PF7" } }, + { 72, L0, 10, H, 1,-1, KC_F8, { "PF8" } }, + { 82, L0, 10, H, 1,-1, KC_F9, { "PF9" } }, + { 92, L0, 10, H, 1,-1, KC_F10, { "PF10" } }, + { 102, L0, 10, H, 1,-1, KC_PF11, { "消去" } }, + { 112, L0, 10, H, 1,-1, KC_PF12, { "呼出" } }, + { 122, L0, 10, H, 1,-1, KC_PF13, { "文節\n←" } }, + { 132, L0, 10, H, 1,-1, KC_PF14, { "文節\n→" } }, + { 142, L0, 14, H, 1,-1, KC_BS, { "BS" } }, + + { 210, L0+1, 14, H-2, 1,-1, KC_buzzer, { "Buzzer" } }, + + { 2, L1, 12, H, 1,-1, KC_ESC, { "ESC" } }, + { 14, L1, 10, H, 0,-1, KC_1, { "1", "!" } }, + { 24, L1, 10, H, 0,-1, KC_2, { "2", "\"" } }, + { 34, L1, 10, H, 0,-1, KC_3, { "3", "#" } }, + { 44, L1, 10, H, 0,-1, KC_4, { "4", "$" } }, + { 54, L1, 10, H, 0,-1, KC_5, { "5", "%" } }, + { 64, L1, 10, H, 0,-1, KC_6, { "6", "&" } }, + { 74, L1, 10, H, 0,-1, KC_7, { "7", "'" } }, + { 84, L1, 10, H, 0,-1, KC_8, { "8", "(" } }, + { 94, L1, 10, H, 0,-1, KC_9, { "9", ")" } }, + { 104, L1, 10, H, 0,-1, KC_0, { "0", " " } }, + { 114, L1, 10, H, 0,-1, KC_minus, { "-", "=" } }, + { 124, L1, 10, H, 0,-1, KC_circum, { "^", "~" } }, + { 134, L1, 10, H, 0,-1, KC_backslash, { "\\", "|" } }, + { 144, L1, 12, H, 1,-1, KC_DEL, { "DEL" } }, + + { 2, L2, 17, H, 1,-1, KC_TAB, { "TAB" } }, + { 19, L2, 10, H, 0,-1, KC_Q, { "q", "Q" } }, + { 29, L2, 10, H, 0,-1, KC_W, { "w", "W" } }, + { 39, L2, 10, H, 0,-1, KC_E, { "e", "E" } }, + { 49, L2, 10, H, 0,-1, KC_R, { "r", "R" } }, + { 59, L2, 10, H, 0,-1, KC_T, { "t", "T" } }, + { 69, L2, 10, H, 0,-1, KC_Y, { "y", "Y" } }, + { 79, L2, 10, H, 0,-1, KC_U, { "u", "U" } }, + { 89, L2, 10, H, 0,-1, KC_I, { "i", "I" } }, + { 99, L2, 10, H, 0,-1, KC_O, { "o", "O" } }, + { 109, L2, 10, H, 0,-1, KC_P, { "p", "P" } }, + { 119, L2, 10, H, 0,-1, KC_at, { "@", "`" } }, + { 129, L2, 10, H, 0,-1, KC_bracketleft, { "[", "{" } }, + + { 2, L3, 20, H, 1,-1, KC_CTRL, { "CTRL" } }, + { 22, L3, 10, H, 0,-1, KC_A, { "a", "A" } }, + { 32, L3, 10, H, 0,-1, KC_S, { "s", "S" } }, + { 42, L3, 10, H, 0,-1, KC_D, { "d", "D" } }, + { 52, L3, 10, H, 0,-1, KC_F, { "f", "F" } }, + { 62, L3, 10, H, 0,-1, KC_G, { "g", "G" } }, + { 72, L3, 10, H, 0,-1, KC_H, { "h", "H" } }, + { 82, L3, 10, H, 0,-1, KC_J, { "j", "J" } }, + { 92, L3, 10, H, 0,-1, KC_K, { "k", "K" } }, + { 102, L3, 10, H, 0,-1, KC_L, { "l", "L" } }, + { 112, L3, 10, H, 0,-1, KC_semicolon, { ";", "+" } }, + { 122, L3, 10, H, 0,-1, KC_colon, { ":", "*" } }, + { 132, L3, 10, H, 0,-1, KC_bracketright, { "]", "}" } }, + + { 2, L4, 25, H, 1,-1, KC_SHIFT_L, { "SHIFT" } }, + { 27, L4, 10, H, 0,-1, KC_Z, { "z", "Z" } }, + { 37, L4, 10, H, 0,-1, KC_X, { "x", "X" } }, + { 47, L4, 10, H, 0,-1, KC_C, { "c", "C" } }, + { 57, L4, 10, H, 0,-1, KC_V, { "v", "V" } }, + { 67, L4, 10, H, 0,-1, KC_B, { "b", "B" } }, + { 77, L4, 10, H, 0,-1, KC_N, { "n", "N" } }, + { 87, L4, 10, H, 0,-1, KC_M, { "m", "M" } }, + { 97, L4, 10, H, 0,-1, KC_comma, { ",", "<" } }, + { 107, L4, 10, H, 0,-1, KC_period, { ".", ">" } }, + { 117, L4, 10, H, 0,-1, KC_slash, { "/", "?" } }, + { 127, L4, 10, H, 0,-1, KC_underscore, { " ", "_" } }, + { 137, L4, 19, H, 1,-1, KC_SHIFT_R, { "SHIFT" } }, + + { 2, L5, 10, H, 1,-1, KC_SF, { "META" } }, + { 12, L5, 20, H, 1,-1, KC_VALID, { "確定" } }, + { 32, L5, 10, H, 1, 1, KC_CAPS, { "CAPS" } }, + { 42, L5, 80, H, 0,-1, KC_space, { " " } }, + { 122, L5, 10, H, 1, 0, KC_kana, { "かな" } }, + { 132, L5, 24, H, 1,-1, KC_XFER, { "変換" } }, + + // カーソルキー島 + { 159, L1, 11, H, 1,-1, KC_INS, { "INS" } }, + { 170, L1, 11, H, 1,-1, KC_COPY, { "COPY" } }, + { 159, L2, 11, H, 1,-1, KC_CUT, { "CUT" } }, + { 170, L2, 11, H, 1,-1, KC_PASTE, { "PASTE" } }, + { 159, L3, 22, H, 1,-1, KC_up, { "↑" } }, + { 159, L4, 11, H, 1,-1, KC_left, { "←" } }, + { 170, L4, 11, H, 1,-1, KC_right, { "→" } }, + { 159, L5, 22, H, 1,-1, KC_down, { "↓" } }, + + // テンキー島 + { 184, L1, 10, H, 1,-1, KC_HOME, { "HOME" } }, + { 194, L1, 10, H, 1,-1, KC_PAD_plus, { "+" } }, + { 204, L1, 10, H, 1,-1, KC_PAD_minus, { "-" } }, + { 214, L1, 10, H, 1,-1, KC_PAD_multiply, { "*" } }, + + { 184, L2, 10, H, 0,-1, KC_PAD_7, { "7" } }, + { 194, L2, 10, H, 0,-1, KC_PAD_8, { "8" } }, + { 204, L2, 10, H, 0,-1, KC_PAD_9, { "9" } }, + { 214, L2, 10, H, 1,-1, KC_PAD_divide, { "/" } }, + + { 184, L3, 10, H, 0,-1, KC_PAD_4, { "4" } }, + { 194, L3, 10, H, 0,-1, KC_PAD_5, { "5" } }, + { 204, L3, 10, H, 0,-1, KC_PAD_6, { "6" } }, + { 214, L3, 10, H, 1,-1, KC_PAD_equal, { "=" } }, + + { 184, L4, 10, H, 0,-1, KC_PAD_1, { "1" } }, + { 194, L4, 10, H, 0,-1, KC_PAD_2, { "2" } }, + { 204, L4, 10, H, 0,-1, KC_PAD_3, { "3" } }, + { 214, L4, 10, H, 1,-1, KC_PAD_comma, { "," } }, + + { 184, L5, 10, H, 0,-1, KC_PAD_0, { "0" } }, + { 194, L5, 10, H, 0,-1, KC_PAD_decimal, { "." } }, + { 204, L5, 20, H, 1,-1, KC_PAD_enter, { EnterMark } }, + + // Enter だけ矩形でないので別処理するが + // この構造体には全部のキー情報が統一的に並んでいるほうがよかろう。 + { -1, -1, -1, -1, 1,-1, KC_enter, { EnterMark } }, +}; + + +// // X68k ソフトウェアキーボード (パネル) // @@ -805,19 +1170,23 @@ WXX68kSoftKeyPanel::WXX68kSoftKeyPanel(w : inherited(parent, keydata, 235, 54) { // X68k では SRAM 設定に同期させる。 - uint8 sw = gSRAM->Peek8(0xed0059); - SetGlyph5C((sw & 0x01)); - SetGlyph7E((sw & 0x02)); - SetGlyph7C((sw & 0x04)); + const auto sram = GetSRAMDevice(); + uint8 sw = sram->Peek1(0xed0059); + gFontManager->SetGlyph5C((sw & 0x01)); + gFontManager->SetGlyph7E((sw & 0x02)); + gFontManager->SetGlyph7C((sw & 0x04)); // とりあえず黒のみ - color.Background = wxColour(50, 50, 53); - color.Text = *wxWHITE; - color.Border = *wxBLACK; + color.Background = Color(50, 50, 53); + color.Text = UD_WHITE; + color.Border = UD_BLACK; color.LED[0] = UD_RED; color.LED[1] = UD_YELLOW_GREEN; - color.Keytop[0][0] = *wxBLACK; + color.Keytop[0][0] = UD_BLACK; InitColorKeytop(1); + + SetBitmapBGColor(color.Background); + Fill(); } // デストラクタ @@ -827,9 +1196,11 @@ WXX68kSoftKeyPanel::~WXX68kSoftKeyPanel( // LED を描画して刻印描画のための y offset を返す。 int -WXX68kSoftKeyPanel::DrawLED(wxDC& dc, const keydata_t& key) +WXX68kSoftKeyPanel::DrawLED(const keydata_t& key) { - const std::vector& led = gKeyboard->GetLED(); + const std::vector& led = keyboard->GetLED(); + Color fg; + Color bg; // 色を選択 // idx 刻印 色 @@ -843,42 +1214,42 @@ WXX68kSoftKeyPanel::DrawLED(wxDC& dc, co // 6 全角 緑 if (led[key.led]) { int n = (key.led < 5) ? 0 : 1; - dc.SetPen(color.LED[n]); - dc.SetBrush(color.LED[n]); + fg = color.LED[n]; + bg = color.LED[n]; } else { // 消灯時は元のキートップの色で、枠は薄めにしたい // (枠の色は今の所全モデル共通なので ColorPalette に含めていない) - dc.SetPen(UD_GREY); - dc.SetBrush(color.Keytop[key.coltype][0]); + fg = UD_GREY; + bg = color.Keytop[key.coltype][0]; } // LED は中央前方(画面でいうと下) - wxPoint pt = wxPoint((key.x + 3) * unit, (key.y + key.h - 2) * unit); - wxSize sz = wxSize(4 * unit, 2 * unit); - dc.DrawRectangle(pt, sz); + Rect rect((key.x + 3) * unit, (key.y + key.h - 2) * unit, + 4 * unit, 2 * unit); + bitmap.FillRect(bg, rect); + bitmap.DrawRect(fg, rect); // そのため文字は少し上げる return -1; } -#define LF (L0 + 2) // ファンクションキーは少し低い(横長い) #define HT (H * 2) // 縦2倍角のキー /*static*/ std::vector WXX68kSoftKeyPanel::keydata { // +- 0以上なら LED 番号 // X Y W H C L KeyCode 通常 SHIFT - { 2, L0, 10, H, 0,-1, KC_BREAK, { "\x4\x5\x6\x7" } }, - { 15, L0, 10, H, 0,-1, KC_COPY, { "COPY" } }, - { 29, LF, 12, 6, 0,-1, KC_F1, { "F1" } }, - { 41, LF, 12, 6, 0,-1, KC_F2, { "F2" } }, - { 53, LF, 12, 6, 0,-1, KC_F3, { "F3" } }, - { 65, LF, 12, 6, 0,-1, KC_F4, { "F4" } }, - { 77, LF, 13, 6, 0,-1, KC_F5, { "F5" } }, - { 94, LF, 13, 6, 0,-1, KC_F6, { "F6" } }, - { 106, LF, 12, 6, 0,-1, KC_F7, { "F7" } }, - { 118, LF, 12, 6, 0,-1, KC_F8, { "F8" } }, - { 130, LF, 12, 6, 0,-1, KC_F9, { "F9" } }, - { 142, LF, 13, 6, 0,-1, KC_F10, { "F10" } }, + { 2, LH, 10, H, 0,-1, KC_BREAK, { "\x4\x5\x6\x7" } }, + { 15, LH, 10, H, 0,-1, KC_COPY, { "COPY" } }, + { 29, L0, 12, 6, 0,-1, KC_F1, { "F1" } }, + { 41, L0, 12, 6, 0,-1, KC_F2, { "F2" } }, + { 53, L0, 12, 6, 0,-1, KC_F3, { "F3" } }, + { 65, L0, 12, 6, 0,-1, KC_F4, { "F4" } }, + { 77, L0, 13, 6, 0,-1, KC_F5, { "F5" } }, + { 94, L0, 13, 6, 0,-1, KC_F6, { "F6" } }, + { 106, L0, 12, 6, 0,-1, KC_F7, { "F7" } }, + { 118, L0, 12, 6, 0,-1, KC_F8, { "F8" } }, + { 130, L0, 12, 6, 0,-1, KC_F9, { "F9" } }, + { 142, L0, 13, 6, 0,-1, KC_F10, { "F10" } }, { 2, L1, 10, H, 0,-1, KC_ESC, { "ESC" } }, { 12, L1, 10, H, 0,-1, KC_1, { "1", "!" } }, @@ -984,7 +1355,7 @@ WXX68kSoftKeyPanel::keydata { { 193, L4, 10, H, 0,-1, KC_PAD_1, { "1" } }, { 203, L4, 10, H, 0,-1, KC_PAD_2, { "2" } }, { 213, L4, 10, H, 0,-1, KC_PAD_3, { "3" } }, - { 223, L4, 10, HT,0,-1, KC_PAD_enter, { "\xb\xc\xd\xe" } }, + { 223, L4, 10, HT,0,-1, KC_PAD_enter, { "\xb\xc\xe\xf" } }, { 193, L5, 10, H, 0,-1, KC_PAD_0, { "0" } }, { 203, L5, 10, H, 0,-1, KC_PAD_comma, { "," } }, { 213, L5, 10, H, 0,-1, KC_PAD_decimal, { "." } }, @@ -994,26 +1365,29 @@ WXX68kSoftKeyPanel::keydata { { -1, -1, -1, -1, 0,-1, KC_enter, { EnterMark } }, }; + // // ソフトウェアキーボード (ウィンドウ) // // コンストラクタ -WXSoftKeyWindow::WXSoftKeyWindow(wxWindow *parent, vmtype_t vmtype) +WXSoftKeyWindow::WXSoftKeyWindow(wxWindow *parent, VMType vmtype) : inherited(parent, wxID_ANY, _("Software Keyboard")) { - wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL); - - if (vmtype == VMTYPE_X68030) { - panel = new WXX68kSoftKeyPanel(this); - } else { - panel = new WXLunaSoftKeyPanel(this); + switch (vmtype) { + case VMType::X68030: + new WXX68kSoftKeyPanel(this); + break; + case VMType::LUNA1: + new WXLuna1SoftKeyPanel(this); + break; + case VMType::LUNA88K: + new WXLuna88kSoftKeyPanel(this); + break; + default: + assert(false); } - topsizer->Add(panel, 0, wxEXPAND); - - SetSizer(topsizer); - // 大きさをそれに固定 Fit(); }