--- nono/wx/wxsubwindow.cpp 2026/04/29 17:05:12 1.1.1.8 +++ nono/wx/wxsubwindow.cpp 2026/04/29 17:05:48 1.1.1.11 @@ -7,6 +7,7 @@ // // サブウィンドウ // +// wxWindow::SetSizeHints() の挙動がアレなのに対応するハック付き。 #include "wxsubwindow.h" #include "fontmanager.h" @@ -17,6 +18,14 @@ // サブウィンドウ (基本クラス) // +//#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) @@ -39,7 +48,8 @@ void WXSubWindow::OnClose(wxCloseEvent& event) { // ウィンドウリストから自身を削除 - gMainFrame->DeleteWindow(this); + auto mainframe = dynamic_cast(GetParent()); + mainframe->DeleteWindow(this); } // フォントサイズ変更 @@ -54,59 +64,158 @@ WXSubWindow::FontChanged() } } - DoSize(); + Fit(); } -// 中身に応じてこのウィンドウの大きさを変更 +// 自前レイアウト方式を使うことを宣言する。 +// ついでに必要な SetAutoLayout() も実行しておく。 +// おそらくコンストラクタで使うことになるはず。 void -WXSubWindow::DoSize() +WXSubWindow::SetMyLayout() +{ + my_layout = true; + + SetAutoLayout(true); +} + +// 中身の状況に応じてこのウィンドウの大きさを変更。 +// 1) 自前方式か +// 2) このウィンドウに top sizer が指定されているか +// 3) 子コントロールが1つしかないか +// のいずれかの場合に使える。 +// +// コントロールが複数あって上記のいずれでもない場合は、コントロールを +// どう配置するのか決定できないので、assert する。 +void +WXSubWindow::Fit() { wxSize csize; - auto *topsizer = GetSizer(); - if (topsizer) { - // Sizer があればそれがサイズを知っている - csize = topsizer->GetMinSize(); + if (my_layout) { + // 1) 自前方式なら継承先がサイズを知っている。 + csize = DoMySizeHints(); + assert(csize.x > 0); + assert(csize.y > 0); } else { - // Sizer を使ってなければ子コントロールは1人のはず - auto& children = GetChildren(); - assert(children.GetCount() == 1); - auto child = children[0]; - csize = child->GetMinSize(); + 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); } -// このサブウィンドウが縦リサイズ可能なテキストパネルの場合の Layout()。 -// 本来は screen を持っている継承クラスが個別に行うことだが、定形処理なので -// ここでサブルーチンとして用意しておく。必要なら継承クラスが呼ぶこと。 +// サイズが変わった場合に呼ばれて、子コントロールの再配置を行う。 bool -WXSubWindow::LayoutTextVResize(const WXTextScreen *screen, int fixed_height) +WXSubWindow::Layout() { - if (inherited::Layout() == false) { - return false; + if (my_layout) { + return MyLayout(); + } else { + return inherited::Layout(); } +} - wxSize tsize = GetSizer()->ComputeFittingClientSize(this); +// 自前方式の時の 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; + } - // 高さ計算。 - // 高さの最小は fixed と TextScreen 2行分にしておく。 - int h = fixed_height; - h += screen->GetPaddingTop(); - h += screen->GetFontHeight() * 2; - h += screen->GetPaddingBottom(); - - // サイズを変える時は、大きくする時は Max を先に、小さくする時は Min を - // 先に変えないと、サイズ制約の不整合の GTK ワーニングが出てしまう。 - // うーんこの API…。 - wxSize csize = GetClientSize(); - if (tsize.x > csize.x) { - SetMaxClientSize(wxSize(tsize.x, wxDefaultCoord)); - SetMinClientSize(wxSize(tsize.x, h)); + // ここまで来るとようやくコントロールを再配置を継承側で行える。 + // それを示すため 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 { - SetMinClientSize(wxSize(tsize.x, h)); - SetMaxClientSize(wxSize(tsize.x, wxDefaultCoord)); + // 対応していない。 + return wxDefaultSize; } +} - return true; +// 自前方式のサイズに関する情報を取得する。 +// 継承先で用意すること。ここにあるのは継承しなかった時の何もしない版。 +// +// 継承側はそれぞれ以下の値を書き戻して 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; }