--- nono/wx/wxplanemonitor.cpp 2026/04/29 17:05:12 1.1.1.1 +++ nono/wx/wxplanemonitor.cpp 2026/04/29 17:05:16 1.1.1.2 @@ -11,7 +11,7 @@ #include "wxplanemonitor.h" #include "wxcolor.h" #include "wxuimessage.h" -#include "bt454.h" +#include "bt45x.h" #include "planevram.h" #include "videoctlr.h" @@ -37,17 +37,30 @@ static_assert(ID_local_end - 1 <= (int)I // プレーン VRAM ビットマップ // +// イベントテーブル +wxBEGIN_EVENT_TABLE(WXPlaneVRAMBitmapPanel, inherited) + EVT_MOUSE_EVENTS(WXPlaneVRAMBitmapPanel::OnMouse) +wxEND_EVENT_TABLE() + // コンストラクタ WXPlaneVRAMBitmapPanel::WXPlaneVRAMBitmapPanel(wxWindow *parent) : inherited(parent) { - plane_width = gPlaneVRAM->GetComposite().GetWidth(); - plane_height = gPlaneVRAM->GetComposite().GetHeight(); + // デバイスを取得 + planevram = GetPlaneVRAMDevice(); + // どちらか一方は NULL のはず + bt45x = gMainApp.FindObject(OBJ_BT45x); + videoctlr = gMainApp.FindObject(OBJ_VIDEOCTLR); + + plane_width = planevram->GetComposite().GetWidth(); + plane_height = planevram->GetComposite().GetHeight(); // まず等倍でセット scale = 1; SetScale(1); + cursor = wxDefaultPosition; + // VM からの通知を受け取る WXUIMessage::Connect(UIMessage::PALETTE, this, wxCommandEventHandler(WXPlaneVRAMBitmapPanel::OnPaletteChanged)); @@ -56,7 +69,7 @@ WXPlaneVRAMBitmapPanel::WXPlaneVRAMBitma // デストラクタ WXPlaneVRAMBitmapPanel::~WXPlaneVRAMBitmapPanel() { - WXUIMessage::Disconnect(UIMessage::PALETTE, + WXUIMessage::Disconnect(UIMessage::PALETTE, this, wxCommandEventHandler(WXPlaneVRAMBitmapPanel::OnPaletteChanged)); } @@ -70,7 +83,7 @@ WXPlaneVRAMBitmapPanel::Draw() assert(view.GetRight() < virtual_width); assert(view.GetBottom() < virtual_height); - const BitmapI8& src = gPlaneVRAM->GetComposite(); + const BitmapI8& src = planevram->GetComposite(); if (scale > 1) { Rect dr(0, 0, bitmap.GetWidth(), bitmap.GetHeight()); @@ -120,10 +133,10 @@ WXPlaneVRAMBitmapPanel::GenPalette() // パレットを適用する場合 const ColorXBGR *vmpal; // 雑に X680x0/LUNA 対応 - if (gVideoCtlr) { - vmpal = gVideoCtlr->GetHostPalette(); + if (videoctlr) { + vmpal = &(videoctlr->GetHostPalette())[0]; } else { - vmpal = gBT454->GetHostPalette(); + vmpal = &(bt45x->GetHostPalette())[0]; } const Color bg = vmpal[0].ToColor(); for (int i = 0; i < pal.size(); i++) { @@ -181,15 +194,45 @@ WXPlaneVRAMBitmapPanel::OnScroll(wxScrol } } +// マウスイベント +void +WXPlaneVRAMBitmapPanel::OnMouse(wxMouseEvent& event) +{ + auto window = dynamic_cast(GetGrandParent()); + assert(window); + + // pt はこのパネル左上からの位置 + const wxPoint& pt = event.GetPosition(); + + // pt がパネル内の座標なのに Leaving() になることがある。 + // pt がパネル外の座標なのに Leaving() にならないことがある。 + if (event.Leaving() == false && GetClientRect().Contains(pt)) { + // cursor は仮想画面上の座標 + cursor.x = (view.x + pt.x) / scale; + cursor.y = (view.y + pt.y) / scale; + assertmsg(cursor.x < plane_width, "cursor.x=%d", cursor.x); + assertmsg(cursor.y < plane_height, "cursor.y=%d", cursor.y); + } else { + cursor = wxDefaultPosition; + } +} + // // プレーン VRAM パネル // +// +------------------------+------------------+ +// | WXPlaneVRAMBitmapPanel | WXScrollBar(V) | +// | VRAM 表示パネル | 縦スクロールバー | +// +------------------------+------------------+ +// | WXScrollBar(H) | WXBitmapPanel | +// | 横スクロールバー | 空き地 | +// +------------------------+------------------+ + // イベントテーブル wxBEGIN_EVENT_TABLE(WXPlaneVRAMPanel, inherited) EVT_SIZE(WXPlaneVRAMPanel::OnSize) - EVT_TIMER(wxID_ANY, WXPlaneVRAMPanel::OnTimer) wxEND_EVENT_TABLE() // コンストラクタ @@ -221,10 +264,6 @@ WXPlaneVRAMPanel::WXPlaneVRAMPanel(wxWin wxScrollEventHandler(WXPlaneVRAMBitmapPanel::OnScroll), NULL, viewctl); hscroll->Connect(NONO_EVT_SCROLL, wxScrollEventHandler(WXPlaneVRAMBitmapPanel::OnScroll), NULL, viewctl); - - // 更新タイマーはモニターパネル同様の 30Hz にしておく。 - timer.SetOwner(this); - timer.Start(33); } // デストラクタ @@ -327,13 +366,6 @@ WXPlaneVRAMPanel::DoSize(wxSize size) vscroll->SetScrollParam(vpos, vthumbsize, vrange, vpagesize); } -// タイマーイベント -void -WXPlaneVRAMPanel::OnTimer(wxTimerEvent& event) -{ - Refresh(); -} - // プレーン選択変更 void WXPlaneVRAMPanel::EnablePlane(int plane, bool value) @@ -367,12 +399,21 @@ WXPlaneVRAMPanel::SetScale(int scale_) // プレーン VRAM ウィンドウ // +// ↓+-----------------------------+ +// | wx コントロール用パネル | +// +-----------------------------+ +// | WXTextScreen *statuspanel | ← 情報欄 +// +-----------------------------+ +// | WXPlaneVRAMPanel *viewpanel | ← ビットマップ (スクロールバー込み) +// +-----------------------------+ + // イベントテーブル wxBEGIN_EVENT_TABLE(WXPlaneVRAMWindow, inherited) EVT_COMMAND_RANGE(ID_PLANE0, ID_PLANE7, wxEVT_CHECKBOX, WXPlaneVRAMWindow::OnPlane) EVT_CHECKBOX(ID_PALETTE, WXPlaneVRAMWindow::OnApplyPalette) EVT_CHOICE(ID_SCALE, WXPlaneVRAMWindow::OnScale) + EVT_TIMER(wxID_ANY, WXPlaneVRAMWindow::OnTimer) wxEND_EVENT_TABLE() // コンストラクタ @@ -380,28 +421,48 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW : inherited(parent, wxID_ANY, name, inherited::DEFAULT_STYLE | wxRESIZE_BORDER) { + // デバイスを取得 + planevram = GetPlaneVRAMDevice(); + + int nplane = planevram->GetPlaneCount(); + auto *topsizer = new wxBoxSizer(wxVERTICAL); // 上にコントロールパネル(のまず下敷き) - ctrlpanel = new WXBitmapPanel(this); + ctrlpanel = new wxPanel(this); topsizer->Add(ctrlpanel, 0, wxEXPAND); // コントロールパネル用の横 Sizer auto *ctrlbox = new wxBoxSizer(wxHORIZONTAL); - // プレーン選択チェックボックス (#3, #2, #1, #0 の順に並べる) - for (int i = planesw.size() - 1; i >= 0; i--) { - planesw[i] = new wxCheckBox(ctrlpanel, ID_PLANE(i), - string_format("#%d", i)); - ctrlbox->Add(planesw[i], 0, wxALIGN_CENTER); + // プレーン選択チェックボックス + auto *csbox = new wxStaticBoxSizer(wxHORIZONTAL, ctrlpanel, + _("Planes to display")); + // GTK3 標準のレンダリングだとコントロールの周りの空きがなさすぎて + // 特にスタティックボックスは読みづらいので自力で少し空ける。どうして…。 + ctrlbox->Add(csbox, 0, wxALIGN_CENTER | wxALL, 3); + // X68030 なら 4プレーン。 + // LUNA で 1bpp なら 4プレーン (上位3つは Disabled)、 + // LUNA で 4bpp なら 4プレーン、 + // LUNA で 8bpp なら 8プレーン。 + int nplane4 = (nplane == 1) ? 4 : nplane; + for (int i = 0; i < nplane4; i++) { + auto *ctrl = new wxCheckBox(ctrlpanel, ID_PLANE(i), + string_format("%d", i)); + planesw.push_back(ctrl); + } + // 降順に並べる + for (int i = nplane4 - 1; i >= 0; i--) { + csbox->Add(planesw[i]); } // パレット合成チェックボックス applysw = new wxCheckBox(ctrlpanel, ID_PALETTE, _("Apply palette")); - ctrlbox->Add(applysw, 0, wxALIGN_CENTER); + ctrlbox->Add(applysw, 0, wxALIGN_CENTER | wxALL, 3); // セパレータを出したいのだが… - ctrlbox->Add(new wxStaticText(ctrlpanel, wxID_ANY, "|"), 0, wxALIGN_CENTER); + ctrlbox->Add(new wxStaticText(ctrlpanel, wxID_ANY, "|"), 0, + wxALIGN_CENTER | wxALL, 3); // 倍率 wxString scale_choice[] = { @@ -412,12 +473,15 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW scalesw = new wxChoice(ctrlpanel, ID_SCALE, wxDefaultPosition, wxDefaultSize, countof(scale_choice), scale_choice); - ctrlbox->Add(scalesw, 0, wxALIGN_CENTER); + ctrlbox->Add(scalesw, 0, wxALIGN_CENTER | wxALL, 3); // sizer と下敷きパネルを紐付ける ctrlpanel->SetSizer(ctrlbox); ctrlbox->SetSizeHints(ctrlpanel); + statuspanel = new WXTextScreen(this, nnSize(50, nplane)); + topsizer->Add(statuspanel, 0, wxEXPAND); + viewpanel = new WXPlaneVRAMPanel(this); topsizer->Add(viewpanel, 1, wxEXPAND); @@ -429,8 +493,8 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW SetMinClientSize(minsize); // 最大サイズは DoScale() 内で設定している - // 1bpp ならプレーン選択はすべて無効、4bpp ならすべて有効。 - if (gPlaneVRAM->GetPlaneCount() == 1) { + // 1bpp ならプレーン選択はすべて無効、それ以外はすべて有効。 + if (nplane == 1) { for (int i = 0; i < planesw.size(); i++) { planesw[i]->Enable(false); } @@ -445,7 +509,7 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW applysw->SetValue(sticky_apply); scalesw->SetSelection(sticky_scale); } else { - if (gPlaneVRAM->GetPlaneCount() == 1) { + if (nplane == 1) { // 初回 1bpp なら #0 プレーンのみオン。 for (int i = 0; i < planesw.size(); i++) { planesw[i]->SetValue((i == 0)); @@ -468,6 +532,10 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW } DoApplyPalette(applysw->IsChecked()); DoScale(scalesw->GetSelection()); + + // 更新タイマーはモニターパネル同様の 30Hz にしておく。 + timer.SetOwner(this); + timer.Start(33); } // デストラクタ @@ -545,14 +613,30 @@ WXPlaneVRAMWindow::DoScale(int scale_ind // 最大サイズは表示部の最大サイズから wxSize maxsize = viewpanel->GetMaxClientSize(); maxsize.y += ctrlpanel->GetMinClientSize().y; + maxsize.y += statuspanel->GetMinClientSize().y; SetMaxClientSize(maxsize); // 保存しておくのはインデックスのほう sticky_scale = scale_index; } +// タイマーイベント +void +WXPlaneVRAMWindow::OnTimer(wxTimerEvent& event) +{ + viewpanel->Refresh(); + + // この位置の情報で screen を更新 + TextScreen& screen = statuspanel->GetScreen(); + const wxPoint& cursor = viewpanel->GetCursor(); + + // UpdateInfo() は cursor.x が範囲外の時の描画も行っている。 + planevram->UpdateInfo(screen, cursor.x, cursor.y); + statuspanel->Refresh(); +} + // static 変数 -std::array WXPlaneVRAMWindow::sticky_plane; +std::array WXPlaneVRAMWindow::sticky_plane; bool WXPlaneVRAMWindow::sticky_apply; int WXPlaneVRAMWindow::sticky_scale; bool WXPlaneVRAMWindow::sticky_used;