--- nono/wx/wxsubwindow.cpp 2026/04/29 17:04:39 1.1.1.3 +++ nono/wx/wxsubwindow.cpp 2026/04/29 17:05:48 1.1.1.11 @@ -4,32 +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() { @@ -40,113 +48,174 @@ void WXSubWindow::OnClose(wxCloseEvent& event) { // ウィンドウリストから自身を削除 - gMainFrame->DeleteWindow(GetId()); + auto mainframe = dynamic_cast(GetParent()); + mainframe->DeleteWindow(this); } -// テキスト系パネルのフォントサイズを変更する +// フォントサイズ変更 void -WXSubWindow::SetFontSize(FontId fontid) +WXSubWindow::FontChanged() { - // このウィンドウの子のうち.. - wxWindowList& children = GetChildren(); - for (wxWindow *child : children) { - // すべての WXTextPanel の継承クラスだけに対して - // フォントサイズの変更を指示する。 - WXTextPanel *textpanel = dynamic_cast(child); + // WXTextPanel 派生の子コントロールに対し FontChanged() を呼ぶ + for (auto child : GetChildren()) { + auto textpanel = dynamic_cast(child); if (textpanel) { - textpanel->SetFontSize(fontid); + textpanel->FontChanged(); } } - // 大きさを変更 - Layout(); Fit(); } - -// -// 縦リサイズ可能なサブウィンドウ -// - -// コンストラクタ -WXVResizeSubWindow::WXVResizeSubWindow(wxWindow *parent, wxWindowID id, - const wxString& name) - : inherited(parent, id, name, DEFAULT_STYLE | wxRESIZE_BORDER) +// 自前レイアウト方式を使うことを宣言する。 +// ついでに必要な SetAutoLayout() も実行しておく。 +// おそらくコンストラクタで使うことになるはず。 +void +WXSubWindow::SetMyLayout() { -} + my_layout = true; -// デストラクタ -WXVResizeSubWindow::~WXVResizeSubWindow() -{ + SetAutoLayout(true); } -// フォントサイズ変更 +// 中身の状況に応じてこのウィンドウの大きさを変更。 +// 1) 自前方式か +// 2) このウィンドウに top sizer が指定されているか +// 3) 子コントロールが1つしかないか +// のいずれかの場合に使える。 +// +// コントロールが複数あって上記のいずれでもない場合は、コントロールを +// どう配置するのか決定できないので、assert する。 void -WXVResizeSubWindow::SetFontSize(FontId fontid) +WXSubWindow::Fit() { - // 親クラスの処理をして.. - inherited::SetFontSize(fontid); + wxSize csize; - // フォントサイズが確定したので、継承先のウィンドウサイズ計算処理。 - // 引数なしの DoSize() は継承先ごとに実装する仮想関数、 - // そいつが共通の CalcVSize(int) を呼び出してくる。 - DoSize(); + 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); } -// ウィンドウサイズを再計算する共通部分。 -// -// テキスト系ウィンドウではウィンドウの縦方向のリサイズは1行の高さ分ずつ -// 行いたいが、そのためにはこのウィンドウのクライアント領域が1行の高さの -// 整数倍になっている必要がある。TextScreen 自身は行の高さの整数倍だが、 -// 他に余白や別コントロールを追加して、フォントサイズも動的に変化すると -// なると、諸々自動で計算したい。 -// フォントサイズが確定したところで呼び出すこと。 -// -// fixed は TextScreen とパディングを除いたコントロールの高さ -void -WXVResizeSubWindow::CalcVSize(int fixed) +// サイズが変わった場合に呼ばれて、子コントロールの再配置を行う。 +bool +WXSubWindow::Layout() { - const wxSize& fontsize = screen->GetFontSize(); + if (my_layout) { + return MyLayout(); + } else { + return inherited::Layout(); + } +} - int pad_top = 0; - int pad_btm = 0; +// 自前方式の時の 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); - // pad は必要なパディング高 - int pad = 0; - if (fixed == 0) { - // 追加のコントロールが一切なければ1行分のパディング - pad = fontsize.y; - } else { - // 追加のコントロールがあれば、繰り上がるまでの余りをとる - pad = ((fixed + fontsize.y - 1) / fontsize.y) * fontsize.y - fixed; + Hide(); + Show(); + + DPRINTF("%s done(pass2)\n", __method__); + return true; } - if (pad > fontsize.y / 2) { - // 必要なパディングが行の 1/2 以上なら、下に 1/2、残りを上に - pad_btm = fontsize.y / 2; - pad_top = pad - pad_btm; + // ここまで来るとようやくコントロールを再配置を継承側で行える。 + // それを示すため 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 { - // 必要なパディングが行の 1/2 以下なら、半分ずつ - pad_btm = pad / 2; - pad_top = pad - pad_btm; + // 対応していない。 + return wxDefaultSize; } +} - // パディングを設定 - wxSize size1(1, pad_top); - wxSize size2(1, pad_btm); - padding1->SetSize(size1); - padding1->SetMinSize(size1); - padding2->SetSize(size2); - padding2->SetMinSize(size2); - - // 横は固定、縦方向は1行ずつ伸縮可能 - // 最小サイズは fixed と1行分とパディング。 - wxSize tsize = topsizer->ComputeFittingWindowSize(this); - wxSize minsize = wxSize(tsize.x, fixed + fontsize.y + pad); - wxSize maxsize = wxSize(tsize.x, -1); - wxSize incsize = wxSize(0, fontsize.y); - SetSizeHints(minsize, maxsize, incsize); - - Layout(); - Fit(); +// 自前方式のサイズに関する情報を取得する。 +// 継承先で用意すること。ここにあるのは継承しなかった時の何もしない版。 +// +// 継承側はそれぞれ以下の値を書き戻して 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; }