--- nono/wx/wxsubwindow.cpp 2026/04/29 17:04:37 1.1.1.2 +++ nono/wx/wxsubwindow.cpp 2026/04/29 17:05:48 1.1.1.11 @@ -4,31 +4,40 @@ // Licensed under nono-license.txt // +// +// サブウィンドウ +// +// wxWindow::SetSizeHints() の挙動がアレなのに対応するハック付き。 + #include "wxsubwindow.h" +#include "fontmanager.h" #include "wxmainframe.h" +#include "wxtextscreen.h" // -// サブウィンドウ +// サブウィンドウ (基本クラス) // +//#define WINDOW_DEBUG 1 + +#if defined(WINDOW_DEBUG) +#define DPRINTF(fmt...) printf(fmt) +#else +#define DPRINTF(fmt...) /**/ +#endif + // イベントテーブル wxBEGIN_EVENT_TABLE(WXSubWindow, inherited) EVT_CLOSE(WXSubWindow::OnClose) wxEND_EVENT_TABLE() -// コンストラクタ (スタイル指定あり) +// コンストラクタ WXSubWindow::WXSubWindow(wxWindow *parent, wxWindowID id, const wxString& name, int style) : inherited(parent, id, name, wxDefaultPosition, wxDefaultSize, style) { } -// コンストラクタ (スタイル指定なし) -WXSubWindow::WXSubWindow(wxWindow *parent, wxWindowID id, const wxString& name) - : WXSubWindow(parent, id, name, DEFAULT_STYLE) -{ -} - // デストラクタ WXSubWindow::~WXSubWindow() { @@ -39,8 +48,174 @@ void WXSubWindow::OnClose(wxCloseEvent& event) { // ウィンドウリストから自身を削除 - gMainFrame->DeleteWindow(GetId()); + auto mainframe = dynamic_cast(GetParent()); + mainframe->DeleteWindow(this); +} + +// フォントサイズ変更 +void +WXSubWindow::FontChanged() +{ + // WXTextPanel 派生の子コントロールに対し FontChanged() を呼ぶ + for (auto child : GetChildren()) { + auto textpanel = dynamic_cast(child); + if (textpanel) { + textpanel->FontChanged(); + } + } + + Fit(); +} + +// 自前レイアウト方式を使うことを宣言する。 +// ついでに必要な SetAutoLayout() も実行しておく。 +// おそらくコンストラクタで使うことになるはず。 +void +WXSubWindow::SetMyLayout() +{ + my_layout = true; + + SetAutoLayout(true); } -// スタティック変数 (初期化は wxmainframe.cpp で行っている) -/*static*/ wxSizerFlags WXSubWindow::BorderFlags; +// 中身の状況に応じてこのウィンドウの大きさを変更。 +// 1) 自前方式か +// 2) このウィンドウに top sizer が指定されているか +// 3) 子コントロールが1つしかないか +// のいずれかの場合に使える。 +// +// コントロールが複数あって上記のいずれでもない場合は、コントロールを +// どう配置するのか決定できないので、assert する。 +void +WXSubWindow::Fit() +{ + wxSize csize; + + if (my_layout) { + // 1) 自前方式なら継承先がサイズを知っている。 + csize = DoMySizeHints(); + assert(csize.x > 0); + assert(csize.y > 0); + } else { + auto *topsizer = GetSizer(); + if (topsizer) { + // 2) Sizer があればそれがサイズを知っている。 + csize = topsizer->ComputeFittingClientSize(this); + } else { + // 3) どちらでもない場合、子コントロールが1人なら対応出来る。 + // これはネイティブの Fit() と等価のはず。 + auto& children = GetChildren(); + assert(children.GetCount() == 1); + auto child = children[0]; + csize = child->GetSize(); + } + } + SetClientSize(csize); +} + +// サイズが変わった場合に呼ばれて、子コントロールの再配置を行う。 +bool +WXSubWindow::Layout() +{ + if (my_layout) { + return MyLayout(); + } else { + return inherited::Layout(); + } +} + +// 自前方式の時の Layout()。 +// 何度か呼び出されて仕事をしないと適切なサイズになってくれないため。 +// +// Layout() は何かしたら true を返すので、ここもそれに倣う。 +// MyLayout() が何かしたら true を返すので継承側は何もせずに true を返すこと。 +// MyLayout() が false を返したら、継承側は子コントロールを配置してよい。 +// その場合も呼び出し元には true を返すこと +// つまり、継承側はこうなるはず。 +// bool Children::Layout() { +// if (MyLayout() == false) { +// ここで子コントロールを配置。 +// } +// return true; +// } +bool +WXSubWindow::MyLayout() +{ + wxSize client = GetClientSize(); + const wxSize& win = GetSize(); + const wxSize& border = win - client; + + if (border != prev_border) { + // ウィンドウマネージャ管轄のサイズが変わったので最小サイズを再設定。 + my_layout_pass2 = true; + prev_border = border; + // ここでは出来ないのでこの関数を抜けた後 CallAfter 内で設定する。 + CallAfter([this]() { + DoMySizeHints(); + }); + return true; + + } else if (my_layout_pass2) { + // SetSizeHints() 後に呼ばれるところ。 + // なぜか設定したサイズと変わっている(ことがある?)ので、その場合は + // 仕方ないのでここで元の要求サイズに再設定する。 + // ウィンドウマネージャによっては表示サイズが変わらない(?)ので + // Hide()/Show() で叩き起こすと直るようだ??。 + my_layout_pass2 = false; + DPRINTF("%s ClientSize(%d,%d)->", __method__, client.x, client.y); + GetMySizeHints(&client, NULL, NULL, NULL); + DPRINTF("(%d,%d)\n", client.x, client.y); + SetClientSize(client); + + Hide(); + Show(); + + DPRINTF("%s done(pass2)\n", __method__); + return true; + } + + // ここまで来るとようやくコントロールを再配置を継承側で行える。 + // それを示すため false を返す。 + // wxTopLevelWindow::Layout() は何かすれば true、 + // sizer がないか子コントロールが複数あって何も出来ることがない場合は + // false を返すと書いてあるので都合よく借りておく。 + DPRINTF("%s done false\n", __method__); + return false; +} + +// SetSizeHints() を実行して、希望するクライアントサイズを返す。 +// 自前方式に対応していない場合は何もせず wxDefaultSize を返す。 +wxSize +WXSubWindow::DoMySizeHints() +{ + wxSize newsize; + wxSize minsize; + wxSize maxsize; + wxSize incsize; + + if (GetMySizeHints(&newsize, &minsize, &maxsize, &incsize)) { + wxSize minwin = ClientToWindowSize(minsize); + wxSize maxwin = ClientToWindowSize(maxsize); + SetSizeHints(minwin, maxwin, incsize); + return newsize; + } else { + // 対応していない。 + return wxDefaultSize; + } +} + +// 自前方式のサイズに関する情報を取得する。 +// 継承先で用意すること。ここにあるのは継承しなかった時の何もしない版。 +// +// 継承側はそれぞれ以下の値を書き戻して true を返す。 +// *newsz は希望するクライアントサイズ。子コントロールの配置とサイズで決まる。 +// *minsz は最小クライアントサイズ。指定しないなら (-1, -1)。 +// *maxsz は最大クライアントサイズ。指定しないなら (-1, -1)。 +// *incsz はウィンドウサイズ変更時の増減サイズ。 +// newsz, minsz, maxsz, incsz はいずれも NULL でなければ代入する。 +bool +WXSubWindow::GetMySizeHints(wxSize *newsz, wxSize *minsz, wxSize *maxsz, + wxSize *incsz) +{ + return false; +}