diff --git a/Common/WinUtility.h b/Common/WinUtility.h index 09e3db86..d92dac57 100644 --- a/Common/WinUtility.h +++ b/Common/WinUtility.h @@ -116,15 +116,16 @@ inline BOOL setWindowRect(HWND hwnd, LPCRECT rc, UINT flags = 0) // inline BOOL isAncestor(HWND hwnd1, HWND hwnd2) { - while (hwnd1) - { - if (hwnd1 == hwnd2) - return TRUE; + //while (hwnd1) + //{ + // if (hwnd1 == hwnd2) + // return TRUE; - hwnd1 = ::GetParent(hwnd1); - } + // hwnd1 = ::GetParent(hwnd1); + //} - return FALSE; + //return FALSE; + return hwnd1 == hwnd2 || ::IsChild(hwnd2, hwnd1); } //-------------------------------------------------------------------- diff --git a/Common/Window.h b/Common/Window.h index 1598603f..0b2db1f1 100644 --- a/Common/Window.h +++ b/Common/Window.h @@ -141,8 +141,8 @@ namespace Tools UINT_PTR id, DWORD_PTR refData) { // MY_TRACE_FUNC("0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", hwnd, message, wParam, lParam, id, refData); - TCHAR className[MAX_PATH] = {}; - ::GetClassName(hwnd, className, std::size(className)); + //TCHAR className[MAX_PATH] = {}; + //::GetClassName(hwnd, className, std::size(className)); // MY_TRACE_TSTR(className); auto window = (Window*)refData; diff --git a/DirtyCheck/auls_confirmclose.h b/DirtyCheck/auls_confirmclose.h index 9960920d..6ad22779 100644 --- a/DirtyCheck/auls_confirmclose.h +++ b/DirtyCheck/auls_confirmclose.h @@ -89,15 +89,12 @@ namespace fgo::dirty_check } // - // ダーティーフラグをチェックします。 - // ダーティーな場合はtrueを返します。 + // ダーティーフラグを更新します. // - bool check() + void update() { - if (*g_undo_id_ptr == g_undo_id_prev) return false; - g_undo_id_prev = *g_undo_id_ptr; - g_dirty_flag = true; - return true; + if (!g_dirty_flag && *g_undo_id_ptr != g_undo_id_prev) + g_dirty_flag = true; } // @@ -105,20 +102,17 @@ namespace fgo::dirty_check // void clear() { - g_undo_id_prev = 0; + g_undo_id_prev = *g_undo_id_ptr; g_dirty_flag = false; } } dirty_flag; inline struct AviUtlWindow { - HWND hwnd = 0; - WNDPROC orig = 0; - // // AviUtlウィンドウのウィンドウプロシージャをフックします。 // - static LRESULT CALLBACK hook(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) + static LRESULT CALLBACK hook(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, uintptr_t id, auto) { if (dirty_flag.is_trigger_message(msg, wp)) { int id = MessageBoxA(hwnd, "変更された編集データがあります。保存しますか?", @@ -129,7 +123,9 @@ namespace fgo::dirty_check } else if (id == IDCANCEL) return 0; } - return CallWindowProc(aviutl_window.orig, hwnd, msg, wp, lp); + else if (msg == WM_DESTROY) + ::RemoveWindowSubclass(hwnd, hook, id); + return ::DefSubclassProc(hwnd, msg, wp, lp); } // @@ -138,10 +134,10 @@ namespace fgo::dirty_check BOOL init(AviUtl::FilterPlugin* fp) { // AviUtlウィンドウを取得します。 - hwnd = GetWindow(fp->hwnd, GW_OWNER); + auto hwnd = fp->hwnd_parent; // AviUtlウィンドウをサブクラス化します。 - orig = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG)hook); + ::SetWindowSubclass(hwnd, hook, reinterpret_cast(&dirty_flag), {}); return TRUE; } @@ -165,8 +161,8 @@ namespace fgo::dirty_check switch (msg) { case AviUtl::FilterPlugin::WindowMessage::Update: if (!fp->exfunc->is_editing(editp)) break; - // プロジェクトが変更されたかもしれないのでダーティーフラグをチェックします。 - dirty_flag.check(); + // プロジェクトが変更されたかもしれないのでダーティーフラグを更新します. + dirty_flag.update(); break; case AviUtl::FilterPlugin::WindowMessage::FileClose: // プロジェクトが閉じられたのでダーティーフラグをクリアします。 @@ -180,13 +176,20 @@ namespace fgo::dirty_check { // プロジェクトが保存されたのでダーティーフラグをクリアします。 dirty_flag.clear(); + + // フラグをクリアしてもタイトルは変わらないので手動で変更. + char title[MAX_PATH]; + ::GetWindowTextA(fp->hwnd_parent, title, std::size(title)); + if (title[0] == '*' && title[1] == ' ') + ::SetWindowTextA(fp->hwnd_parent, title + 2); return FALSE; } BOOL func_modify_title(AviUtl::FilterPlugin* fp, AviUtl::EditHandle* editp, int frame, LPSTR title, int max_title) { // プロジェクトが変更されたかもしれないのでダーティーフラグをチェックします。 - if (!dirty_flag.check()) return FALSE; + dirty_flag.update(); + if (!dirty_flag.get()) return FALSE; std::string str(title); sprintf_s(title, max_title, "* %s", str.c_str()); return TRUE; diff --git a/EditBoxTweaker/Servant.h b/EditBoxTweaker/Servant.h index 70362e2d..079c3ce7 100644 --- a/EditBoxTweaker/Servant.h +++ b/EditBoxTweaker/Servant.h @@ -110,12 +110,12 @@ namespace fgo::editbox_tweaker if (wcslen(font.name) != 0) { // DPIに合わせてフォントのサイズを調整します。 - int dpi = ::GetSystemDpiForProcess(::GetCurrentProcess()); - int height = ::MulDiv(font.height, dpi, 96); + //int dpi = ::GetSystemDpiForProcess(::GetCurrentProcess()); + //int height = ::MulDiv(font.height, dpi, 96); // HFONTを作成します。 // 複数行エディットボックスのWM_SETFONTでこのハンドルが渡されます。 - font.handle = ::CreateFontW(height, 0, + font.handle = ::CreateFontW(font.height, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, font.pitch, font.name); } @@ -235,7 +235,7 @@ namespace fgo::editbox_tweaker case WM_SETFONT: MY_TRACE(_T("WM_SETFONT, 0x%08X, 0x%08X\n"), wparam, lparam); wparam = reinterpret_cast(servant.font.handle); // handle is not null here. - //[[fallthrough]]; + [[fallthrough]]; case WM_DESTROY: ::RemoveWindowSubclass(hwnd, subclassproc, id); break; @@ -270,16 +270,16 @@ namespace fgo::editbox_tweaker // この構造体は::DispatchMesageA()を::DispatchMesageW()に置き換えるために使用されます。 // struct { - inline static decltype(&::DispatchMessageA) hook = ::DispatchMessageW; - inline static decltype(hook) orig = ::DispatchMessageA; + constexpr static auto& hook = ::DispatchMessageW; + inline static decltype(&hook) orig = ::DispatchMessageA; } DispatchMessageA; // // この構造体は::PeekMesageA()を::PeekMesageW()に置き換えるために使用されます。 // struct { - inline static decltype(&::PeekMessageA) hook = ::PeekMessageW; - inline static decltype(hook) orig = ::PeekMessageA; + constexpr static auto& hook = ::PeekMessageW; + inline static decltype(&hook) orig = ::PeekMessageA; } PeekMessageA; } servant; } diff --git a/Nest/Container/Container.h b/Nest/Container/Container.h index cf205610..5d4e7533 100644 --- a/Nest/Container/Container.h +++ b/Nest/Container/Container.h @@ -357,6 +357,20 @@ namespace fgo::nest break; } + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + { + // モーダルダイアログを閉じた直後などで + // ショートカットキーが利かなくなっていた問題に対処. + // メッセージをAviUtlのメインウィンドウに転送します. + return ::SendMessage(hive.aviutlWindow, message, wParam, lParam); + } case WM_ACTIVATE: case WM_COMMAND: case WM_CLOSE: @@ -364,6 +378,14 @@ namespace fgo::nest // メッセージをそのままターゲットウィンドウに転送します。 return ::SendMessage(content->getHWND(), message, wParam, lParam); } + case WM_SYSCOMMAND: + { + // Windows 標準のコマンドでないならターゲットウィンドウに転送して処理させます. + // これでサブウィンドウタイトルの右クリックメニューから名前の変更ができます. + if (wParam < 0xF000) + ::PostMessage(content->getHWND(), message, wParam, lParam); + break; + } } switch (message) diff --git a/Nest/DockSite.h b/Nest/DockSite.h index 38009efe..2c192665 100644 --- a/Nest/DockSite.h +++ b/Nest/DockSite.h @@ -400,14 +400,26 @@ namespace fgo::nest if (id) { + // 原点を切り替えたときボーダーの位置が変わらないよう調整します. + constexpr auto flip_border = [](auto& pane, int origin) { + if (pane->origin == origin) return; + pane->origin = origin; + if (pane->splitMode == Pane::SplitMode::none) return; + + pane->border = -pane->borderWidth - pane->border; + switch (pane->splitMode) { + case Pane::SplitMode::horz: pane->border += pane->position.bottom - pane->position.top; break; + case Pane::SplitMode::vert: pane->border += pane->position.right - pane->position.left; break; + } + }; switch (id) { case CommandID::SPLIT_MODE_NONE: pane->setSplitMode(Pane::SplitMode::none); break; case CommandID::SPLIT_MODE_VERT: pane->setSplitMode(Pane::SplitMode::vert); break; case CommandID::SPLIT_MODE_HORZ: pane->setSplitMode(Pane::SplitMode::horz); break; - case CommandID::ORIGIN_TOP_LEFT: pane->origin = Pane::Origin::topLeft; break; - case CommandID::ORIGIN_BOTTOM_RIGHT: pane->origin = Pane::Origin::bottomRight; break; + case CommandID::ORIGIN_TOP_LEFT: flip_border(pane, Pane::Origin::topLeft); break; + case CommandID::ORIGIN_BOTTOM_RIGHT: flip_border(pane, Pane::Origin::bottomRight); break; case CommandID::MOVE_TO_LEFT: pane->moveTab(ht, ht - 1); break; case CommandID::MOVE_TO_RIGHT: pane->moveTab(ht, ht + 1); break; @@ -705,8 +717,13 @@ namespace fgo::nest { // クリックされたペインがシャトルを持っているなら Shuttle* shuttle = pane->getCurrentShuttle(); - if (shuttle) + if (shuttle) { ::SetFocus(*shuttle); // そのシャトルにフォーカスを当てます。 + if (pane->hitTestCaption(point)) + // キャプションをクリックしたときは, + // その親のサブウィンドウにフォーカスが移らないよう処理済み扱いにします. + return 0; + } } } diff --git a/Nest/Pane/Pane.h b/Nest/Pane/Pane.h index 0b8a23a5..b75c68a7 100644 --- a/Nest/Pane/Pane.h +++ b/Nest/Pane/Pane.h @@ -671,6 +671,11 @@ namespace fgo::nest break; } } + if (limited) + // タブコントロールのサイズが変わらないと再描画が行われないらしい. + // 最大化モードで再描画を抑制中にレイアウトした後で移動が起こった場合, + // アーティファクトが残っていたので対処. + ::InvalidateRect(tab, nullptr, FALSE); } // タブが1個以下なら else diff --git a/Nest/SubWindow.h b/Nest/SubWindow.h index 2e3a051c..b13a81ef 100644 --- a/Nest/SubWindow.h +++ b/Nest/SubWindow.h @@ -52,6 +52,23 @@ namespace fgo::nest parent, 0, hive.instance, 0); } + // + // Shuttle の init() を呼び出して,システムメニューに項目を追加します. + // + void init(const _bstr_t& name, HWND hwnd) { + __super::init(name, hwnd); + + // フローティングコンテナのアイコンを設定します。 + HICON icon = (HICON)::GetClassLong(hive.aviutlWindow, GCL_HICON); + ::SendMessage(*floatContainer, WM_SETICON, ICON_SMALL, (LPARAM)icon); + ::SendMessage(*floatContainer, WM_SETICON, ICON_BIG, (LPARAM)icon); + + // フローティングコンテナのシステムメニューにメニュー項目とセパレータを追加します. + HMENU menu = ::GetSystemMenu(*floatContainer, FALSE); + ::InsertMenu(menu, 0, MF_BYPOSITION | MF_STRING, CommandID::RENAME_SUB_WINDOW, _T("名前を変更")); + ::InsertMenu(menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr); + } + // // ウィンドウプロシージャです。 // @@ -67,6 +84,37 @@ namespace fgo::nest // これにより、現存するすべてのサブウィンドウにアクセスできるようになります。 subWindowManager.add(hwnd); + break; + } + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + { + // Nest の MainWindow にフォーカスがある状態から + // フロート状態の SubWindow のタイトルをクリックしてフォーカスを移動すると, + // ショートカットキーが利かなくなっていた問題に対処. + // メッセージをAviUtlのメインウィンドウに転送します. + return ::SendMessage(hive.aviutlWindow, message, wParam, lParam); + } + case WM_SYSCOMMAND: + { + // SubWindow を保持する Container クラスから送られてきます. + switch (wParam) { + case CommandID::RENAME_SUB_WINDOW: + { + auto container = getCurrentContainer(); + if (!container) break; + + // サブウィンドウの名前変更ダイアログを表示します. + shuttleManager.showRenameDialog(this, *container); + break; + } + } break; } case WM_CLOSE: