-
Постов
410 -
Зарегистрирован
-
Посещение
-
Победитель дней
16
Тип контента
Профили
Форумы
Загрузки
Блоги
Сообщения, опубликованные Antonshka
-
-
10 часов назад, MasterGH сказал:
Скорее всего, только свой компонент делать. Я бы поигрался с Image компонентом. В нем менять картинки этого чекбокса по обраотчику.
Я игрался как-то раз с ним. Хотел сделать чекбоксы в виде слайдеров-переключателей. В каждом положении было по две картинки, одна видимая, другая нет. При клике они менялись видимостью.
Такой трейнер занимал 1 гигабайт RAM памяти. Пришлось делать стандартные чекбосы.
Не помню, использовал ли я тогда collectgarbage или нет. И помогает ли он вообще в этой ситуации.
-
3 часа назад, Pitronic сказал:
Вопрос короткий.
При создании формы на UDF1 с чекбоксами. Что можно добавить в lua чтоб чекбокс менял цвета, если галочка стоит цвет чекбокса красный, а если нет цвет чекбокса по дефолту чёрный, чтоб красивее выглядели функции активация диактивация.
Я в свое время хотел поменять цвет текста на обычной СЕ кнопке. Но это невозможно.
СпойлерThe WM_CTLCOLORBTN message is sent to the parent window of a button before drawing the button. The parent window can change the button's text and background colors. However, only owner-drawn buttons respond to the parent window processing this message.
Единственный выход, рисовать самому. В СЕ в celua.txt я видел такую возможность.
СпойлерCECustomButton class(Inheritance: CustomControl->WinControl->Control->Component->Object)
A more customizable button instead of the windows theme'd button, and lets you repaint it from scratch as wellcreateCECustomButton(owner)
properties
ShowPrefix: boolean - Process first single '&' per line as an underscore and draw '&&' as '&'
BorderColor: Color - The color of the button border
BorderSize: integer - The thickness of the border
ButtonColor: Color - The color of the button face
ButtonHighlightedColor: Color - The color of the buttonface when highlighted(hovered over with the mouse)
ButtonDownColor: Color - The color of the buttonface when the mouse is pressed down on it
DrawFocusRect: boolean - If true will draw a focus roundrect showing it has focus
DrawBorder: boolean - default=true. Will draw a border around the button
FocusedSize: integer - The with of the focus roundrect
FocusElipseColor: Color - The color of the focus roundrect
GrowFont: boolean read - When true the font will get resized till the caption fits the button
RoundingX: integer
RoundingY: integer
CustomDrawn: boolean - Do your own drawing in the OnPaint property of the button
FramesPerSecond: integer - If animation is used this will determine how often per second the OnPaint gets called
ButtonAnimationSpeed: integer - If not customdrawn, this determnines how long the animations for enter/leave take -
7 часов назад, imaginary сказал:
Всё очень просто, надо написать блок __try и __except и всё, можешь пытаться разыменовывать что угодно, а исключение интерпретировать как неправильный указатель с мусором вместо адреса.
У меня такой способ не работает.
Спойлер#include <windows.h> #include <iostream> int main() { DWORD_PTR* processAddress = reinterpret_cast<DWORD_PTR*>(GetModuleHandleW(NULL)); processAddress += 0x10000; __try { if (processAddress && *processAddress) { //Завершение работы прилжения std::wcout << L"Good"; } } __except (EXCEPTION_EXECUTE_HANDLER){ std::wcout << L"Bad"; } //----------------------------------------------------------------- INT v1 = 1; INT v2 = 0; __try { if (v1 / v2) { std::wcout << L"Good"; } } __except (EXCEPTION_EXECUTE_HANDLER) { // Выводится "Bad" std::wcout << L"Bad"; } return 0; }
Когда есть предусмотренный способ проверки через VirtualQuery, зачем использовать try/except?
Я кстати в библиотеке WGW нигде не использую обработку ошибок. Если ее использовать, то использовать по максимуму. А если так, то код превратится не пойми во что.
-
24 минуты назад, Xipho сказал:
С Qt
Понятно.
Кстати я сейчас смотрел исходники QT. Там тоже есть интересные моменты в коде, как и у меня
Спойлерvoid QGridLayoutPrivate::setupSpacings(QList<QLayoutStruct> &chain, QGridBox *grid[], int fixedSpacing, Qt::Orientation orientation) { Q_Q(QGridLayout); int numRows = rr; // or columns if orientation is horizontal int numColumns = cc; // or rows if orientation is horizontal if (orientation == Qt::Horizontal) { qSwap(numRows, numColumns); } QStyle *style = nullptr; if (fixedSpacing < 0) { if (QWidget *parentWidget = q->parentWidget()) style = parentWidget->style(); } for (int c = 0; c < numColumns; ++c) { QGridBox *previousBox = nullptr; int previousRow = -1; // previous *non-empty* row for (int r = 0; r < numRows; ++r) { if (chain.at(r).empty) continue; QGridBox *box = gridAt(grid, r, c, cc, orientation); if (previousRow != -1 && (!box || previousBox != box)) { int spacing = fixedSpacing; if (spacing < 0) { QSizePolicy::ControlTypes controlTypes1 = QSizePolicy::DefaultType; QSizePolicy::ControlTypes controlTypes2 = QSizePolicy::DefaultType; if (previousBox) controlTypes1 = previousBox->item()->controlTypes(); if (box) controlTypes2 = box->item()->controlTypes(); if ((orientation == Qt::Horizontal && hReversed) || (orientation == Qt::Vertical && vReversed)) qSwap(controlTypes1, controlTypes2); if (style) spacing = style->combinedLayoutSpacing(controlTypes1, controlTypes2, orientation, nullptr, q->parentWidget()); } else { if (orientation == Qt::Vertical) { QGridBox *sibling = vReversed ? previousBox : box; if (sibling) { if (sibling->item()->isEmpty()) { spacing = 0; } else { QWidget *wid = sibling->item()->widget(); if (wid) spacing = qMax(spacing, sibling->item()->geometry().top() - wid->geometry().top()); } } } } if (spacing > chain.at(previousRow).spacing) chain[previousRow].spacing = spacing; } previousBox = box; previousRow = r; } } }
Я заметил что в WGW Layout система уступает той что в QT. Теперь хочу доработать.
Подумал, посмотрю как там сделано в QT. Но при виде кода решил что легче придумать самому. Пока разберешься, что там и к чему.
Как было с Docking.
-
5 часов назад, Xipho сказал:
Есть, но поверхностно - делал UI управления настройками устройства для депарафинизации нефтяных скважин. Там не было необходимости дорабатывать существующие контролы.
У тебя только с QT мало опыта, или вообще с GUI?
-
1 час назад, Xipho сказал:
Есть, но поверхностно - делал UI управления настройками устройства для депарафинизации нефтяных скважин. Там не было необходимости дорабатывать существующие контролы.
Получается это коммерческая ситуация? Нужно было платить как-то за использование библиотеки QT?
1 час назад, Xipho сказал:Вопрос подхода. Qt в этом случае выбрали наследование, а ты выбираешь композицию. Сходу нельзя сказать, лучше что-то из этого, или хуже. Тут вопрос больше удобства кодинга. В случае с композицией у тебя ответственность смещается в сторону "композитора", то есть того, кто создает кнопку. В случае с наследованием всё, что связано с кнопками, инкапсулировано в классах кнопок.
По моему личному скромному мнению - всё, что связано с GUI контролами выглядит как вполне подчинающееся иерархии классов, и, следовательно, тут хорошо подходит наследование. Но наследование должно быть грамотным, продуманным.
Я сейчас посмотрел видео, где пишут калькулятор на QT. Мне понравилось. Что-то в этом наследование есть.
Нужно подумать, может получиться совместить то что сейчас в WGW с тем что в QT.
-
В 03.08.2022 в 09:24, Xipho сказал:
Твоя обязанность как разработчика библиотеки - максимально обезопасить пользователя от подобных ошибок. Пользователь НЕ должен иметь доступа на запись полей внутри класса.
А у тебя есть опыт работы с QT? В QT, как я понял, можно расширять классы контролов. Наследуясь от них.
В WGW же, создание интерфейса осуществляется как бы процедурно, не через наследование.
СпойлерWND_MAIN = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 20, 20, 900, 900); WND_MAIN->onClose = OnCloseFunction; // Это обычная функция, не метод какого-либо класса WND_MAIN->color = RGB(188, 190, 220); WND_MAIN->onPaint = OnPaintFunctionName; // Это обычная функция, не метод какого-либо класса WND_MAIN->onEraseBkg = OnErase; WND_MAIN->onClick = functionName1; // Это обычная функция, не метод какого-либо класса BTN1 = new WGW::ButtonStandart(TRUE, 0, 0, L"Btn1", 10, 100, 100, 100, WND_MAIN); BTN1->toolTip.enabled = TRUE; BTN1->toolTip.titleText.append(L"ToolTipTitle ", RGB(10, 10, 200)); BTN1->toolTip.titleText.append(L"ToolTipTitleSecond1 \n", RGB(144, 140, 10)); BTN1->onClick = functionName2; // Это обычная функция, не метод какого-либо класса
В QT события как я понял обрабатываются в методах класса, а не в простых функция как в WGW.
И еще я кажется видел что в QT можно создать контрол через оператор "new". Не через наследование. То есть как я понял просто используя то что QT дает стандартно .
- в QT есть расширение классов
- в QT события обрабатываются в методах класса, а не в простых функцияч, как в WGW.
Интересно, WGW от этого сильно проигрывает QT?
Ну хорошо, наследовал в QT класс QPushButton, расширин функционал, добавил что новый класс при нажатии на кнопку пиликает. И теперь все кнопки созданные этим классом будут пиликать.
А в WGW, вместо этого, можно создать файл PilicedButtons.h, там определить функцию на событие кнопка нажата, а в ней уже делать пиликанье. Затем все простые кнопки направить при нажатии на эту функцию. Получаем что и в QT.
-
8 часов назад, MasterGH сказал:
Через WinAPI проверять доступные регионы памяти
А ведь логично. Должна же API предоставлять какой-то механизм исследования участка памяти.
Рихтера читал недавно, но видимо не запечатлелось. Из MSDN собрал такое
Спойлер#include <windows.h> #include <iostream> BOOL isAddressAccesable(LPVOID address) { BOOL isAddressAccesable = FALSE; MEMORY_BASIC_INFORMATION memoryInfo{ 0 }; SIZE_T result = VirtualQueryEx(GetCurrentProcess(), address, &memoryInfo, sizeof(MEMORY_BASIC_INFORMATION)); if (result && memoryInfo.State == MEM_COMMIT) { isAddressAccesable = TRUE; } return isAddressAccesable; } int main() { DWORD_PTR* processAddress = reinterpret_cast<DWORD_PTR*>(GetModuleHandleW(NULL)); processAddress += 0x10000; if (isAddressAccesable(processAddress)) { processAddress = reinterpret_cast<DWORD_PTR*>(*processAddress); } return 0; }
9 часов назад, youneuoy сказал:ага, мусор будет или ошибка чтения. Как в cheat engine когда структуру смотришь - оно указателями всё подряд считает и даже развернуть их можно.
Да, есть такой момент.
-
11 часов назад, Antonshka сказал:
Интересно как на С++ проверяется валидность значения адреса.
Кажется вот рабочий способ. Не знаю насколько это правильно, но работает.
Спойлер#include <windows.h> #include <iostream> int main() { DWORD_PTR* processAdress = reinterpret_cast<DWORD_PTR*>(GetModuleHandleW(NULL)); processAdress += 0x10000; BYTE byteData = 0; BOOL accessResult = ReadProcessMemory(GetCurrentProcess(), processAdress, &byteData, sizeof(BYTE), NULL); if (accessResult) { int d = *processAdress; } return 0; }
Каждый раз перед проверкой доступа вызывать ReadProcessMemory.
СпойлерReturn value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError.
-
1 час назад, youneuoy сказал:
😱
0xc0000005 или мусор какой-нить, очевидно. Если я правильно тебя понял.Не сам базовый адрес модуля, а его адрес + смещение.
Спойлер -
5 часов назад, Oonatesara сказал:
почему то мне постоянно возвращает 0, но я хотя бы понял, в какую сторону мне идти. спасибо
Покажи свой код. Может что-то связано с защитой памяти. VirtualProtectEx.
-
2 часа назад, Xipho сказал:
Твоя обязанность как разработчика библиотеки - максимально обезопасить пользователя от подобных ошибок. Пользователь НЕ должен иметь доступа на запись полей внутри класса. Да, пользователь может сделать переменную и ее переназначить - и вот это уже будет на совести пользователя. А вот открытые к изменению публичные поля, это еще одна точка отказа, за которую ответственность несешь ты, как разработчик библиотеки. В идеальном мире ни одно поле не должно быть публичным, а сеттер поля должен делать валидацию поступающих на вход значений. У того же HDC в OnPaintEvent можно сеттер вообще сделать приватным, как и само поле, и тогда снаружи его изменить будет нельзя. Не устаю говорить всегда о том, что пользователю библиотеки ни в коем случае нельзя доверять. Потому нужно стараться максимально закрыть от пользователя детали реализации, и открыть ему только те части, в которых не сможет накосячить. Ну или накосячит по минимуму. В данном конкретном случае я сходу не подскажу, как это можно сделать, надо посидеть, подумать.
Я понял тебя. Ты говоришь вообще в целом. С этим я согласен, это я держу во внимании.
Сейчас в библиотеке все так и есть. Все закрыто, ничего лишнего пользователь не то что изменить, даже и создать не может. Например никакой объект класса свойства контрола.
Все что ему открыто, все безопасно.
А я говорил про конкретную ситуацию с параметром функции события. Этот объект-параметр, специально имеет открытые поля, они безопасны, их можно изменять. Как я писал выше, по сути это все равно что вызвать метод такого объекта-параметра, просто библиотека делает это за пользователя.
LRESULT OnPaintFunctionName(WGW::OnPaintEvent* onPaintEvent) { //Как в QT, получаем значение методом. HDC compDC = onPaintEvent->getMyCompatibleDC(); FillRect(compDC, ...) //Как в WGW FillRect(onPaintEvent->compDС, ...) }
-
Интересно как на С++ проверяется валидность значения адреса.
HMODULE gameExeModule = GetModuleHandleW(L"ThisGame.exe"); gameExeModule += 0x10E58C; gameExeModule = *gameExeModule; //А если по *gameExeModule у нас NILL? Помню даже на форуме видел темы про это, только на СЕ. //Интересно такое допустимо на C++? if (*(gameExeModule)) //Этот вопрос конечно же разрешается несколькими тестами. Но пока не до них.
-
14 минут назад, Xipho сказал:
Подход не очень. Тот, кто будет пользовать твою библиотеку, может задуматься, ошибиться и записать в это поле какое-то значение. А потом будет тратить часы на отладку не понимая, почему получает графические артефакты (или что-нибудь еще менее приятное)
Графические артефакты? Я немного не понял тебя.
Если к примеру взять событие WM_PAINT.
Вот обычная функция для события рисования
СпойлерLRESULT OnPaintFunctionName(WGW::OnPaintEvent* onPaintEvent) { RECT rect{ 0 }; GetClientRect(onPaintEvent->hWnd, &rect); FillRect(onPaintEvent->compDC, &rect, reinterpret_cast<HBRUSH>(GetClassLongPtrW(onPaintEvent->hWnd, GCLP_HBRBACKGROUND))); BitBlt(onPaintEvent->origDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, onPaintEvent->compDC, rect.left, rect.top, SRCCOPY); return TRUE; } int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { WGW::GUI::initializeWGWLibrary(hInstance); //------------------------------------------- WND_MAIN = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 20, 20, 1000, 900); WND_MAIN->onPaint = OnPaintFunctionName;
Это класс передаваемого объекта в параметре (указателя на него, сам он находится в классе окна, постоянно). Как видно из класса, только поля объявлены как public. Пользователь не может создать или обратится к этому объекту вне функции события. Создать его может только класс окна/контрола.
Спойлерstruct OnPaintEvent : public OnEvent { friend class Control; friend class Window; private: OnPaintEvent() {} OnPaintEvent(CONST OnPaintEvent&) = delete; ~OnPaintEvent() {} public: HDC compDC{ NULL }; HDC origDC{ NULL }; INT menuHeight{ 0 }; INT menuTopPosition{ 0 }; INT titleBarHeight{ 0 }; }; using OnPaint = LRESULT(*)(OnPaintEvent* onPaintEvent);
А это то как заполняются поля при каждом сообщении WM_PAINT
СпойлерLRESULT Window::wmPaint(HWND& hWnd, UINT& msg, WPARAM& wParam, LPARAM& lParam) { if (onPaint != nullptr || titleBar.enabled.isTitleBarEnabled && !::GetParent(hWnd) || menu.enabled.isMenuEnabled) { if (GetUpdateRect(hWnd, NULL, FALSE)) { ValidateRect(hWnd, NULL); if (onPaint != nullptr) { onPaintEvent.compDC = compatibleDC.compDC; onPaintEvent.control = this; onPaintEvent.hWnd = hWnd; onPaintEvent.msg = msg; onPaintEvent.wParam = wParam; onPaintEvent.lParam = lParam; onPaintEvent.origDC = compatibleDC.origDC; if (titleBar.enabled.isTitleBarEnabled && !::GetParent(hWnd)) { onPaintEvent.titleBarHeight = titleBar.titleBarRect.bottom; } else { onPaintEvent.titleBarHeight = 0; } if (menu.enabled.isMenuEnabled) { onPaintEvent.menuHeight = menu.menuHeight; onPaintEvent.menuTopPosition = menu.menuTopPosition; } else { onPaintEvent.menuHeight = 0; onPaintEvent.menuTopPosition = 0; } onPaint(&onPaintEvent); } if (titleBar.enabled.isTitleBarEnabled && !::GetParent(hWnd)) { titleBar.onOwnerWmPaint(); } if (menu.enabled.isMenuEnabled) { menu.onOwnerWmPaint(); } return 0; } } return DefWindowProcW(hWnd, msg, wParam, lParam); }
Поля объекта получают копии значений. Оригиналы не затрагиваются.
Если пользователь по ошибке запишет в поле compDC что-то свое, то да, код далее этого момента будет глючить. Но так подумать, это можно отнести вообще ко всем переменным.
Например
СпойлерLRESULT OnPaintFunctionName(WGW::OnPaintEvent* onPaintEvent) { HDC hDC = onPaintEvent->getMyCompatibleDC(); //Как в QT, получаем значение методом. //Продолжение кода. hDC = 0; //Пользователь ошибся, изменил значение определенной самим же им переменной. //Продолжение кода. }
В WGW этот вызов
HDC hDC = onPaintEvent->getMyCompatibleDC();
выполняется за пользователя, автоматически и заранее.
Все ошибки какие пользователь может совершить с переменными в данном случае в WGW, равны тем что в QT.
Поэтому я не понял тебя.
-
2 часа назад, youneuoy сказал:
Это не мой код🙂Я его взял у какого-то пользователя с форума когда программированию учился только и по этому шаблону наделал патчей в память.
Понятно 🙂.
1 час назад, Xipho сказал:Это на твоё усмотрение )
Мы же вместе собрались изобретать велосипед. Пусть простой, но зато свой.
Вчера я даже не смог скачать QT Creator. Хотел посмотреть как там устроены сигналы, слоты.
VPN не стал организовывать, из роликов и документации я примерно понял что там и к чему. Мне кажется WGW в этом плане попроще будет.
Я заметил что в QT в параметре метода события (например кнопка нажата), передается определенный для этого типа события объект. И чтобы получить какие-либо его характеристики, нужно вызвать его методы.
//QPushButton Class virtual void keyPressEvent(QKeyEvent *e) override
И некоторые методы QKeyEvent
Спойлерint count() const bool isAutoRepeat() const int key() const QKeyCombination keyCombination() const bool matches(QKeySequence::StandardKey key) const Qt::KeyboardModifiers modifiers() const quint32 nativeModifiers() const quint32 nativeScanCode() const quint32 nativeVirtualKey() const QString text() const
А в WGW немного по другому. Вместо QKeyEvent объекта передается тоже определенный для этого события объект , но он не имеет методов. Он имеет public поля с уже подсчитанными/полученными значениями (из wParam, lparam). Такой подход в сравнении с QT удобнее, как мне кажется. Но есть один минус, - даже если пользователю не нужны эти данные в методе события, они все равно подсчитываются/получаются. То есть холостая работа. Хотя опять же, она такая незначительная, что я подумал так и оставить, а не писать методы для их получения.
Либо разработчики гнались за максимальной производительностью, что даже такие мелочи оформили в отдельные методы. Либо это у них такой принцип. Не знаю.
-
13 минут назад, youneuoy сказал:
кидай, посмотрю тоже https://github.com/youneuoy
Добавил. Я заметил у тебя в коде употребление префикса m_ (m_cheatSize).
Про свой я уж молчу.
-
30 минут назад, Xipho сказал:
Оптимально будет выложить код на гитхаб в приватный репозиторий, и добавить желающих в контрибьюторы
Понял, так и сделаю. Не буду ждать пока поправлю все.
Несколько дней назад я создал Github аккаунт. Раза с 10 только получилось разобраться как правильно обновлять проект. Пока не знаю как вести обсуждения, впрочем, пока не до них.
Я отправил приглашение для Xipho Github.
@Xipho можно мне написать что этот проект совместная работа с тобой? А также всеми, кто помогал в разрешении вопросов, конечно же.
Если у кого-то также есть желание, присоединяйтесь. Нужно ваше имя на Github, для отправки приглашения.
-
33 минуты назад, youneuoy сказал:
метод update слишком размыт, а GUI, от которого наследуются виджеты - нет?😃
По идее да, тоже размыто 😜. Но в случае с update я думаю что будут ложные холостые вызовы. Например если есть 100 кнопок, из которых активны только 99 . Система сделает 99 вызовов update просто так. 99 проверок if легче же для системы чем 99 вызовов?
38 минут назад, youneuoy сказал:Вариантов масса, просто сделай как-нибудь для начала.
Уже сделано, мы просто теперь обсуждаем правильность сделанного. Вообще просто обсуждаем как сделано.
-
59 минут назад, youneuoy сказал:
это не так. У тебя ведь условие || есть. Для второй части выражения isAnchorEnabled теперь не существует.
Теперь кажется понятно. Значить действие || распространяется на все вообще части, слева от него и справа.
Спойлер#include <windows.h> #include <iostream> VOID calculate() { std::wcout << L"calculated!"; } int main() { INT isAnchorEnabled = 0; INT isInLayout = 1; INT layoutOwner = 1; INT layoutOwnerLayoutEnabled = 0; if (isAnchorEnabled && TRUE && !isInLayout || layoutOwner && !layoutOwnerLayoutEnabled) { calculate(); } //Действие || распространяется на всю левую часть - isAnchorEnabled && TRUE && !isInLayout //и на всю правую часть - layoutOwner && !layoutOwnerLayoutEnabled //Я же думал что только на - !isInLayout || layoutOwner return 0; }
Со скобками получается можно избавиться от повторного сравнения isAnchorEnabled
if ( isAnchorEnabled && (!isInLayout || layoutOwner && !layoutOwner->layout.enabled) ) { calculate(); }
-
2 часа назад, Xipho сказал:
Все логические операторы обладают одинаковым приоритетом и выполняются слева направо. Если тебе нужно сложное условие, ты можешь более простые условия объединять в круглые скобки, тогда приоритетно будут вычисляться выражения внутри них.
В приведенном тобой примере получится
if ((isAnchorEnabled && !isInLayout) || (isAnchorEnabled && layoutOwner && !layoutOwner->layout.enabled)) calculate();
isAnchorEnabled повторяется два раза. Я поэтому и начал задумываться над тем как все это работает.
Скобки в интернете советовали тоже, но что если без них? Как тогда поведет себя выражение. Хочу просто понять, а использовать можно и скобки.
Можно ли так упростить
if (isAnchorEnabled && !isInLayout || layoutOwner && !layoutOwner->layout.enabled) calculate();
isAnchorEnabled если будет true, тогда по идее далее его нет смысла проверять.
Если && и || работают только с тем что конкретно слева и справа от них (а не вообще все, до конца), тогда по идее такой вариант сработает.
У меня еще была мысль посмотреть в Disassembler, посмотреть как там на самом деле работает && и || в таких длинных выражениях.
-
2 часа назад, Xipho сказал:
Вот тут ты, на мой взгляд, слегка торопишься. Книги, которые ты прочитал, содержат примеры на Java, и используют общепринятую код конвенцию языка Java. Код конвенция C++ вполне себе может отличаться, этот момент лучше прокопать, учитывая, что библиотеку ты собираешься делать опенсорсной.
Я столько много копал на этот момент. Плюс сам для себя пытался понять, как мне удобнее.
В интернете большинство придерживается methodName. На счет скобок не нашел.
Автор Imgui использует MethodName, и скобки все слева.
Я перевел в одном классе все методы на methodName, перевел все поля-объекты также на camelCase (не поля обычных int, float... они и так были camelCase). И я нашел что для глаз так легче.
BTN2->ToolTip.TitleText.Append(L"ToolTipTitleSecond1 \n", RGB(144, 140, 10)); //было BTN2->toolTip.titleText.append(L"ToolTipTitleSecond1 \n", RGB(144, 140, 10)); //стало
Не знаю как это работает, но camelCase читается удобнее.
Со скобками как в Java менее удобно, (наверно потому что не привык). Но зато с ними методы отделяются друг от друга более очевидно.
Спойлер// было VOID ToolTip::onOwnerWmLButtonDown(LPARAM& lParam) { if (threadLocal) { terminateThreadLocalForThisToolTipObject(); } } VOID ToolTip::onOwnerWmMButtonDown(LPARAM& lParam) { if (threadLocal) { terminateThreadLocalForThisToolTipObject(); } } VOID ToolTip::onOwnerWmRButtonDown(LPARAM& lParam) { if (threadLocal) { terminateThreadLocalForThisToolTipObject(); } } // стало VOID ToolTip::onOwnerWmLButtonDown(LPARAM& lParam) { if (threadLocal) { terminateThreadLocalForThisToolTipObject(); } } VOID ToolTip::onOwnerWmMButtonDown(LPARAM& lParam) { if (threadLocal) { terminateThreadLocalForThisToolTipObject(); } } VOID ToolTip::onOwnerWmRButtonDown(LPARAM& lParam) { if (threadLocal) { terminateThreadLocalForThisToolTipObject(); } }
2 часа назад, Xipho сказал:Например, если у тебя есть класс базового окна, и класс окна с рамкой - выглядит, как будто у них будет очень много общих свойст, и если в данном случае ты будешь использовать композицию, то какой тогда смысл в разных классах окна?
Класс окна всего один. Он создает простое окно. Он поддерживает все что только можно сделать с окном. Создал такой экземпляр, и навесил на него все что нужно, все что он позволяет на себя навесить. Стили, полосы прокрутки, альфа канал, переопределил WM_PAINT и получил в своей функции в параметре уже готовый CompatibleDC, только и рисуй на нем.
Я примерно прикидывал, и не нашел ничего такого специфичного, чтобы не позволил сделать класс окна с окном. Потому не стал его раздваивать.
С предопределенными WinAPI контролами (Button, EditBox), тоже самое что и с окном, что хочешь, то и навесил на них.
Есть например и специфичные окна, как например самодельный PopupMenu. Можно было наследовать его о таким обрызом
СпойлерНо PopupMenu не нуждается ни в Menu ни в Полосах прокрутки. Поэтому PopupMenu выведен в свой собственный класс, и наследуется не от Control класса, как например это делают Window, Button, EditBox, а наследуется сразу от GUI.
PopupMenu не нуждается в тех объектах-свойствах в которых нужнаются эти.
Поэтому всему архитектура сейчас такая. Ее лучше как ты говоришь увидеть, но и так схематично тоже помогает понять что к чему.
2 часа назад, Xipho сказал:но, еще раз повторюсь, всё нужно использовать в разумных пределах.
Я запомнил этот момент. Лишний раз не нужно оверинженерить, как говорил ты и книга.
-
Привет всем, недавно видел на форуме пост @imaginary
[bad_alloc] переполнение памяти C++
В 27.07.2022 в 20:53, imaginary сказал:Ты знаешь что некоторые думают что если проверять всё в одном месте то будет тратить много ресурсов, потому что если вложенно, то там первое не выполнится - другие не будут тратить время. Я тоже так думала когда не разбиралась как всё работает раньше. Так что не удивительно что многие делают много условий
, и думал про себя, а я разбираюсь? Оказалось что нет.
Короткие выражения я понимаю/ Как например
if (a == 1 || a == 2)
a == 2 проверяется только если a == 1 false.
if (a == 1 && a == 2)
a == 2 проверяется только если a == 1 true.
Но как быть когда операторов много? Например
if (isAnchorEnabled && !isInLayout || isAnchorEnabled && layoutOwner && !layoutOwner->layout.enabled) calculate();
Нужно чтобы calculate() вызывался только если у контрола якорь активен и контрол НЕ в системе Layout.
Либо если у контрола якорь активен и контрол В системе Layout, но система Layout не активна.
&& layoutOwner - эта проверка указателя на null перед тем как к нему обратиться.
Не могу найти в интернете решение для такого выражения.
Оператор || в данном примере оперирует только !isInLayout и isAnchorEnabled? Или он оперирует вообще всем что слева от него и всем что справа, и рссматривает все что слева как одну единицу и все что справа как одну единицу.
-
1 час назад, Xipho сказал:
В целом выглядит неплохо, но смущает использование стратегии.
Стратегия используется только в качестве свойств контролов. Без них я даже не представляю как организовать свойства. Делать классы свойств и наследовать их контролами? Или писать методы для общих свойств в базовом классе, а некоторые, специфичные, в конкретном классе? Стратегия выглядит более подходящей. В книга также было написано, использовать композицию вместо наследования.
1 час назад, Xipho сказал:Возможно, надо повнимательнее в код посмотреть, в части контролов для окон как будто наследование было бы логичнее...
Если использовать стратегию в качестве свойств, то в случае если в классе Window будет объект-стратегия Menu, и если контролы будут наследовать класс Window, то тогда они наследуют и объект Menu. Да и другие некоторые, не свойственные для них объекты.
Даже если использовать не стратегию для свойств, а наследование их классов, то получится тоже самое. Класс Window наследует класс Menu, а класс Button, например, наследует и Window и Menu.
Это если я правильно понял тебя про наследование. Я тоже думаю лучше увидеть код.
Я его привожу в порядок. Вчера половина дня ушла только на переход с MethodName на methodName. На выставление скобок для всех одиночных if. На перенос первой скобки вправо в конец.
- 1
-
1 час назад, Xipho сказал:
Это немного странно, потому как четко прослеживается иерархия классов окна и контрола (опять же, по примеру винапи).
Когда я говорил "в основном", я имел ввиду что всю работу выполняют объекты "стратегии". Они в библиотеке представляю определенные свойства, окна или WinAPI контрола. А сама иерархия вот такая
СпойлерСами контролы практически ничего не умеют делать. Все на что они способны, это делегировать (теперь новое слово в моем словарном запасе) задачу объектам стратегии из своей оконной процедуры, когда это нужно.
А так, сам пользователь обращается к объекту "стратегии" окна/контрола, и через него влияет на окно/контрол.
СпойлерWND_MAIN = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 20, 20, 1000, 900); WND_MAIN->onClose = OnCloseFunction; WND_MAIN->color = RGB(188, 190, 220); WND_MAIN->onPaint = OnPaintFunctionName; WND_MAIN->onEraseBkg = OnErase; WND_MAIN->onClick = FirstRandomFunctionName; WND_MAIN->icon16 = _IDI_PACK; WND_MAIN->titleBar.enabled = TRUE; BTN2 = new WGW::ButtonStandart(TRUE, 0, 0, L"Btn2", 220, 400, 200, 200, WND_MAIN); BTN2->toolTip.enabled = TRUE; BTN2->toolTip.titleText.append(L"ToolTipTitle ", RGB(10, 10, 200));
1 час назад, Xipho сказал:А у тебя есть понимание, почему стоит программировать именно так?
Да, в этом плане все хорошо, все понятно из книги. Как я понимаю, основная цель паттернов это перспективы на будущее. Патерны это приемы избавляющего разработчика от переделывания проекта при его расширении.
В этом плане "стратегия" мне очень нравится. Ни от кого не зависит, делает что хочет. Окну/контролам не важно что она там у себя делает. Важно только иметь в своем классе ее объект и некоторые разрешения через "friend" по необходимости.
Очистка очереди сообщений WinAPI
in Общение
Опубликовано
Привет всем, подскажите, как удалить из очереди сообщений потока определенное сообщение/сообщения? Кто-нибудь игрался с этим? Что-то как-то темно все с этим в интернете.
Ситуация примерно такая:
- Поток A создал кнопку.
- При клике на эту кнопку, поток A создал поток Б.
- Поток Б через 30 секунд запостит (используя PostMessageW) в очередь сообщений потока A специальное мною определенное сообщение. Запостит и завершится.
- Однако, я передумал, и захотел отменить поток Б. Поток А, спустя 29.99 секунд после создания потока Б, идет и выполняет функцию отмены.
Но, поток А не успел, и поток Б уже запостил сообщение. После возвращения потока А из функции отмены потока Б, он найдет сообщение от потока Б, и начнет его выполнять.
Я же хочу, чтобы поток А, в функции отмены, дождался принудительного завершения потока Б (SetEvant, WaitForSigleObject). Затем, в этой же функции отмены, поток А проверяет свою очередь сообщений, и удаляет из нее возможное в ней сообщение от потока Б.
Затем, поток А, вернется из функции отмены в MessageLoop функцию, и когда вызовет GetMessage, найдет что сообщения от потока Б уже нет. Значит оно не исполниться.