--- nono/wx/wxmonitor.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/wx/wxmonitor.cpp 2026/04/29 17:05:53 1.1.1.12 @@ -1,66 +1,278 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// モニター // -#include "wxheader.h" #include "wxmonitor.h" -#include "wxtextscreen.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 // -// モニターウィンドウ +// モニターパネル // // イベントテーブル -wxBEGIN_EVENT_TABLE(WXMonitorWindow, inherited) - EVT_TIMER(wxID_ANY, WXMonitorWindow::OnTimer) +wxBEGIN_EVENT_TABLE(WXMonitorPanel, inherited) + EVT_TIMER(wxID_ANY, WXMonitorPanel::OnTimer) wxEND_EVENT_TABLE() // コンストラクタ -WXMonitorWindow::WXMonitorWindow(wxWindow *parent, wxWindowID id, - const wxString& name, Object *obj) - : inherited(parent, id, name) -{ - object = obj; - assert(object); - - // テキストスクリーン - TextScreen& monitor = object->monitor; - int col = monitor.GetCol(); - int row = monitor.GetRow(); - const uint16 *textbuf = monitor.GetBuf(); - screen = new WXTextScreen(this, col, row, textbuf); +WXMonitorPanel::WXMonitorPanel(wxWindow *parent, Monitor *monitor_) + : inherited(parent, monitor_->GetSize()) + , monitor(monitor_) +{ + timer.SetOwner(this); - // 大きさをそれに固定 - Layout(); - Fit(); + // 現在の設定値を適用 + auto mainframe = dynamic_cast(GetParent()->GetParent()); + SetRate(mainframe->GetMonitorRate()); - // 更新タイマーは適当な短さにしておく - timer.SetOwner(this); - timer.Start(10); + // 最初に一回描画を起こす + wxTimerEvent dummy(timer); + AddPendingEvent(dummy); } // デストラクタ -WXMonitorWindow::~WXMonitorWindow() +WXMonitorPanel::~WXMonitorPanel() { } // タイマーイベント void -WXMonitorWindow::OnTimer(wxTimerEvent& event) +WXMonitorPanel::OnTimer(wxTimerEvent& event) +{ + DoRefresh(); +} + +// 画面を更新して再描画する。 +// +// 通常はタイマーイベントにより更新間隔ごとに呼ばれるが、 +// ユーザ操作により画面の更新が必要ならその都度直接これを呼ぶ。 +void +WXMonitorPanel::DoRefresh() +{ + // モニタースクリーンを更新して + monitor->Update(screen); + + // 必要なら再描画 + if (screen.GetBuf() != prevbuf) { + Refresh(); + } +} + +// 画面更新頻度を Hz で設定する。 +void +WXMonitorPanel::SetRate(int hz) +{ + timer.Start(1000 / hz); +} + + +// +// モニターウィンドウ +// + +// コンストラクタ +WXMonitorWindow::WXMonitorWindow(wxWindow *parent, const wxString& name, + Monitor *monitor_) + : inherited(parent, wxID_ANY, name) +{ + // モニタパネル + monpanel = new WXMonitorPanel(this, monitor_); + Fit(); +} + +// デストラクタ +WXMonitorWindow::~WXMonitorWindow() +{ +} + + +// +// 縦スクロールバー付きモニタウィンドウ +// + +// コンストラクタ +WXScrollMonitorWindow::WXScrollMonitorWindow(wxWindow *parent, + const wxString& name, Monitor *monitor_) + : inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER) +{ + DPRINTF("%s begin\n", __method__); + + // +--------------+---------+ + // | MonitorPanel | VScroll | + // +--------------+---------+ + + // モニタパネル + monpanel = new WXMonitorPanel(this, monitor_); + + // スクロールバー + vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL); + + // 自前レイアウト。 + SetMyLayout(); + + // ウィンドウサイズを確定させる + FontChanged(); + + // スクロールのために (パネル領域での) MouseWheel イベントもここで + // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが + // 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ + // (wxFrame)ではなくパネルに対して飛んでくる。 + // ここで一緒に処理したほうが楽なので、こちらに回す。 + monpanel->Connect(wxEVT_MOUSEWHEEL, + wxMouseEventHandler(WXScrollMonitorWindow::OnMouseWheel), NULL, this); + + // スクロールバーからの位置変更通知 + vscroll->Connect(NONO_EVT_SCROLL, + wxScrollEventHandler(WXScrollMonitorWindow::OnScroll), NULL, this); + DPRINTF("%s done\n", __method__); +} + +// デストラクタ +WXScrollMonitorWindow::~WXScrollMonitorWindow() { - // モニタースクリーンを更新して再描画。 - bool avail = object->MonitorUpdate(); - assert(avail); - screen->Refresh(); } -// フォントサイズ変更 void -WXMonitorWindow::SetFontSize(fontsize_t fontsize) +WXScrollMonitorWindow::FontChanged() { - screen->SetFontSize(fontsize); - // 大きさを変更 - Layout(); + monpanel->FontChanged(); + vscroll->FontChanged(); + DPRINTF("%s %d\n", __method__, monpanel->GetFontHeight()); + Fit(); } + +// 自前レイアウト方式のサイズに関する情報を返す。 +bool +WXScrollMonitorWindow::GetMySizeHints(wxSize *newp, wxSize *minp, wxSize *maxp, + wxSize *incp) +{ + 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 newsize(x, new_y); + wxSize minsize(x, min_y); + wxSize maxsize(x, GetMaxClientSize().y); + wxSize incsize(0, monpanel->GetFontHeight()); + + if (newp) *newp = newsize; + if (minp) *minp = minsize; + if (maxp) *maxp = maxsize; + if (incp) *incp = incsize; + return true; +} + +bool +WXScrollMonitorWindow::Layout() +{ + if (MyLayout() == false) { + // コントロールを配置。 + wxSize client = GetClientSize(); + const wxSize vsize = vscroll->GetSize(); + + int mon_width = client.x - vsize.x; + int mon_height = client.y; + // 最低でも 1px ないと GTK とかに怒られる。今は起きないはず? + if (__predict_false(mon_width < 1)) { + mon_width = 1; + } + if (__predict_false(mon_height < 1)) { + mon_height = 1; + } +#if defined(WINDOW_DEBUG) + const wxSize win = GetSize(); + const wxSize minwin = GetMinSize(); + printf("%s WinSize =(%d,%d) MinWin =(%d,%d) Shown=%u\n", + __method__, win.x, win.y, minwin.x, minwin.y, IsShown()); + const wxSize min = GetMinClientSize(); + printf("%s ClientSize=(%d,%d) MinClient=(%d,%d)\n", + __method__, client.x, client.y, min.x, min.y); +#endif + + monpanel->SetSize(0, 0, mon_width, mon_height); + vscroll->SetSize(mon_width, 0, vsize.x, mon_height); + +#if defined(WINDOW_DEBUG) + const wxSize monsize = monpanel->GetSize(); + const wxSize vsize1 = vscroll->GetSize(); + int py = monsize.y - monpanel->GetPaddingY(); + int height = monpanel->GetFontHeight(); + int row = (height != 0) ? (py / height) : 0; + int res = (height != 0) ? (py % height) : 0; + printf("%s mon=(%d,%d) vscroll=(%d,%d), row=%d%s\n", __method__, + monsize.x, monsize.y, vsize1.x, vsize1.y, + row, (res == 0 ? "" : "+")); +#endif + + // スクロールバーを再設定。 + // 今の所ヘッダが常に1行ある。 + static int HEADLINES = 1; + int pos = (int)monpanel->GetUserData(); + int thumbsize = monpanel->GetRow() - 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); + } + return true; +} + +// マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある) +void +WXScrollMonitorWindow::OnMouseWheel(wxMouseEvent& event) +{ + // GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。 + // どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行 + // スクロールするのでそれに合わせる。 + int pos = (int)monpanel->GetUserData(); + pos = pos - event.GetWheelRotation() / 40; + + int maxpos = vscroll->GetRange() - vscroll->GetThumbSize(); + if (pos < 0) + pos = 0; + if (pos > maxpos) + pos = maxpos; + + DoScroll(pos); + // スクロールバーの位置を追従 + vscroll->SetThumbPosition(pos); +} + +// スクロールバーからの通知イベント +void +WXScrollMonitorWindow::OnScroll(wxScrollEvent& event) +{ + DoScroll(event.GetPosition()); +} + +// スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる) +void +WXScrollMonitorWindow::DoScroll(int pos) +{ + // userdata が表示開始位置になっている + monpanel->SetUserData(pos); + monpanel->DoRefresh(); +}