WM_IME_COMPOSITIONはlParamによって取得できる情報を区別している。特にGCS_COMPATTRは変換中の文字列の一文字ずつの情報を扱うのでATTR_*でさらに分岐が必要。
#pragma warning(disable:4996) #include <windows.h> #include <imm.h> #pragma comment(lib, "imm32.lib") #include <string> // https://learn.microsoft.com/ja-jp/windows/win32/intl/ime-composition-string-values
// 変換中の文字列 GCS_COMPSTR void test_GCS_COMPSTR(HIMC hIMC, HDC hdc) { DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); if (inputBytes) { size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数 wchar_t* buffer = new wchar_t[U16Count]; // 確定した文字列を取得 ImmGetCompositionStringW(hIMC, GCS_COMPSTR, buffer, inputBytes); buffer[U16Count - 1] = L'\0'; // 確定した文字列を描画 int inx = 0; int iny = 0; TextOutW(hdc, inx, iny, buffer, wcslen(buffer)); delete[] buffer; } }
// 変換中の文字列の属性情報 GCS_COMPATTR void test_GCS_COMPATTR(HIMC hIMC, HDC hdc) { DWORD attr = ImmGetCompositionString(hIMC, GCS_COMPATTR, NULL, 0); BYTE* buffer = new BYTE[attr];// 変換中の文字数と同じ個数の要素を確保 ImmGetCompositionString(hIMC, GCS_COMPATTR, buffer, attr);// 変換中の各文字ごとに状態情報が格納される char str[256] = {}; for (size_t i = 0; i < 256; i++) str[i] = ' '; str[255] = '\0'; TextOutA(hdc, 0, 30, str, strlen(str)); size_t i; for (i = 0; i < attr; i++) { switch (buffer[i]) { case ATTR_INPUT: str[i] = 'I'; break; case ATTR_TARGET_CONVERTED: str[i] = 'T'; break; case ATTR_CONVERTED: str[i] = 'C'; break; case ATTR_FIXEDCONVERTED: str[i] = 'F'; break; case ATTR_TARGET_NOTCONVERTED: str[i] = 'N'; break; case ATTR_INPUT_ERROR: str[i] = 'E'; break; } } str[i] = '*'; TextOutA(hdc, 0, 30, str, strlen(str)); delete[] buffer; }
// 変換中の文字列内のカーソル位置 void test_GCS_CURSORPOS(HIMC hIMC, HDC hdc) { LONG cursorPos = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0); char str[256] = {}; sprintf(str, "cursorPos:%d", cursorPos); TextOutA(hdc, 0, 50, str, strlen(str)); }
// 変換開始位置 GCS_DELTASTART void test_GCS_DELTASTART(HIMC hIMC, HDC hdc) { LONG deltaStart = ImmGetCompositionString(hIMC, GCS_DELTASTART, NULL, 0); char str[256] = {}; sprintf(str, "deltaStart:%d", deltaStart); TextOutA(hdc, 0, 90, str, strlen(str)); }
// GCS_RESULTSTR void test_GCS_RESULTSTR(HIMC hIMC,HDC hdc) { // GCS_RESULTSTR ... IMEの入力が確定されたフラグ // 確定した文字列のバイト数を取得(NULLは含まない) DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); if (inputBytes) { size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数 wchar_t* buffer = new wchar_t[U16Count]; // 確定した文字列を取得 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buffer, inputBytes); buffer[U16Count - 1] = L'\0'; // 確定した文字列を描画 int inx = 100; int iny = 100; TextOutW(hdc, inx, iny, buffer, wcslen(buffer)); delete[] buffer; } }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HIMC hIMC; int inx = 100; int iny = 100; switch (message) { case WM_CREATE: // IMEを使用可能にする ImmAssociateContext(hWnd, ImmCreateContext()); break; case WM_IME_STARTCOMPOSITION: OutputDebugString(L"IME 入力開始\n"); hIMC = ImmGetContext(hWnd); // IMEコンテキストを取得 if (hIMC) { // IMEのウィンドウ位置を設定 COMPOSITIONFORM cf2; cf2.dwStyle = CFS_POINT; cf2.ptCurrentPos.x = inx; cf2.ptCurrentPos.y = iny; ImmSetCompositionWindow(hIMC, &cf2); // IMEのウィンドウ位置を設定 ImmReleaseContext(hWnd, hIMC); } break; case WM_IME_COMPOSITION: // IMEの入力中の処理 hIMC = ImmGetContext(hWnd); if (hIMC) { HDC hdc = GetDC(hWnd); if (lParam & GCS_RESULTSTR) { test_GCS_RESULTSTR(hIMC,hdc); } if (lParam & GCS_COMPSTR) { test_GCS_COMPSTR(hIMC, hdc); } if (lParam & GCS_COMPATTR) { test_GCS_COMPATTR(hIMC, hdc); } if (lParam & GCS_CURSORPOS) { test_GCS_CURSORPOS(hIMC, hdc); } if (lParam & GCS_DELTASTART) { test_GCS_DELTASTART(hIMC, hdc); } ReleaseDC(hWnd, hdc); ImmReleaseContext(hWnd, hIMC); } break; case WM_IME_ENDCOMPOSITION: OutputDebugString(L"IME 入力終了\n"); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam);; }
imm.h,imm32.libを読み込み、ImmAssociateContextなどを使ってIMEへアクセスする。
#include <windows.h> #include <imm.h> #pragma comment(lib, "imm32.lib") LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HIMC hIMC; int inx = 100; int iny = 100; switch (message) { case WM_CREATE:
// IMEを使用可能にする ImmAssociateContext(hWnd, ImmCreateContext()); break;
case WM_IME_STARTCOMPOSITION:
// IMEでの入力が開始される
OutputDebugString(L"IME 入力開始\n"); hIMC = ImmGetContext(hWnd); // IMEコンテキストを取得 if (hIMC) { // IMEのウィンドウ位置を設定 COMPOSITIONFORM cf2; cf2.dwStyle = CFS_POINT; cf2.ptCurrentPos.x = inx; cf2.ptCurrentPos.y = iny; ImmSetCompositionWindow(hIMC, &cf2); // IMEのウィンドウ位置を設定 ImmReleaseContext(hWnd, hIMC); } break;
case WM_IME_COMPOSITION:
// IMEの入力中の処理 hIMC = ImmGetContext(hWnd); if (hIMC) { if (lParam & GCS_RESULTSTR) { // GCS_RESULTSTR ... IMEの入力が確定されたフラグ // 確定した文字列のバイト数を取得(NULLは含まない) DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); if (inputBytes) { size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数 wchar_t* buffer = new wchar_t[U16Count]; // 確定した文字列を取得 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buffer, inputBytes); buffer[U16Count - 1] = L'\0'; // 確定した文字列を描画 HDC hdc = GetDC(hWnd); TextOutW(hdc, inx, iny, buffer, wcslen(buffer)); ReleaseDC(hWnd, hdc); delete[] buffer; } } ImmReleaseContext(hWnd, hIMC); } break;
case WM_IME_ENDCOMPOSITION:
OutputDebugString(L"IME 入力終了\n"); break;
case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam);; } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = L"SZL-IME-TEST"; RegisterClass(&wc); HWND hWnd = CreateWindowW(L"SZL-IME-TEST", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
#ifndef TREE_H #define TREE_H #endif //TREE_H #include <algorithm> #include <memory> using namespace std::literals::string_literals; // ノードを継承したクラスに asString() メンバ関数があるかを判定するコンセプト template<typename T> concept HasAsString = requires(T a) { { a.asString() } -> std::same_as<std::string>; }; // ノードの基底クラス class NodeBase { protected: public: std::shared_ptr<NodeBase> parent; virtual ~NodeBase() = default; virtual std::string asString() const = 0; // 必ず実装させる仮想関数 virtual std::shared_ptr<NodeBase> getParent() const { return parent; } virtual bool isFolder() const = 0; }; // フォルダクラス template <HasAsString T> class Folder : public NodeBase, public std::enable_shared_from_this < Folder<T> > { std::vector<std::shared_ptr<NodeBase>> children; // 子ノードは NodeBase 型を保持 T value; bool isOpen; public: Folder(const T& data) : value(data), isOpen(false) {} // 子ノードを追加するメソッド void add(const std::shared_ptr<NodeBase>& node) { children.push_back(node); node->parent = std::dynamic_pointer_cast<NodeBase>(this->shared_from_this()); } const T& getName() const { return value; } std::string asString() const override { return value.asString(); } const std::vector<std::shared_ptr<NodeBase>>& getChildren() const { return children; } std::vector<std::shared_ptr<NodeBase>>& getChildren() { return children; } bool getIsOpen() const { return isOpen; } void setIsOpen(bool open) { isOpen = open; } bool isFolder() const override { return true; } }; // アイテムクラス // フォルダに入れるデータを保持するクラス template <HasAsString T> class Item : public NodeBase, public std::enable_shared_from_this < Item<T> > { T value; public: Item(const T& data) : value(data) {} std::string asString() const override { return value.asString(); } bool isFolder() const override { return false; } }; ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// // 全てのノードの数をカウントする関数 template <HasAsString FT> int getAllNodeCount(const std::shared_ptr<NodeBase>& node) { int count = 1; // 自分自身をカウント // フォルダの場合は、子ノードも再帰的にカウント if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { for (const auto& child : folder->getChildren()) { count += getAllNodeCount<FT>(child); } } return count; } // 開いているノードの数をカウントする関数 template <HasAsString FT> int getOpenNodeCount(const std::shared_ptr<NodeBase>& node) { int count = 1; // 自分自身をカウント // フォルダの場合、開いているかどうかを確認 if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (folder->getIsOpen()) { // 開いている場合、子ノードも再帰的にカウント for (const auto& child : folder->getChildren()) { count += getOpenNodeCount<FT>(child); } } } return count; } //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// // 指定した index のノードを取得する関数の補助関数 template <HasAsString FT> std::shared_ptr<NodeBase> getNodeHelper(const std::shared_ptr<NodeBase>& node, int& currentIndex, int targetIndex, bool open = false) { // 現在のノードをカウント if (currentIndex == targetIndex) { return node; } currentIndex++; // フォルダの場合は、開閉状態を確認し、再帰的に子ノードを探索 if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (open || folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { auto result = getNodeHelper<FT>(child, currentIndex, targetIndex, open); if (result) { return result; } } } } return nullptr; // 見つからなかった場合 } // 指定した index のノードを取得する関数 // ここでの index は、開いているノードのみでカウントする template <HasAsString FT> std::shared_ptr<NodeBase> getNodeOnlyOpen(const std::shared_ptr<NodeBase>& root, int targetIndex) { int currentIndex = 0; // ノードのカウント用 return getNodeHelper<FT>(root, currentIndex, targetIndex, false); } //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// // ノードを削除する関数 template <HasAsString FT> void deleteNode(std::shared_ptr<NodeBase> node) { if (auto parent = node->getParent()) { if (auto parentFolder = std::dynamic_pointer_cast<Folder<FT>>(parent)) { auto& siblings = parentFolder->getChildren(); siblings.erase(std::remove(siblings.begin(), siblings.end(), node), siblings.end()); } } } // 指定したノードの管理情報を取得する関数 // 戻り値 1 親ノード // 戻り値 2 posで指定したノードを持つ親ノードの、子ノードリストのインデクス template <HasAsString FT> std::pair< std::shared_ptr<NodeBase>, int> GetNodeListPos(std::shared_ptr<NodeBase> root, int pos) { // posの位置の現在のノードを取得 auto now_node = getNodeOnlyOpen<FT>(root, pos); // now_nodeの親ノードを取得 auto parent_node = now_node->getParent(); // now_nodeの親ノードの子ノードリストを取得 auto parent_folder = std::dynamic_pointer_cast<Folder<FT>>(parent_node); auto& siblings = parent_folder->getChildren(); // now_nodeの親ノードの子ノードリストのインデクスを取得 auto it = std::find(siblings.begin(), siblings.end(), now_node); int index = std::distance(siblings.begin(), it); return { parent_node,index }; } template <HasAsString FT, HasAsString IT> void addNodeUnder(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node) { // nodeをrootの最後に追加 auto folder = std::dynamic_pointer_cast<Folder<FT>>(root); folder->getChildren().push_back(node); node->parent = root; } template <HasAsString FT, HasAsString IT> void addNodeHere(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node, int pos) { // ノードを追加する関数 // 挿入位置は番号posで指定する // 新しく追加したアイテムが必ずpos番目になる // 必ず、追加前のposのノードと同じ階層に追加する // 番号posは開いているノードのみでカウントする // 例えばfolder21がノードを持っていても、folder21は開いていないのでその中のノードはカウントしない // 1 - folder1 // 2 - folder2 // 3 item21 // 4 item22 // 5 + folder21 // 6 - folder3 // 7 item31 // 8 item32 // 9 item33 // getNode(folder1,3) == item21 // この時、 // 例1 addNode(NewFolder,3) で // 2 - folder2 // 3 + NewFolder // 4 item21 // 5 item22 auto childinfo = GetNodeListPos<FT>(root, pos); std::shared_ptr<NodeBase> childlist = childinfo.first; auto index = childinfo.second; // nodeをchildlistのindex番に追加 auto folder = std::dynamic_pointer_cast<Folder<FT>>(childlist); folder->getChildren().insert(folder->getChildren().begin() + index, node); node->parent = childlist; } //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// struct TreeSliceItem { int pos; std::shared_ptr<NodeBase> node; int hierarchy; }; template <HasAsString FT> void GetTreeSliceTraverse(const std::shared_ptr<NodeBase>& node, int hierarchy, int& currentIndex, int from, int to, std::vector<TreeSliceItem>& slice) { if (currentIndex >= from && currentIndex <= to) { slice.push_back({ currentIndex, node, hierarchy }); } currentIndex++; if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { GetTreeSliceTraverse<FT>(child, hierarchy + 1, currentIndex, from, to, slice); } } } } // ツリーから一部を取り出す関数 template <HasAsString FT> std::vector< TreeSliceItem> GetTreeSlice(std::shared_ptr<NodeBase> root, int from, int to) { // 例えば、以下の状態の時、 // 1 - folder1 // 2 - folder2 // 3 item21 // 4 item22 // 5 + folder21 // 6 - folder3 // 7 item31 // 8 item32 // 9 item33 // GetTreeSlice(folder1,3,7) で // 3 item21 // 4 item22 // 5 + folder21 // 6 - folder3 // 7 item31 // を取得 std::vector<TreeSliceItem> slice; int currentIndex = 0; int currentHierarchy = 0; GetTreeSliceTraverse<FT>(root, currentHierarchy, currentIndex, from, to, slice); return slice; }
ツリーの一部を取得するクラスを取得するクラス。
#include "tree.hpp"
// ツリーの一部を取り出すクラス
template <HasAsString FT, HasAsString IT> class TreeSlice { std::shared_ptr<Folder<FT>> _root; std::vector< TreeSliceItem> _items; int _size; int _pos; public: void SetTree(const std::shared_ptr<Folder<FT>>& r) { _root = r; } void SetSliceInfo(int size,int pos) { _size = size; _pos = pos; } void GetSlice() { _items = GetTreeSlice<FT>(_root, _pos, _pos + _size - 1); } const std::vector< TreeSliceItem>& GetItems() const { return _items; } };
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
struct FolderData { std::string name; std::string asString() const { return name; } // コンストラクタ FolderData(const std::string& n) : name(n) {} };
struct ItemData { std::string name; std::string asString() const { return name; } // コンストラクタ ItemData(const std::string& n) : name(n) {} };
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 // wxBufferedPaintDC のinclude #include <wx/dcbuffer.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> #include <memory> #include <unordered_map> #include "tree_data.hpp"
// データを作成 std::shared_ptr<Folder<FolderData>> create_data() { // ツリー構造を作成 auto root = std::make_shared<Folder<FolderData>>(FolderData{ "folder1" }); for (size_t i = 0; i < 10; i++) { auto folder = std::make_shared<Folder<FolderData>>(FolderData{ "folder" + std::to_string(i) }); root->add(folder); for (size_t j = 0; j < 10; j++) { auto item = std::make_shared<Item<ItemData>>(ItemData{ "item" + std::to_string(i) + std::to_string(j) }); folder->add(item); } } root->setIsOpen(true); return root; }
// リストボックスが一度に表示可能なアイテム数を取得 int GetVisibleItemCount(wxListBox * listBox) { // リストボックスのDCを使って一行の高さを取得し // リストボックスのクライアントサイズから表示可能なアイテム数を計算する // リストボックスのクライアントサイズを取得 wxSize clientSize = listBox->GetClientSize(); // wxClientDCを使用してデフォルトのアイテム高さを取得 wxClientDC dc(listBox); wxCoord textWidth, textHeight; // 指定した文字列の幅と高さを取得 dc.GetTextExtent("TEXT", &textWidth, &textHeight); // 表示可能なアイテム数を計算 int visibleItemCount = clientSize.GetHeight() / textHeight; return visibleItemCount; }
// スクロールバー付きリスト class ExList : public wxPanel { int itemCount;// アイテム数 int visibleItemCount;// 一度に表示できるアイテム数 wxBoxSizer* mainSizer; wxScrollBar* scrollBar; // リストコントロール wxListBox* listBox; TreeSlice<FolderData, ItemData> _treeSlice;// リストの一部を取得 std::shared_ptr<Folder<FolderData>> _root;// ツリーのルート std::vector< TreeSliceItem> _now_valid_items;// 表示する範囲のツリーのアイテム void PostCreate() { _root = create_data();// アイテム作成 _treeSlice.SetTree(_root);// ツリーの一部を切り出す準備 visibleItemCount = GetVisibleItemCount(listBox);// 表示可能なアイテム個数を更新 disp(); } public: // コンストラクタ ExList(wxWindow* parent) : wxPanel(parent, wxID_ANY) { // メインSizer mainSizer = new wxBoxSizer(wxHORIZONTAL); this->SetSizer(mainSizer); // リストコントロール listBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SINGLE); mainSizer->Add(listBox, 1, wxEXPAND | wxALL, 5); // スクロールバー scrollBar = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL); mainSizer->Add(scrollBar, 0, wxEXPAND | wxALL, 5); this->Layout(); CallAfter(&ExList::PostCreate); // スクロールバーのイベント scrollBar->Bind(wxEVT_SCROLL_PAGEUP, &ExList::OnScrollPageUp, this); scrollBar->Bind(wxEVT_SCROLL_PAGEDOWN, &ExList::OnScrollPageDown, this); scrollBar->Bind(wxEVT_SCROLL_THUMBTRACK, &ExList::OnScrollThumbTrack, this); scrollBar->Bind(wxEVT_SCROLL_LINEUP, &ExList::OnScrollUp, this); scrollBar->Bind(wxEVT_SCROLL_LINEDOWN, &ExList::OnScrollDown, this); // リストボックスのアイテムをクリックした時のイベント listBox->Bind(wxEVT_LISTBOX, &ExList::OnListBox, this); // サイズイベント this->Bind(wxEVT_SIZE, &ExList::OnSize, this); }
void disp() { listBox->Clear(); int offset = scrollBar->GetThumbPosition(); itemCount = getOpenNodeCount<FolderData>(_root); // スクロールバーの範囲とサムサイズを設定 scrollBar->SetScrollbar(offset, visibleItemCount, itemCount, 5, true); _treeSlice.SetSliceInfo(visibleItemCount, offset); _treeSlice.GetSlice(); _now_valid_items = _treeSlice.GetItems(); // デバッグにposを表示 std::string debugMessage = "pos: " + std::to_string(offset) + "\n"; OutputDebugStringA(debugMessage.c_str()); ///////////////////////////////////////////// // 現在の表示範囲のアイテムを追加 for (int i = 0; i < _now_valid_items.size(); i++) { std::string itemstr; // itemstr にアイテムの文字列を設定 // hierarchy個のスペースを追加 for (int j = 0; j < _now_valid_items[i].hierarchy; j++) { itemstr += " "; } auto node = _now_valid_items[i].node; if (node->isFolder()) { std::dynamic_pointer_cast<Folder<FolderData>>(node)->getIsOpen() ? itemstr += "- " : itemstr += "+ "; } else { itemstr += " "; } itemstr += node->asString(); listBox->Append(itemstr);// アイテムをリストに追加 } }
// スクロールバーのつまみ以外の部分をクリックしたとき void OnScrollPageUp(wxScrollEvent& event) { disp(); } void OnScrollPageDown(wxScrollEvent& event) { disp(); } void OnScrollThumbTrack(wxScrollEvent& event) { disp(); } // 下方向クリック void OnScrollDown(wxScrollEvent& event) { disp(); } // 上方向クリック void OnScrollUp(wxScrollEvent& event) { disp(); } // リストボックスのアイテムをクリックした時のイベント void OnListBox(wxCommandEvent& event) { // 選択されたアイテムのインデックスを取得 int index = listBox->GetSelection(); if (index == wxNOT_FOUND) { return; } auto node = _now_valid_items[index].node; std::string debugMessage = "Selected: " + node->asString() + "\n"; OutputDebugStringA(debugMessage.c_str()); auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node); if (folder) { folder->setIsOpen(!folder->getIsOpen());// クリックしたフォルダをオープン disp(); } } void OnSize(wxSizeEvent& event) { visibleItemCount = GetVisibleItemCount(listBox);// 表示可能なアイテム個数を更新 disp(); // イベントを処理済みにする event.Skip(); } };
// ウィンドウ作成 class MyFrame : public wxFrame { wxBoxSizer* mainSizer; ExList* exList; // スクロールバー付きリスト public: void PostCreate() { this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // メインSizer mainSizer = new wxBoxSizer(wxHORIZONTAL); this->SetSizer(mainSizer); // リストコントロール exList = new ExList(this); mainSizer->Add(exList, 1, wxEXPAND | wxALL, 5); // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 // wxBufferedPaintDC のinclude #include <wx/dcbuffer.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> #include <memory> #include <unordered_map>
// リストボックスが一度に表示可能なアイテム数を取得 int GetVisibleItemCount(wxListBox * listBox) { // リストボックスのDCを使って一行の高さを取得し // リストボックスのクライアントサイズから表示可能なアイテム数を計算する // リストボックスのクライアントサイズを取得 wxSize clientSize = listBox->GetClientSize(); // wxClientDCを使用してデフォルトのアイテム高さを取得 wxClientDC dc(listBox); wxCoord textWidth, textHeight; // 指定した文字列の幅と高さを取得 dc.GetTextExtent("TEXT", &textWidth, &textHeight); // 表示可能なアイテム数を計算 int visibleItemCount = clientSize.GetHeight() / textHeight; return visibleItemCount; }
// スクロールバー付きリスト class ExList : public wxPanel { int itemCount = 100; // アイテム数 int visibleItemCount;// 一度に表示できるアイテム数 wxBoxSizer* mainSizer; wxScrollBar* scrollBar; // リストコントロール wxListBox* listBox;
void PostCreate() { visibleItemCount = GetVisibleItemCount(listBox); // スクロールバーの範囲とサムサイズを設定 scrollBar->SetScrollbar(0, visibleItemCount, itemCount, 5, true); disp(); }
public: // コンストラクタ ExList(wxWindow* parent) : wxPanel(parent, wxID_ANY) { // メインSizer mainSizer = new wxBoxSizer(wxHORIZONTAL); this->SetSizer(mainSizer); // リストコントロール listBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr); mainSizer->Add(listBox, 1, wxEXPAND | wxALL, 5); // スクロールバー scrollBar = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL); mainSizer->Add(scrollBar, 0, wxEXPAND | wxALL, 5); this->Layout(); CallAfter(&ExList::PostCreate); // スクロールバーのイベント scrollBar->Bind(wxEVT_SCROLL_PAGEUP, &ExList::OnScrollPageUp, this); scrollBar->Bind(wxEVT_SCROLL_PAGEDOWN, &ExList::OnScrollPageDown, this); scrollBar->Bind(wxEVT_SCROLL_THUMBTRACK, &ExList::OnScrollThumbTrack, this); scrollBar->Bind(wxEVT_SCROLL_LINEUP, &ExList::OnScrollUp, this); scrollBar->Bind(wxEVT_SCROLL_LINEDOWN, &ExList::OnScrollDown, this); // サイズイベント this->Bind(wxEVT_SIZE, &ExList::OnSize, this); }
void disp() { listBox->Clear(); int offset = scrollBar->GetThumbPosition(); // デバッグにposを表示 std::string debugMessage = "pos: " + std::to_string(offset) + "\n"; OutputDebugStringA(debugMessage.c_str()); ///////////////////////////////////////////// // 現在の表示範囲のアイテムを追加 for (int i = 0; i < visibleItemCount; i++) { int item_id = offset + i; // 番外のアイテムは *** で表示 if (item_id >= itemCount) { listBox->Append("***"); continue; } listBox->Append(wxString::Format("Item %d", item_id)); } }
void OnScrollPageUp(wxScrollEvent& event) { disp(); } void OnScrollPageDown(wxScrollEvent& event) { disp(); } void OnScrollThumbTrack(wxScrollEvent& event) { disp(); } // 下方向クリック void OnScrollDown(wxScrollEvent& event) { disp(); } // 上方向クリック void OnScrollUp(wxScrollEvent& event) { disp(); } void OnSize(wxSizeEvent& event) { // イベントを処理済みにする event.Skip(); } }; // ウィンドウ作成 class MyFrame : public wxFrame { wxBoxSizer* mainSizer; ExList* exList; // スクロールバー付きリスト public: void PostCreate() { this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // メインSizer mainSizer = new wxBoxSizer(wxHORIZONTAL); this->SetSizer(mainSizer); // リストコントロール exList = new ExList(this); mainSizer->Add(exList, 1, wxEXPAND | wxALL, 5); // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
wxListBoxに一度に表示可能な最大アイテム数を求めたい。アイテムがある場合は、GetItemRect関数を使って要素の高さを取得できるので、コントロールのクライアント領域で割れば個数が得られる
int GetVisibleItemCount2(wxListBox* listBox) { // アイテムを一つ追加し、そのアイテムの高さを取得 listBox->Append("Sample Item"); wxRect rect; listBox->GetItemRect(0, rect); listBox->Clear(); // 追加したアイテムを削除 // アイテムの高さを取得 wxSize clientSize = listBox->GetClientSize(); // 表示可能なアイテム数を計算 int visibleItemCount = clientSize.GetHeight() / rect.height; return visibleItemCount; }
アイテムがない場合、dc.GetTextExtentで文字列の高さを取得し、クライアントの高さをそれで割ることで取得できる。
// リストボックスが一度に表示可能なアイテム数を取得 int GetVisibleItemCount(wxListBox * listBox) { // リストボックスのDCを使って一行の高さを取得し // リストボックスのクライアントサイズから表示可能なアイテム数を計算する // リストボックスのクライアントサイズを取得 wxSize clientSize = listBox->GetClientSize(); // wxClientDCを使用してデフォルトのアイテム高さを取得 wxClientDC dc(listBox); wxCoord textWidth, textHeight; // 指定した文字列の幅と高さを取得 dc.GetTextExtent("TEXT", &textWidth, &textHeight); // 表示可能なアイテム数を計算 int visibleItemCount = clientSize.GetHeight() / textHeight; return visibleItemCount; }
// ウィンドウ作成 class MyFrame : public wxFrame { // リストコントロール wxListBox* listBox; wxBoxSizer* mainSizer; public: void PostCreate() { int itemCount = listBox->GetCount(); int count = GetVisibleItemCount(listBox); int count2 = GetVisibleItemCount2(listBox); assert(count == count2); // listBoxにcount個のアイテムを追加 for (int i = 0; i < count; i++) { listBox->Append(wxString::Format("Item %d", i + 1)); } // メッセージボックスでcountを表示 wxMessageBox(wxString::Format("表示可能なアイテム数: %d %d", count,count2)); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // メインSizer mainSizer = new wxBoxSizer(wxHORIZONTAL); this->SetSizer(mainSizer); // リストコントロール listBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr); mainSizer->Add(listBox, 1, wxEXPAND | wxALL, 5); // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: };
いろいろ機能を追加した。
p 番号 ... ノード表示
s 番号 ... ツリーの一部を表示
afh 番号 フォルダ名 ... 指定した番号の位置にフォルダを追加
aih 番号 アイテム名 ... 指定した番号の位置にアイテムを追加
afu 番号 フォルダ名 ... 指定した番号のフォルダの子要素としてフォルダを追加
aif 番号 アイテム名 ... 指定した番号のフォルダの子要素としてアイテムを追加
#ifndef TREE_H #define TREE_H #endif //TREE_H #include <algorithm> using namespace std::literals::string_literals;
// ノードを継承したクラスに asString() メンバ関数があるかを判定するコンセプト template<typename T> concept HasAsString = requires(T a) { { a.asString() } -> std::same_as<std::string>; };
// ノードの基底クラス class NodeBase { protected: public: std::shared_ptr<NodeBase> parent; virtual ~NodeBase() = default; virtual std::string asString() const = 0; // 必ず実装させる仮想関数 virtual std::shared_ptr<NodeBase> getParent() const { return parent; } };
// フォルダクラス template <HasAsString T> class Folder : public NodeBase, public std::enable_shared_from_this < Folder<T> > { std::vector<std::shared_ptr<NodeBase>> children; // 子ノードは NodeBase 型を保持 T value; bool isOpen; public: Folder(const T& data) : value(data), isOpen(false) {} // 子ノードを追加するメソッド void add(const std::shared_ptr<NodeBase>& node) { children.push_back(node); node->parent = std::dynamic_pointer_cast<NodeBase>(this->shared_from_this()); } const T& getName() const { return value; } std::string asString() const override { return value.asString(); } const std::vector<std::shared_ptr<NodeBase>>& getChildren() const { return children; } std::vector<std::shared_ptr<NodeBase>>& getChildren() { return children; } bool getIsOpen() const { return isOpen; } void setIsOpen(bool open) { isOpen = open; } };
// アイテムクラス // フォルダに入れるデータを保持するクラス template <HasAsString T> class Item : public NodeBase, public std::enable_shared_from_this < Item<T> > { T value; public: Item(const T& data) : value(data) {} std::string asString() const override { return value.asString(); } };
///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// // 全てのノードの数をカウントする関数 template <HasAsString FT> int getAllNodeCount(const std::shared_ptr<NodeBase>& node) { int count = 1; // 自分自身をカウント // フォルダの場合は、子ノードも再帰的にカウント if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { for (const auto& child : folder->getChildren()) { count += getAllNodeCount<FT>(child); } } return count; }
// 開いているノードの数をカウントする関数 template <HasAsString FT> int getOpenNodeCount(const std::shared_ptr<NodeBase>& node) { int count = 1; // 自分自身をカウント // フォルダの場合、開いているかどうかを確認 if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (folder->getIsOpen()) { // 開いている場合、子ノードも再帰的にカウント for (const auto& child : folder->getChildren()) { count += getOpenNodeCount<FT>(child); } } } return count; }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
// 指定した index のノードを取得する関数の補助関数 template <HasAsString FT> std::shared_ptr<NodeBase> getNodeHelper(const std::shared_ptr<NodeBase>& node, int& currentIndex, int targetIndex, bool open = false) { // 現在のノードをカウント if (currentIndex == targetIndex) { return node; } currentIndex++; // フォルダの場合は、開閉状態を確認し、再帰的に子ノードを探索 if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (open || folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { auto result = getNodeHelper<FT>(child, currentIndex, targetIndex, open); if (result) { return result; } } } } return nullptr; // 見つからなかった場合 }
// 指定した index のノードを取得する関数 // ここでの index は、開いているノードのみでカウントする template <HasAsString FT> std::shared_ptr<NodeBase> getNodeOnlyOpen(const std::shared_ptr<NodeBase>& root, int targetIndex) { int currentIndex = 0; // ノードのカウント用 return getNodeHelper<FT>(root, currentIndex, targetIndex,false); }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////// ノードを削除する関数 template <HasAsString FT> void deleteNode(std::shared_ptr<NodeBase> node) { if (auto parent = node->getParent()) { if (auto parentFolder = std::dynamic_pointer_cast<Folder<FT>>(parent)) { auto& siblings = parentFolder->getChildren(); siblings.erase(std::remove(siblings.begin(), siblings.end(), node), siblings.end()); } } }
// 指定したノードの管理情報を取得する関数 // 戻り値 1 親ノード // 戻り値 2 posで指定したノードを持つ親ノードの、子ノードリストのインデクス template <HasAsString FT> std::pair< std::shared_ptr<NodeBase>,int> GetNodeListPos(std::shared_ptr<NodeBase> root,int pos) { // posの位置の現在のノードを取得 auto now_node = getNodeOnlyOpen<FT>(root, pos); // now_nodeの親ノードを取得 auto parent_node = now_node->getParent(); // now_nodeの親ノードの子ノードリストを取得 auto parent_folder = std::dynamic_pointer_cast<Folder<FT>>(parent_node); auto& siblings = parent_folder->getChildren(); // now_nodeの親ノードの子ノードリストのインデクスを取得 auto it = std::find(siblings.begin(), siblings.end(), now_node); int index = std::distance(siblings.begin(), it); return { parent_node,index }; }
template <HasAsString FT, HasAsString IT> void addNodeUnder(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node) { // nodeをrootの最後に追加 auto folder = std::dynamic_pointer_cast<Folder<FT>>(root); folder->getChildren().push_back(node); node->parent = root; }
template <HasAsString FT, HasAsString IT> void addNodeHere(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node, int pos) { // ノードを追加する関数 // 挿入位置は番号posで指定する // 新しく追加したアイテムが必ずpos番目になる // 必ず、追加前のposのノードと同じ階層に追加する // 番号posは開いているノードのみでカウントする // 例えばfolder21がノードを持っていても、folder21は開いていないのでその中のノードはカウントしない // 0 - folder1 // 1 - folder2 // 2 item21 // 3 item22 // 4 + folder21 // 5 - folder3 // 6 item31 // 7 item32 // 8 item33 // getNode(folder1,3) == item21 // この時、 // 例1 addNode(NewFolder,2) で // 1 - folder2 // 2 + NewFolder // 3 item21 // 4 item22 auto childinfo = GetNodeListPos<FT>(root, pos); std::shared_ptr<NodeBase> childlist = childinfo.first; auto index = childinfo.second; // nodeをchildlistのindex番に追加 auto folder = std::dynamic_pointer_cast<Folder<FT>>(childlist); folder->getChildren().insert(folder->getChildren().begin() + index, node); node->parent = childlist; }//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
struct TreeSliceItem { int pos; std::shared_ptr<NodeBase> node; int hierarchy; };
template <HasAsString FT> void GetTreeSliceTraverse(const std::shared_ptr<NodeBase>& node, int hierarchy, int& currentIndex, int from, int to, std::vector<TreeSliceItem>& slice) { if (currentIndex >= from && currentIndex <= to) { slice.push_back({ currentIndex, node, hierarchy }); } currentIndex++; if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { GetTreeSliceTraverse<FT>(child, hierarchy + 1, currentIndex, from, to, slice); } } } }
// ツリーから一部を取り出す関数 template <HasAsString FT> std::vector< TreeSliceItem> GetTreeSlice(std::shared_ptr<NodeBase> root, int from,int to) { // 例えば、以下の状態の時、 // 0 - folder1 // 1 - folder2 // 2 item21 // 3 item22 // 4 + folder21 // 5 - folder3 // 6 item31 // 7 item32 // 8 item33 // GetTreeSlice(folder1,2,6) で // 2 item21 // 3 item22 // 4 + folder21 // 5 - folder3 // 6 item31 // を取得 std::vector<TreeSliceItem> slice; int currentIndex = 0; int currentHierarchy = 0; GetTreeSliceTraverse<FT>(root, currentHierarchy, currentIndex, from, to, slice); return slice; }
#include <iostream> #include <iomanip> #include <sstream> #include <memory> #include <vector> #include <string> #include "tree.hpp" //////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// struct FolderData { std::string name; std::string asString() const { return name; } // コンストラクタ FolderData(const std::string& n) : name(n) {} }; struct ItemData { std::string name; std::string asString() const { return name; } // コンストラクタ ItemData(const std::string& n) : name(n) {} }; ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// ノードの番号を付けて表示するための補助関数 template <HasAsString FT, HasAsString IT> void printTreeHelper(const std::shared_ptr<NodeBase>& node, int& lineNum, int indent = 0, bool open = false) { if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { // フォルダの場合 std::cout << std::setw(2) << lineNum++ << std::string(indent+2, ' ') << (folder->getIsOpen() ? "- " : "+ ") << folder->asString() << "\n"; if (open || folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { printTreeHelper<FT, IT>(child, lineNum, indent + 2, open); } } } else if (auto item = std::dynamic_pointer_cast<Item<IT>>(node)) { // アイテムの場合 std::cout << std::setw(2) << lineNum++ << std::string(indent + 2, ' ') << item->asString() << "\n"; } } // 指定したノード以下のツリーを表示する関数 template <HasAsString FT, HasAsString IT> void printTree(const std::shared_ptr<NodeBase>& node, bool open = false) { int lineNum = 0; printTreeHelper<FT, IT>(node, lineNum, 0, open); }
template <HasAsString FT, HasAsString IT> void printTreeSlice(const std::vector<TreeSliceItem>& slice) { for (const auto& item : slice) { if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(item.node)) { std::cout << std::setw(2) << item.pos << std::string(item.hierarchy * 2, ' ') << (folder->getIsOpen() ? "- " : "+ ") << folder->asString() << "\n"; } else if (auto itemNode = std::dynamic_pointer_cast<Item<IT>>(item.node)) { std::cout << std::setw(2) << item.pos << std::string(item.hierarchy * 2, ' ') << itemNode->asString() << "\n"; } } }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
// ノードの親ノードを表示する関数 template <HasAsString FT, HasAsString IT> void printParentChain(const std::shared_ptr<NodeBase>& node) { auto current = node; while (current) { if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(current)) { std::cout << folder->asString() << " -> "; } else if (auto item = std::dynamic_pointer_cast<Item<IT>>(current)) { std::cout << item->asString() << " -> "; } current = current->getParent(); } std::cout << "nullptr\n"; // ルートに到達したら終了 }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
bool input_command(std::string* cmd, int* id, std::string* value) { std::string input; std::getline(std::cin, input); if (input.empty()) return false; std::istringstream iss(input); iss >> *cmd >> *id; if (iss >> std::ws && !iss.eof()) { std::getline(iss, *value); *value = value->substr(value->find_first_not_of(" \t")); // 先頭の空白を削除 } else { *value = ""; } return true; }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// int main() { // ツリー構造を作成 auto root = std::make_shared<Folder<FolderData>>(FolderData{ "folder1" }); auto folder2 = std::make_shared<Folder<FolderData>>(FolderData{ "folder2" }); auto folder21 = std::make_shared<Folder<FolderData>>(FolderData{ "folder21" }); auto item211 = std::make_shared<Item<ItemData>>(ItemData{ "item211" }); auto folder22 = std::make_shared<Folder<FolderData>>(FolderData{ "folder22" }); auto item221 = std::make_shared<Item<ItemData>>(ItemData{ "item221" }); auto item222 = std::make_shared<Item<ItemData>>(ItemData{ "item222" }); auto item223 = std::make_shared<Item<ItemData>>(ItemData{ "item223" }); auto folder23 = std::make_shared<Folder<FolderData>>(FolderData{ "folder23" }); auto folder24 = std::make_shared<Folder<FolderData>>(FolderData{ "folder24" }); auto item241 = std::make_shared<Item<ItemData>>(ItemData{ "item241" }); auto item242 = std::make_shared<Item<ItemData>>(ItemData{ "item242" }); root->add(folder2); folder2->add(folder21); folder21->add(item211); folder2->add(folder22); folder22->add(item221); folder22->add(item222); folder22->add(item223); folder2->add(folder23); folder2->add(folder24); folder24->add(item241); folder24->add(item242); // フォルダの開閉状態を設定 root->setIsOpen(true); folder2->setIsOpen(true); folder22->setIsOpen(true); folder24->setIsOpen(false); std::string cmd = " "; int id; std::string value; do { if (cmd == "*") { // 開閉 auto node = getNodeOnlyOpen<FolderData>(root, id); auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node); folder->setIsOpen(!folder->getIsOpen()); } else if (cmd == "p") { // 指定したノードを表示 auto node = getNodeOnlyOpen<FolderData>(root, id); std::cout<<node->asString() << std::endl; } else if (cmd == "afh") { // 指定した場所にフォルダを追加 ex. afh 1 folder3 addNodeHere<FolderData, ItemData>( root, std::make_shared<Folder<FolderData>>(FolderData{ value }), id ); } else if (cmd == "aih") { // 指定した場所にアイテムを追加 ex. aih 3 item3 addNodeHere<FolderData, ItemData>( root, std::make_shared<Item<ItemData>>(ItemData{ value }), id ); } else if (cmd == "afu") { // 指定したフォルダの下にフォルダを追加 ex. afu 3 folder3 auto selected = getNodeOnlyOpen<FolderData>(root, id); addNodeUnder<FolderData, ItemData>( selected, std::make_shared<Folder<FolderData>>(FolderData{ value }) ); } else if (cmd == "aiu") { // 指定したフォルダの下にアイテムを追加 ex. aiu 3 item3 auto selected = getNodeOnlyOpen<FolderData>(root, id); addNodeUnder<FolderData, ItemData>( selected, std::make_shared<Item<ItemData>>(ItemData{ value }) ); } else if (cmd == "d") { // 削除 auto node = getNodeOnlyOpen<FolderData>(root, id); deleteNode<FolderData>(node); } else if (cmd == "s") { // ツリーの一部を取得 ex. s 3 auto slice = GetTreeSlice<FolderData>(root, id, id+4); printf("%d-%d\n", id, id + 4); printTreeSlice<FolderData, ItemData>(slice); printf("\n"); } printTree<FolderData, ItemData>(root, false); std::cout << "opennodes " << getOpenNodeCount<FolderData>(root) << std::endl; std::cout << "allnodes " << getAllNodeCount<FolderData>(root) << std::endl; } while (input_command(&cmd, &id, &value) == true); }
0 - folder1 1 - folder2 2 + folder21 3 - folder22 4 item221 5 item222 6 item223 7 + folder23 8 + folder24
0 - folder1 1 - folder2 2 + folder21 3 - folder22 4 item221 5 + NEW_FOLDER 6 item222 7 item223 8 + folder23 9 + folder24
0 - folder1 1 - folder2 2 + folder21 3 - folder22 4 item221 5 + NEW_FOLDER 6 item222 7 item223 8 New_Item 9 + folder23 10 + folder24
6 item222 7 item223 8 New_Item 9 + folder23 10 + folder24
ツリー構造を作っている。動作確認のためコンソールから操作できるようにしている。
#include <iostream> #include <iomanip> #include <sstream> #include <memory> #include <vector> #include <string> using namespace std::literals::string_literals; template<typename T> concept HasAsString = requires(T a) { { a.asString() } -> std::same_as<std::string>; };
class NodeBase { protected: public: std::shared_ptr<NodeBase> parent; virtual ~NodeBase() = default; virtual std::string asString() const = 0; // 必ず実装させる仮想関数 virtual std::shared_ptr<NodeBase> getParent() const{return parent;} }; template <HasAsString T> class Folder : public NodeBase, public std::enable_shared_from_this < Folder<T> > { std::vector<std::shared_ptr<NodeBase>> children; // 子ノードは NodeBase 型を保持 T value; bool isOpen; public: Folder(const T& data) : value(data), isOpen(false) {} // 子ノードを追加するメソッド void add(const std::shared_ptr<NodeBase>& node) { children.push_back(node); node->parent = std::dynamic_pointer_cast<NodeBase>(this->shared_from_this()); } const T& getName() const { return value; } std::string asString() const override { return value.asString(); } const std::vector<std::shared_ptr<NodeBase>>& getChildren() const { return children; } std::vector<std::shared_ptr<NodeBase>>& getChildren(){ return children; } bool getIsOpen() const { return isOpen; } void setIsOpen(bool open) { isOpen = open; } }; template <HasAsString T> class Item : public NodeBase { T value; public: Item(const T& data) : value(data) {} std::string asString() const override { return value.asString(); } };
///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// ノードの番号を付けて表示するための補助関数 template <HasAsString FT,HasAsString IT> void printTreeHelper(const std::shared_ptr<NodeBase>& node, int& lineNum, int indent = 0, bool open = false) { if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { // フォルダの場合 std::cout << std::setw(2) << lineNum++ << std::string(indent, ' ') << (folder->getIsOpen() ? "- " : "+ ") << folder->asString() << "\n"; if (open || folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { printTreeHelper<FT, IT>(child, lineNum, indent + 2, open); // ここでテンプレート引数を明示的に指定 } } } else if (auto item = std::dynamic_pointer_cast<Item<IT>>(node)) { // アイテムの場合 std::cout << std::setw(2) << lineNum++ << std::string(indent+1, ' ') << item->asString() << "\n"; } } // 指定したノード以下のツリーを表示する関数 template <HasAsString FT, HasAsString IT> void printTree(const std::shared_ptr<NodeBase>& node, bool open = false) { int lineNum = 1; printTreeHelper<FT,IT>(node, lineNum, 0, open); }
///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// 全てのノードの数をカウントする関数 template <HasAsString FT> int getAllNodeCount(const std::shared_ptr<NodeBase>& node) { int count = 1; // 自分自身をカウント // フォルダの場合は、子ノードも再帰的にカウント if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { for (const auto& child : folder->getChildren()) { count += getAllNodeCount<FT>(child); } } return count; }
// 開いているノードの数をカウントする関数 template <HasAsString FT> int getOpenNodeCount(const std::shared_ptr<NodeBase>& node) { int count = 1; // 自分自身をカウント // フォルダの場合、開いているかどうかを確認 if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (folder->getIsOpen()) { // 開いている場合、子ノードも再帰的にカウント for (const auto& child : folder->getChildren()) { count += getOpenNodeCount<FT>(child); } } } return count; }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
// 指定した index のノードを取得する関数 template <HasAsString FT> std::shared_ptr<NodeBase> getNodeHelper(const std::shared_ptr<NodeBase>& node, int& currentIndex, int targetIndex, bool open = false) { // 現在のノードをカウント if (++currentIndex == targetIndex) { return node; } // フォルダの場合は、開閉状態を確認し、再帰的に子ノードを探索 if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) { if (open || folder->getIsOpen()) { for (const auto& child : folder->getChildren()) { auto result = getNodeHelper<FT>(child, currentIndex, targetIndex, open); if (result) { return result; } } } } return nullptr; // 見つからなかった場合 } template <HasAsString FT> std::shared_ptr<NodeBase> getNode(const std::shared_ptr<NodeBase>& root, int targetIndex, bool open = false) { int currentIndex = 0; // ノードのカウント用 return getNodeHelper<FT>(root, currentIndex, targetIndex, open); }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
// ノードの親ノードを表示する関数 template <HasAsString FT, HasAsString IT> void printParentChain(const std::shared_ptr<NodeBase>& node) { auto current = node; while (current) { if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(current)) { std::cout << folder->asString() << " -> "; } else if (auto item = std::dynamic_pointer_cast<Item<IT>>(current)) { std::cout << item->asString() << " -> "; } current = current->getParent(); } std::cout << "nullptr\n"; // ルートに到達したら終了 }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
// ノードを削除する関数 template <HasAsString FT> void deleteNode(std::shared_ptr<NodeBase> node) { if (auto parent = node->getParent()) { if (auto parentFolder = std::dynamic_pointer_cast<Folder<FT>>(parent)) { auto& siblings = parentFolder->getChildren(); siblings.erase(std::remove(siblings.begin(), siblings.end(), node), siblings.end()); } } }
//////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
struct FolderData { std::string name; std::string asString() const { return name; } // コンストラクタ FolderData(const std::string& n) : name(n) {} }; struct ItemData { std::string name; std::string asString() const { return name; } // コンストラクタ ItemData(const std::string& n) : name(n) {} };
//////////////////////////////////////////////////// //////////////////////////////////////////////////// //////////////////////////////////////////////////// bool input_command(char* cmd, int* id, std::string* value) { std::string input; std::getline(std::cin, input); if (input.empty()) return false; std::istringstream iss(input); iss >> *cmd >> *id; if (iss >> std::ws && !iss.eof()) { std::getline(iss, *value); *value = value->substr(value->find_first_not_of(" \t")); // 先頭の空白を削除 } else { *value = ""; } return true; }
int main() { // ツリー構造を作成 auto root = std::make_shared<Folder<FolderData>>(FolderData{ "folder1" }); auto folder2 = std::make_shared<Folder<FolderData>>(FolderData{ "folder2" }); auto folder21 = std::make_shared<Folder<FolderData>>(FolderData{ "folder21" }); auto item211 = std::make_shared<Item<ItemData>>(ItemData{ "item211" }); auto folder22 = std::make_shared<Folder<FolderData>>(FolderData{ "folder22" }); auto item221 = std::make_shared<Item<ItemData>>(ItemData{ "item221"}); auto item222 = std::make_shared<Item<ItemData>>(ItemData{ "item222"}); auto item223 = std::make_shared<Item<ItemData>>(ItemData{ "item223"}); auto folder23 = std::make_shared<Folder<FolderData>>(FolderData{ "folder23" }); auto folder24 = std::make_shared<Folder<FolderData>>(FolderData{ "folder24" }); auto item241 = std::make_shared<Item<ItemData>>(ItemData{ "item241" }); auto item242 = std::make_shared<Item<ItemData>>(ItemData{ "item242" }); root->add(folder2); folder2->add(folder21); folder21->add(item211); folder2->add(folder22); folder22->add(item221); folder22->add(item222); folder22->add(item223); folder2->add(folder23); folder2->add(folder24); folder24->add(item241); folder24->add(item242); // フォルダの開閉状態を設定 root->setIsOpen(true); folder2->setIsOpen(true); folder22->setIsOpen(true); folder24->setIsOpen(false); char cmd=' '; int id; std::string value; do { switch (cmd) { case '*': // 開閉 { auto node = getNode<FolderData>(root, id, false); auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node); folder->setIsOpen(!folder->getIsOpen()); } break; case 'a': // 追加 { auto node = getNode<FolderData>(root, id, false); auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node); auto item = std::make_shared<Item<ItemData>>(ItemData{ value }); folder->add(item); } break; case 'd': // 削除 { auto node = getNode<FolderData>(root, id, false); deleteNode<FolderData>(node); } break; } printTree<FolderData, ItemData>(root, false); std::cout << "opennodes " << getOpenNodeCount<FolderData>(root) << std::endl; std::cout << "allnodes " << getAllNodeCount<FolderData>(root) << std::endl; } while (input_command(&cmd, &id, &value) == true); }
最初に以下のようにツリーが表示される。フォルダの左側に+,-がついている。+は現在開いている記号、-は現在閉じている記号。
1- folder1 2 - folder2 3 + folder21 4 - folder22 5 item221 6 item222 7 item223 8 + folder23 9 + folder24 opennodes 9 allnodes 12
*でフォルダを開閉する。例えば「* 4」を入力すると以下のように表示される。
1- folder1 2 - folder2 3 + folder21 4 + folder22 5 + folder23 6 + folder24 opennodes 6 allnodes 12
dでフォルダやアイテムを削除。 「d 5」で以下の結果になる
1- folder1 2 - folder2 3 + folder21 4 + folder22 5 + folder24
一行にパネルを配置することでリストを作成する。
ウィンドウサイズが変わるたびに、
① wxGridSizer::Clear(true);で現在のパネルをすべて削除し
② GridSizerを置いているwxBoxSizerから既存のwxGridSizerをwxBoxSizer::Detachで除外し、
③ 新しいwxGridSizerを作成し、この時に新しい行数を指定
④ 新しいwxGridSizerをGoxSizerにAddする
ということをやる。どうやらwxGridSizerの分割数を更新する方法がないらしく、仕方がないので毎回作り直している。
// 自作リストコントロール class MyList : public wxWindow { std::vector<int> _itemlist; // データリスト int row; int col = 1; int itemcount() { return _itemlist.size(); } int _itemHeight; wxScrollBar* _scroll; wxGridSizer* _gridSizer; wxBoxSizer* _boxSizer; public:
// 縦に配置するアイテム一覧用のGridSizerを作成 wxGridSizer* NewGridSizer() { int vgap = 1;// 垂直方向の間隔 int hgap = 1;// 水平方向の間隔 wxGridSizer* newGridSizer = new wxGridSizer(row, col, vgap, hgap); for (int i = 0; i < row * col; ++i) { //// 新しいパネルを作成 wxPanel* panel = new wxPanel(this, wxID_ANY); //// パネルの背景色を設定 panel->SetBackgroundColour(wxColour(200, 200, 100 * (i + 1) % 256)); //// パネルをgridSizer追加 newGridSizer->Add(panel, 1, wxEXPAND); // パネルにラベルを追加 wxStaticText* label = new wxStaticText(panel, wxID_ANY, ""); wxBoxSizer* labelboxSizer = new wxBoxSizer(wxHORIZONTAL); labelboxSizer->Add(label, 1, wxALIGN_CENTER); panel->SetSizer(labelboxSizer); } return newGridSizer; }
// 水平分割のSizerを作成 // 左側 ... GridSizer アイテム一覧 // 右側 ... スクロールバー wxBoxSizer* NewBoxSizer(wxGridSizer* gridsizer) { // 水平分割のSizer作成 auto boxSizer = new wxBoxSizer(wxHORIZONTAL); boxSizer->Add(gridsizer, 1, wxEXPAND); if (_scroll == nullptr) { //スクロールバー作成 _scroll = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxSize(20, -1), wxSB_VERTICAL); boxSizer->Add(_scroll, 0, wxEXPAND); _scroll->Bind(wxEVT_SCROLL_CHANGED, &MyList::OnScroll, this); _scroll->Bind(wxEVT_SCROLL_LINEUP, &MyList::OnScroll, this); _scroll->Bind(wxEVT_SCROLL_LINEDOWN, &MyList::OnScroll, this); _scroll->Bind(wxEVT_SCROLL_THUMBTRACK, &MyList::OnScroll, this); } return boxSizer; }
// 表示行数を変更する void ResetRow() { wxGridSizer* newGridSizer = NewGridSizer(); int scrollpos = 0; // リストを表示しているpanelをすべて削除 if (_gridSizer != nullptr) { _gridSizer->Clear(true); } if (_boxSizer == nullptr) { _boxSizer = NewBoxSizer(newGridSizer); } else { // 既存の_gridSizerをboxSizerから削除 _boxSizer->Detach(_gridSizer); _boxSizer->Detach(_scroll); // _gridSizerを削除 delete _gridSizer; _gridSizer = nullptr; _boxSizer->Add(newGridSizer, 1, wxEXPAND); _boxSizer->Add(_scroll, 0, wxEXPAND); } _gridSizer = newGridSizer; scrollpos = _scroll->GetThumbPosition(); SetSizer(_boxSizer); _scroll->SetScrollbar( scrollpos, // position スクロールバーの位置 row, // thumbSize つまみのサイズ。これは一度に画面上に表示する行数と等しいことが望ましい itemcount(), // range 総行数 2, // pageSize ページ単位でのスクロールを行ったときに移動する量 true // refresh trueにするとスクロールバーの位置を更新する ); // 表示を行う panel_update(); }
MyList( int ItemHeight, std::vector<int>& ITEM_LIST, wxWindow* parent, wxWindowID WINID, wxPoint point, wxSize size, long style, wxString name ) : wxWindow(parent, WINID,point,size,style,name) { _gridSizer = nullptr; _scroll = nullptr; _boxSizer = nullptr; _itemlist = ITEM_LIST; // アイテムの高さとウィンドウの高さから、一回に表示する件数を計算 // この値はgridSizerの行数になる // ウィンドウの高さは変化するので、アイテム高さは完全に固定しないで、 // ItemHeight以上、ItemHeight×2未満の範囲で調整する _itemHeight = ItemHeight; // ウィンドウサイズ変更時のイベントをBind Bind(wxEVT_SIZE, &MyList::OnSize, this); Centre();// ウィンドウを画面中央に表示 }
// ウィンドウサイズ変更時に呼び出されるイベントハンドラ void OnSize(wxSizeEvent& event) { // ウィンドウの高さから、一回に表示する件数を計算 int newrow = event.GetSize().GetHeight() / _itemHeight; if (newrow < 1) newrow = 1; // 表示行数が変わったら、表示をリセット if (newrow != row) { row = newrow; ResetRow(); } // 再配置 _boxSizer->Layout(); event.Skip(); }
// wxScrollBarをクリックしたときに呼び出されるイベントハンドラ void OnScroll(wxScrollEvent& event) { // スクロールバーの位置を取得 int pos = event.GetPosition(); // pos をデバッグ出力 wxLogDebug(wxString::Format("pos = %d", pos)); panel_update(); }
// panelの内容を更新する void panel_update() { // _scroll の位置を取得 int pos = _scroll->GetThumbPosition(); // offset = つまみの位置 = pos = 一番上に表示するアイテム番号 // 表示アイテム = offset + i int offset = pos * col; // リストの上からi番目にアイテムを表示 for (int i = 0; i < row * col; ++i) { // _gridSizerに登録されているpanelを取得 wxPanel* panel = (wxPanel*)_gridSizer->GetItem(i)->GetWindow(); // panel上にあるlabelを取得 wxStaticText* label = (wxStaticText*)panel->GetChildren()[0]; if ((offset + i) >= itemcount()) { // アイテム数を超えたら *** を表示 label->SetLabelText("***"); } else { if (offset + i == itemcount() - 1) { // 最後のアイテムなら (last item) を表示 label->SetLabelText(wxString::Format("Panel %d (last item)", _itemlist[offset + i])); } else { // それ以外はアイテム番号を表示 label->SetLabelText(wxString::Format("Panel %d", _itemlist[offset + i])); } } } }
};
class MyFrame : public wxFrame { public: void PostCreate() { // セットするデータ std::vector<int> ImageList; for (size_t i = 0; i < 20; i++) { ImageList.push_back(i); } // リスト作成 MyList* list = new MyList(20, ImageList,this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE, "MyList"); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: };
Windowsでコマンドプロンプトでutf8を表示するには、コードページを指定する必要がある。
VC++から起動した場合chcpできないので、コードから変える必要がある。
#include <cstdio> #include <windows.h> int main() { // 以下の二つはUTF-8を指定 //SetConsoleOutputCP(CP_UTF8); //SetConsoleOutputCP(65001); UINT now = GetConsoleOutputCP(); printf(u8"😊😎❤\n"); printf("こんにちは\n"); printf("\n"); SetConsoleOutputCP(65001); printf(u8"😊😎❤\n"); printf("こんにちは\n"); printf("\n"); SetConsoleOutputCP(now); printf(u8"😊😎❤\n"); printf("こんにちは\n"); printf("\n"); }
wxWidgetsでレイアウトを変更したい場合、
① 現在のSizerに登録されているウィジェットをバックアップ
② Sizerをクリアする(ただしそこに属しているウィジェットは削除しない)
③ 新しいSizerを作成
④ バックアップしたウィジェットを新しいSizerに設定
⑤ 新しいSizerをセット
wxWindowList windowList; wxSizerItemList& itemList = currentSizer->GetChildren(); // 現在のSizerからウィジェットを取り出してリストに保存 for (wxSizerItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) { wxSizerItem* item = *it; if (item->IsWindow()) { wxWindow* window = item->GetWindow(); windowList.push_back(window); } }
// Sizerをクリア currentSizer->Clear(false);
// 新しいSizerを作成 wxBoxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
// ウィジェットを新しいSizerに追加 for (wxWindowList::iterator it = windowList.begin(); it != windowList.end(); ++it) { wxWindow* window = *it; newSizer->Add(window, 1, wxALL | wxEXPAND, 0); }
// 親ウィジェットに新しいSizerを設定 panelB->SetSizer(newSizer);
// プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string>
// ウィンドウ作成 class MyFrame : public wxFrame { public: wxBoxSizer* sizerB; wxPanel* panelB; void PanelAB() { // パネルA,B作成 // 交換ボタン配置用のパネル wxPanel* panelA = new wxPanel(this, wxID_ANY, wxPoint(0, 0), wxSize(200, 100)); panelA->SetBackgroundColour(wxColour(255, 0, 0)); // このパネルにパネルB1,B2を配置 // これらのレイアウトを変更する panelB = new wxPanel(this, wxID_ANY, wxPoint(200, 0), wxSize(200, 100)); panelB->SetBackgroundColour(wxColour(0, 0, 255)); // パネルAにボタン作成 wxButton* buttonA = new wxButton(panelA, wxID_ANY, "ButtonA", wxPoint(10, 10), wxSize(100, 50)); // 画面を上下に分割し、上にpanelA、下にpanelBを配置 wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(panelA, 0, wxEXPAND); sizer->Add(panelB, 1, wxEXPAND); SetSizer(sizer); // ボタンAを押したときのイベント buttonA->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this); } void PanelB1B2() { // パネルBをsizerで左右に分割し、panelB1,panelB2を配置 sizerB = new wxBoxSizer(wxHORIZONTAL); wxPanel* panelB1 = new wxPanel(panelB, wxID_ANY, wxPoint(0, 0), wxSize(100, 100)); wxPanel* panelB2 = new wxPanel(panelB, wxID_ANY, wxPoint(0, 0), wxSize(100, 100)); panelB1->SetBackgroundColour(wxColour(255, 0, 255)); panelB2->SetBackgroundColour(wxColour(0, 255, 255)); sizerB->Add(panelB1, 1, wxEXPAND); sizerB->Add(panelB2, 1, wxEXPAND); panelB->SetSizer(sizerB); // パネルB1にボタン作成 wxButton* buttonB1 = new wxButton(panelB1, wxID_ANY, "ButtonB1", wxPoint(10, 10), wxSize(80, 50)); wxButton* buttonB2 = new wxButton(panelB2, wxID_ANY, "ButtonB2", wxPoint(10, 10), wxSize(80, 50)); } void PostCreate() { PanelAB(); PanelB1B2(); this->Layout(); // レイアウトの更新 }
// ボタンを押したときにSizerを交換 void OnButtonClicked(wxCommandEvent& event) { /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// wxBoxSizer* currentSizer = sizerB; if (currentSizer) { wxWindowList windowList;
wxSizerItemList& itemList = currentSizer->GetChildren(); // 現在のSizerからウィジェットを取り出してリストに保存 for (wxSizerItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) { wxSizerItem* item = *it; if (item->IsWindow()) { wxWindow* window = item->GetWindow(); windowList.push_back(window); } }
// Sizerをクリア currentSizer->Clear(false);
// 新しいSizerを作成 wxBoxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
// ウィジェットを新しいSizerに追加 for (wxWindowList::iterator it = windowList.begin(); it != windowList.end(); ++it) { wxWindow* window = *it; newSizer->Add(window, 1, wxALL | wxEXPAND, 0); }
// 親ウィジェットに新しいSizerを設定 panelB->SetSizer(newSizer);
this->Layout(); // レイアウトの更新 sizerB = newSizer; } }
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } };
///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);