--- nono/wx/wxmonitor.cpp 2026/04/29 17:05:12 1.1.1.7 +++ nono/wx/wxmonitor.cpp 2026/04/29 17:05:44 1.1.1.10 @@ -10,6 +10,15 @@ #include "wxmonitor.h" #include "wxmainframe.h" +#include "monitor.h" + +//#define WINDOW_DEBUG 1 + +#if defined(WINDOW_DEBUG) +#define DPRINTF(fmt...) printf(fmt) +#else +#define DPRINTF(fmt...) /**/ +#endif // // モニターパネル @@ -21,14 +30,15 @@ wxBEGIN_EVENT_TABLE(WXMonitorPanel, inhe wxEND_EVENT_TABLE() // コンストラクタ -WXMonitorPanel::WXMonitorPanel(wxWindow *parent, Monitor& monitor_) - : inherited(parent, monitor_.GetSize()) +WXMonitorPanel::WXMonitorPanel(wxWindow *parent, Monitor *monitor_) + : inherited(parent, monitor_->GetSize()) , monitor(monitor_) { timer.SetOwner(this); // 現在の設定値を適用 - SetRate(gMainFrame->GetMonitorRate()); + auto mainframe = dynamic_cast(GetParent()->GetParent()); + SetRate(mainframe->GetMonitorRate()); // 最初に一回描画を起こす wxTimerEvent dummy(timer); @@ -77,12 +87,12 @@ WXMonitorPanel::SetRate(int hz) // コンストラクタ WXMonitorWindow::WXMonitorWindow(wxWindow *parent, const wxString& name, - Monitor& monitor_) + Monitor *monitor_) : inherited(parent, wxID_ANY, name) { // モニタパネル monpanel = new WXMonitorPanel(this, monitor_); - DoSize(); + Fit(); } // デストラクタ @@ -95,35 +105,28 @@ WXMonitorWindow::~WXMonitorWindow() // 縦スクロールバー付きモニタウィンドウ // -// イベントテーブル -wxBEGIN_EVENT_TABLE(WXScrollMonitorWindow, inherited) - EVT_SIZE(WXScrollMonitorWindow::OnSize) -wxEND_EVENT_TABLE() - // コンストラクタ WXScrollMonitorWindow::WXScrollMonitorWindow(wxWindow *parent, - const wxString& name, Monitor& monitor_) + const wxString& name, Monitor *monitor_) : inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER) { - // → + DPRINTF("%s begin\n", __method__); + // +--------------+---------+ // | MonitorPanel | VScroll | // +--------------+---------+ - wxBoxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL); - // モニタパネル monpanel = new WXMonitorPanel(this, monitor_); - topsizer->Add(monpanel, 1, wxEXPAND); // スクロールバー vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL); - topsizer->Add(vscroll, 0, wxEXPAND); - SetSizer(topsizer); + // Sizer 使わず自前でレイアウトする。 + SetAutoLayout(true); // ウィンドウサイズを確定させる - DoSize(); + FontChanged(); // スクロールのために (パネル領域での) MouseWheel イベントもここで // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが @@ -136,6 +139,7 @@ WXScrollMonitorWindow::WXScrollMonitorWi // スクロールバーからの位置変更通知 vscroll->Connect(NONO_EVT_SCROLL, wxScrollEventHandler(WXScrollMonitorWindow::OnScroll), NULL, this); + DPRINTF("%s done\n", __method__); } // デストラクタ @@ -143,32 +147,142 @@ WXScrollMonitorWindow::~WXScrollMonitorW { } -bool -WXScrollMonitorWindow::Layout() +void +WXScrollMonitorWindow::FontChanged() { - // 縦リサイズ可能レイアウト - return LayoutTextVResize(monpanel); + monpanel->FontChanged(); + vscroll->FontChanged(); + DPRINTF("%s %d\n", __method__, monpanel->GetFontHeight()); + + Fit(); } -// サイズ変更イベント void -WXScrollMonitorWindow::OnSize(wxSizeEvent& event) +WXScrollMonitorWindow::Fit() { - inherited::OnSize(event); + // inherited::Fit() は呼ばずに自力で行う。 - // 今の所ヘッダが常に1行ある - static int HEADLINES = 1; + wxSize newsize = DoSizeHints(); +#if defined(WINDOW_DEBUG) + wxSize newwin = ClientToWindowSize(newsize); + printf("%s (%d,%d) client=(%d,%d)\n", __method__, + newwin.x, newwin.y, newsize.x, newsize.y); +#endif + SetClientSize(newsize); +} - // スクロールバーを再設定 +bool +WXScrollMonitorWindow::Layout() +{ + // inherited::Layout() は(実質何もしないので)、呼ばずに自力で行う。 + + wxSize client = GetClientSize(); + const wxSize win = GetSize(); + const wxSize margin = win - client; + const wxSize vsize = vscroll->GetSize(); + const wxSize monsize = monpanel->GetSize(); +#if defined(WINDOW_DEBUG) + const wxSize min = GetMinClientSize(); + int py = client.y + - monpanel->GetPaddingTop() - monpanel->GetPaddingBottom(); + int height = monpanel->GetFontHeight(); + int row = (height != 0) ? (py / height) : 0; + int res = (height != 0) ? (py % height) : 0; + wxSize minwin = GetMinSize(); + printf("%s begin: margin=(%d,%d) shown=%d\n", __method__, + margin.x, margin.y, IsShown()); + printf("%s WinSize =(%d,%d) MinWin =(%d,%d)\n", __method__, + win.x, win.y, minwin.x, minwin.y); + printf("%s ClientSize=(%d,%d) MinClient=(%d,%d)\n", __method__, + client.x, client.y, min.x, min.y); + printf("%s mon=(%d,%d) vscroll=(%d,%d), row=%d%s\n", __method__, + monsize.x, monsize.y, vsize.x, vsize.y, + row, (res == 0 ? "" : "+")); +#endif + + if (oldmargin != margin) { + // ウィンドウマネージャ管轄のサイズが変わったので最小サイズを再設定。 + // ここでは出来ないのでこの関数を抜けた後 CallAfter 内で設定する。 + layout_pass2 = true; + oldmargin = margin; + CallAfter([this]() { + DoSizeHints(); + }); + DPRINTF("%s done(pass1)\n", __method__); + return true; + } else if (layout_pass2) { + // SetSizeHints() 後に呼ばれるところ。 + // なぜか設定したサイズと変わっている(ことがある?)ので、その場合は + // 仕方ないのでここで元の要求サイズに再設定する。 + // ウィンドウマネージャによっては表示サイズが変わらない(?)ので + // Hide()/Show() で叩き起こすと直るようだ??。 + layout_pass2 = false; + client.y = monsize.y; + DPRINTF("%s SetClientSize (%d,%d)\n", __method__, client.x, client.y); + SetClientSize(client); + Hide(); + Show(); + DPRINTF("%s done(pass2)\n", __method__); + return true; + } + + // コントロールを配置。 + int mon_width = client.x - vsize.x; + int mon_height = client.y; + // 最低でも 1px ないと GTK とかに怒られる。 + if (mon_width < 1) { + mon_width = 1; + } + if (mon_height < 1) { + mon_height = 1; + } + + monpanel->SetSize(0, 0, mon_width, mon_height); + vscroll->SetSize(mon_width, 0, vsize.x, mon_height); + + // スクロールバーを再設定。 + // 今の所ヘッダが常に1行ある。 + static int HEADLINES = 1; int pos = (int)monpanel->GetUserData(); int thumbsize = monpanel->GetRow() - HEADLINES; - int range = monpanel->GetMonitor().GetMaxHeight() - HEADLINES; + int range = monpanel->GetMonitor()->GetMaxHeight() - HEADLINES; int pagesize = thumbsize - 1; if (pos > range - thumbsize) { pos = range - thumbsize; DoScroll(pos); } vscroll->SetScrollParam(pos, thumbsize, range, pagesize); + + DPRINTF("%s done\n", __method__); + + return true; +} + +// クライアントサイズと最大/最小サイズを計算する。 +// SetSizeHints() を実行し、設定すべきクライアントサイズを返す。 +wxSize +WXScrollMonitorWindow::DoSizeHints() +{ + auto psize = monpanel->GetSize(); + auto vsize = vscroll->GetSize(); + + // 幅は monpanel 幅 + スクロールバーの幅、高さは monpanel の高さで決まる。 + // monpanel には四辺に Padding があることに注意。 + int x = psize.x + vsize.x; + int min_y = std::max(vscroll->GetMinSize().y, + monpanel->GetScreenHeight(1)); + int new_y = std::max(psize.y, min_y); + wxSize minsize(x, min_y); + wxSize maxsize(x, GetMaxClientSize().y); + wxSize incsize(0, monpanel->GetFontHeight()); + wxSize minwin = ClientToWindowSize(minsize); + wxSize maxwin = ClientToWindowSize(maxsize); + wxSize newsize(x, new_y); + DPRINTF("%s MinWin=(%d,%d) RecommendClient=(%d,%d)\n", __method__, + minwin.x, minwin.y, newsize.x, newsize.y); + SetSizeHints(minwin, maxwin, incsize); + + return newsize; } // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)