--- nono/wx/wxplanemonitor.cpp 2026/04/29 17:05:27 1.1.1.5 +++ nono/wx/wxplanemonitor.cpp 2026/04/29 17:05:44 1.1.1.7 @@ -12,6 +12,7 @@ #include "wxcolor.h" #include "wxuimessage.h" #include "bt45x.h" +#include "mainapp.h" #include "planevram.h" #include "videoctlr.h" @@ -190,7 +191,7 @@ WXPlaneVRAMBitmapPanel::SetScale(int sca view.w = view.w * scale / before; view.h = view.h * scale / before; - SetMaxClientSize(wxSize(virtual_width, virtual_height)); + SetMaxSize(wxSize(virtual_width, virtual_height)); } // スクロール位置変更通知 @@ -243,11 +244,6 @@ WXPlaneVRAMBitmapPanel::OnMouse(wxMouseE // | 横スクロールバー | 空き地 | // +------------------------+------------------+ -// イベントテーブル -wxBEGIN_EVENT_TABLE(WXPlaneVRAMPanel, inherited) - EVT_SIZE(WXPlaneVRAMPanel::OnSize) -wxEND_EVENT_TABLE() - // コンストラクタ WXPlaneVRAMPanel::WXPlaneVRAMPanel(wxWindow *parent) : inherited(parent) @@ -264,6 +260,7 @@ WXPlaneVRAMPanel::WXPlaneVRAMPanel(wxWin // Sizer 使わず自前でレイアウトする。 SetAutoLayout(true); + FontChanged(); // スクロールバーからの通知イベントは表示パネルへ回す vscroll->Connect(NONO_EVT_SCROLL, @@ -279,62 +276,6 @@ WXPlaneVRAMPanel::~WXPlaneVRAMPanel() { } -bool -WXPlaneVRAMPanel::Layout() -{ - DPRINTF("Panel.Layout\n"); - if (inherited::Layout() == false) { - return false; - } - - const wxSize size = GetClientSize(); - DPRINTF("Panel.Layout inherited, client=(%d,%d)\n", size.x, size.y); - wxSize vscr = vscroll->GetClientSize(); - wxSize hscr = hscroll->GetClientSize(); - DPRINTF("Panel.Layout vscr=(%d,%d) hscr=(%d,%d)\n", - vscr.x, vscr.y, hscr.x, hscr.y); - // 主方向(スクロール方向) の最小サイズ - int vmin = vscroll->GetMinLength(); - int hmin = hscroll->GetMinLength(); - - // 最小サイズはスクロールバーの大きさで制約がかかる。 - // (VRAM パネルなので実際はもっと広くてもいいかも知れない) - SetMinClientSize(wxSize(hmin + vscr.x, vmin + hscr.y)); - SetMaxClientSize(wxSize(viewctl->virtual_width + vscr.x, - viewctl->virtual_height + hscr.y)); - -#if defined(WINDOW_DEBUG) - const wxSize& min = GetMinClientSize(); - const wxSize& max = GetMaxClientSize(); - const wxSize& cli = GetClientSize(); - printf("Panel.Layout min=(%d,%d) max=(%d,%d) client=(%d,%d)\n", - min.x, min.y, max.x, max.y, cli.x, cli.y); -#endif - - // コントロールを配置 - int view_width = size.x - vscr.x; - int view_height = size.y - hscr.y; - DPRINTF("Panel.Layout view=(%d,%d)\n", view_width, view_height); - // 最低でも 1px ないと GTK とかに怒られる - if (view_width < 1) { - view_width = 1; - } - if (view_height < 1) { - view_height = 1; - } - - viewctl->SetSize(0, 0, view_width, view_height); - vscroll->SetSize(view_width, 0, vscr.x, view_height); - hscroll->SetSize(0, view_height, view_width, hscr.y); - corner->SetSize(view_width, view_height, vscr.x, hscr.y); - - // XXX 内部のコントロールのサイズが確定してからでないと DoSize() できない - DoSize(size); - DPRINTF("Panel.Layout done\n"); - - return true; -} - // フォントサイズ変更 void WXPlaneVRAMPanel::FontChanged() @@ -351,37 +292,63 @@ WXPlaneVRAMPanel::FontChanged() hscroll->FontChanged(); vscroll->FontChanged(); - // レイアウト変更も手動で指示。 - Layout(); + Fit(); + DPRINTF("Panel.FontChanged end\n"); } -// サイズ変更イベント void -WXPlaneVRAMPanel::OnSize(wxSizeEvent& event) +WXPlaneVRAMPanel::Fit() { - // Mac ではウィンドウ作成時に 0 以下の値が来ることがある。 - // GTK では 1 で来ることがある。 - wxSize size = event.GetSize(); - DPRINTF("Panel.OnSize (%d,%d)\n", size.x, size.y); - if (size.x <= 1 || size.y <= 1) { - return; - } + // 最小/最大サイズを再計算。 + wxSize vmin = vscroll->GetMinSize(); + wxSize hmin = hscroll->GetMinSize(); + wxSize minsize(hmin.x + vmin.x, vmin.y + hmin.y); + wxSize maxsize(viewctl->virtual_width + vmin.x, + viewctl->virtual_height + hmin.y); + SetSizeHints(minsize, maxsize); + + // 新しいサイズはビューサイズから決定。 + wxSize viewsize = viewctl->GetSize(); + wxSize newsize(viewsize.x + vmin.x, viewsize.y + hmin.y); + DPRINTF("Panel.Fit (%d,%d) min=(%d,%d) max=(%d,%d)\n", + newsize.x, newsize.y, minsize.x, minsize.y, maxsize.x, maxsize.y); + SetSize(newsize); - // Layout() を呼ぶために必要 - event.Skip(); + GetParent()->Fit(); +} + +bool +WXPlaneVRAMPanel::Layout() +{ + const wxSize size = GetSize(); + DPRINTF("Panel.Layout size=(%d,%d)\n", size.x, size.y); + wxSize vscr = vscroll->GetSize(); + wxSize hscr = hscroll->GetSize(); - // XXX レイアウトが終わった後でないと DoSize() できない -// DoSize(size); + // コントロールを配置。 + int view_width = size.x - vscr.x; + int view_height = size.y - hscr.y; + DPRINTF("Panel.Layout view=(%d,%d)\n", view_width, view_height); + + viewctl->SetSize(0, 0, view_width, view_height); + vscroll->SetSize(view_width, 0, vscr.x, view_height); + hscroll->SetSize(0, view_height, view_width, hscr.y); + corner->SetSize(view_width, view_height, vscr.x, hscr.y); + + SetScroll(); + DPRINTF("Panel.Layout done\n"); + + return true; } // サイズ変更処理。パネルサイズと倍率が変わったら呼ぶこと。 // 倍率が変わった場合もスクロールバーの再設定とかがあるため。 void -WXPlaneVRAMPanel::DoSize(wxSize size) +WXPlaneVRAMPanel::SetScroll() { - wxSize viewsize = viewctl->GetClientSize(); - DPRINTF("Panel.DoSize: view=(%d,%d)\n", viewsize.x, viewsize.y); + wxSize viewsize = viewctl->GetSize(); + DPRINTF("Panel.%s: view=(%d,%d)\n", __func__, viewsize.x, viewsize.y); int virtual_width = viewctl->virtual_width; int virtual_height = viewctl->virtual_height; Rect& view = viewctl->view; @@ -403,7 +370,7 @@ WXPlaneVRAMPanel::DoSize(wxSize size) // 下端も同様 if (view.y > 0 && (view.y + view.h >= virtual_height || - view.y + size.y >= virtual_height)) + view.y + viewsize.y >= virtual_height)) { view.y = virtual_height - viewsize.y; if (view.y < 0) { @@ -449,12 +416,12 @@ WXPlaneVRAMPanel::SetScale(int scale_) { viewctl->SetScale(scale_); - int vscr_width = vscroll->GetClientSize().x; - int hscr_height = hscroll->GetClientSize().y; - SetMaxClientSize(wxSize(viewctl->virtual_width + vscr_width, + int vscr_width = vscroll->GetSize().x; + int hscr_height = hscroll->GetSize().y; + SetMaxSize(wxSize(viewctl->virtual_width + vscr_width, viewctl->virtual_height + hscr_height)); - DoSize(GetSize()); + SetScroll(); } @@ -489,11 +456,9 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW int nplane = planevram->GetPlaneCount(); - auto *topsizer = new wxBoxSizer(wxVERTICAL); - - // 上にコントロールパネル(のまず下敷き) + // 上段、コントロールパネル(のまず下敷き) ctrlpanel = new wxPanel(this); - topsizer->Add(ctrlpanel, 0, wxEXPAND); + ctrlpanel->SetName("WXPlaneVRAMWindow.CtrlPanel"); // コントロールパネル用の横 Sizer auto *ctrlbox = new wxBoxSizer(wxHORIZONTAL); @@ -542,16 +507,23 @@ WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxW ctrlpanel->SetSizer(ctrlbox); ctrlbox->SetSizeHints(ctrlpanel); - // 情報パネルは大きさを固定 - statuspanel = new WXTextScreen(this, nnSize(60, nplane)); - statuspanel->SetMinClientSize(statuspanel->GetDesiredSize()); - topsizer->Add(statuspanel, 0, wxEXPAND); - + // 中段、情報パネルは大きさを固定 + constexpr int status_col = 60; + statuspanel = new WXTextScreen(this, nnSize(status_col, nplane)); + statuspanel->SetMinSize(statuspanel->GetBestSize()); + + // 中段の右側の空き地用。 + spacer = new WXBitmapPanel(this); + + // 下段、表示パネル。 + // 大きさは適当だがフォントサイズを大きくした後で再び小さくすると + // このパネルはどうしたらいいのか難しいのと、最初から広いほうが見やすい + // というのもあるしね…ということにして、とりあえずステータスパネルを + // 最初からフォントサイズ 24 の時の幅にしておいて誤魔化す。高さは適当。 viewpanel = new WXPlaneVRAMPanel(this); - topsizer->Add(viewpanel, 1, wxEXPAND); - - SetSizer(topsizer); + viewpanel->SetSize(wxSize(status_col * 12, status_col * 12 / 2)); + SetAutoLayout(true); FontChanged(); // 1bpp ならプレーン選択はすべて無効、それ以外はすべて有効。 @@ -607,32 +579,118 @@ WXPlaneVRAMWindow::~WXPlaneVRAMWindow() } void -WXPlaneVRAMWindow::FontChanged() +WXPlaneVRAMWindow::Fit() { - DPRINTF("Window.FontChanged\n"); - inherited::FontChanged(); - DPRINTF("Window.FontChanged inherited\n"); + // 全部準備出来る前に呼ばれることがある。spacer は見ないので不要。 + if (ctrlpanel == NULL || statuspanel == NULL || viewpanel == NULL) { + return; + } + + // 最大最小サイズを設定。 + DoSizeHints(); - // 子コントロールのサイズが変わったのでウィンドウサイズを再計算。 - // 最小サイズは上コントロール部と情報パネルの横幅のうち大きい方が基準。 - wxSize minsize = ctrlpanel->GetMinClientSize(); - const wxSize& statsize = statuspanel->GetDesiredSize(); - const wxSize& statmin __unused = statuspanel->GetMinClientSize(); - const wxSize& viewmin __unused = viewpanel->GetMinClientSize(); - DPRINTF("Window.FontChanged ctrl.min=(%d,%d)\n", minsize.x, minsize.y); - DPRINTF("Window.FontChanged stat.min=(%d,%d) des=(%d,%d)\n", - statmin.x, statmin.y, statsize.x, statsize.y); - DPRINTF("Window.FontChanged view.min=(%d,%d)\n", viewmin.x, viewmin.y); - - minsize.x = std::max(minsize.x, statsize.x); - minsize.y += statsize.y; - minsize.y += viewmin.y; - DPRINTF("Window.FontChanged SetMin=(%d,%d)\n", minsize.x, minsize.y); - SetMinClientSize(minsize); - SetClientSize(minsize); - Layout(); + // コントロールからウィンドウサイズを計算。 + const wxSize& ctrlsize = ctrlpanel->GetSize(); + const wxSize& statsize = statuspanel->GetSize(); + const wxSize& viewsize = viewpanel->GetSize(); - DPRINTF("Window.FontChanged end\n"); + int size_x = ctrlsize.x; + if (size_x < statsize.x) { + size_x = statsize.x; + } + if (size_x < viewsize.x) { + size_x = viewsize.x; + } + wxSize size(size_x, ctrlsize.y + statsize.y + viewsize.y); + DPRINTF("Window.Fit (%d,%d)\n", size.x, size.y); + SetClientSize(size); +} + +void +WXPlaneVRAMWindow::DoSizeHints() +{ + // 最小サイズを再計算。 + const wxSize& ctrlmin = ctrlpanel->GetMinSize(); + const wxSize& statmin = statuspanel->GetMinSize(); + const wxSize& viewmin = viewpanel->GetMinSize(); + DPRINTF("Window.%s ctrlmin=(%d,%d) statmin=(%d,%d) viewmin=(%d,%d)\n", + __func__, + ctrlmin.x, ctrlmin.y, statmin.x, statmin.y, viewmin.x, viewmin.y); + + int minsize_x = ctrlmin.x; + if (minsize_x < statmin.x) { + minsize_x = statmin.x; + } + if (minsize_x < viewmin.x) { + minsize_x = viewmin.x; + } + min_height = ctrlmin.y + statmin.y + viewmin.y; + wxSize minsize(minsize_x, min_height); + + // 最大サイズは viewsize に依存しそうだから手抜き。 + const wxSize& viewmax = viewpanel->GetMaxSize(); + wxSize maxsize(viewmax.x, ctrlmin.y + statmin.y + viewmax.y); + + // 最大最小サイズを再設定。 + DPRINTF("Window.%s min=(%d,%d), max=(%d,%d)\n", __func__, + minsize.x, minsize.y, maxsize.x, maxsize.y); + SetSizeHints(wxDefaultCoord, wxDefaultCoord); + SetSizeHints(ClientToWindowSize(minsize), ClientToWindowSize(maxsize)); +} + +bool +WXPlaneVRAMWindow::Layout() +{ + // sizer があるので呼ばないといけない。 + bool r = inherited::Layout(); + + // 全部準備出来る前に呼ばれることがある。 + if (ctrlpanel == NULL || statuspanel == NULL || viewpanel == NULL || + spacer == NULL) + { + return r; + } + + wxSize client = GetClientSize(); + DPRINTF("Window.Layout (%d,%d)\n", client.x, client.y); + const wxSize& ctrlsize = ctrlpanel->GetSize(); + const wxSize& statsize = statuspanel->GetSize(); + + // wxmonitor.cpp の Layout() 内のコメント参照。 + wxSize min = GetMinClientSize(); + if (min.y >= min_height) { + // 正常なリサイズなのでこのサイズを覚えておく。 + correct_clientsize = client; + } else { + // 変なリサイズが行われたので、やり直す。 + client = correct_clientsize; + DoSizeHints(); + SetClientSize(client); + + min = GetMinClientSize(); + DPRINTF("Window.%s patch client=(%d,%d) min=(%d,%d)\n", __func__, + client.x, client.y, min.x, min.y); + + // SetClientSize() によってこの場で Layout() が呼ばれて戻ってきた + // はずなので、これ以降はもう処理しなくていい。 + return true; + } + + ctrlpanel->SetSize(0, 0, client.x, ctrlsize.y); + statuspanel->SetSize(0, ctrlsize.y, statsize.x, statsize.y); + viewpanel->SetSize(0, ctrlsize.y + statsize.y, + client.x, client.y - ctrlsize.y - statsize.y); + + // ステータスパネル右に空き地が出来るなら埋める。 + if (statsize.x < client.x) { + spacer->SetSize(statsize.x, ctrlsize.y, + client.x - statsize.x, statsize.y); + spacer->Show(); + } else { + spacer->Hide(); + } + + return true; } // プレーン選択のチェックボックスイベント @@ -694,9 +752,9 @@ WXPlaneVRAMWindow::DoScale(int scale_ind viewpanel->SetScale(1 << scale_index); // 最大サイズは表示部の最大サイズから - wxSize maxsize = viewpanel->GetMaxClientSize(); - maxsize.y += ctrlpanel->GetMinClientSize().y; - maxsize.y += statuspanel->GetMinClientSize().y; + wxSize maxsize = viewpanel->GetMaxSize(); + maxsize.y += ctrlpanel->GetMinSize().y; + maxsize.y += statuspanel->GetMinSize().y; SetMaxClientSize(maxsize); // 保存しておくのはインデックスのほう