Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.217.208.72] |
|
Сообщ.
#1
,
|
|
|
Есть программа в которой рисуются прямоугольники. Расстояние до линий вычисляются по координатам. Если расстояние до линии прямоугольника меньше 4 пикселей при первом клике происходит ВЫДЕЛЕНИЕ прямоугольника, при клике по выделенному прямоугольнику происходит открытие окна СВОЙСТВА. Если второй клик вне области выделенного прямоугольника выделение ОТМЕНЯЕТСЯ. Все работает.
В программах, как правило, принято аналогичные команды размещать в главном меню. У меня в меню есть пункт Properties (СВОЙСТВА) но при выделенном прямоугольнике клик по меню вместо открытия окна свойств отменяет выделение. Вопрос какой функцией определить раскрыто ли главное меню в текущий момент? |
Сообщ.
#2
,
|
|
|
Попробуй отслеживать инициализацию и деинициализацию меню. В оконную процедуру передаются сообщения вроде WM_INITMENU, WM_INITMENUPOPUP и WM_UNINITMENUPOPUP. Соответственно выключай и включай обработку кликов. Я тему не рыл, так что извини, если не сработает.
|
Сообщ.
#3
,
|
|
|
Сделать глобальную переменную-флаг menuvisible, при обработке сообщений меню менять ее значение.
А в функции, где я делаю клик учитывать эту переменную... такая мысль меня посещала. Наверное, как один из вариантов. Хотелось бы узнать, может кто-нибудь подскажет функцию, которая решит проблему без дополнительной глобальной переменной. Потому как у меня глобальных переменных сотни. А все книжки пишут что глобальные переменные - зло. Смотрю в сторону int MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen); похоже что она дает -1 если в заданной точке нет пункта меню. |
Сообщ.
#4
,
|
|
|
Цитата Mr.Brooks @ Вопрос какой функцией определить раскрыто ли главное меню в текущий момент? Mr.Brooks, полагаю, что это вообще не нужно делать. ----- Если в твоей программе обработчики разных событий хотят управлять одними и теми же объектами, значит они (обработчики) должны иметь возможность определить состояние этих объектов для правильного дальнейшего изменения этого состояния. Только и всего. ----- Т.е. если из 2-х разных обработчиков показывается/скрывается окно "Свойства", каждый обработчик должен узнать - показано ли это окно или скрыто в данный момент. Тогда он может произвести правильные действия - скрыть/показать это окно. Итп итд. Можно посоветовать не делать слишком сложных зависимостей между разными обработчиками, если это возможно. |
Сообщ.
#5
,
|
|
|
Цитата Mr.Brooks @ В программах, как правило, принято аналогичные команды размещать в главном меню. У меня в меню есть пункт Properties (СВОЙСТВА) но при выделенном прямоугольнике клик по меню вместо открытия окна свойств отменяет выделение. Скорее всего у тебя при выделении прямоугольника захватывается фокус мыши, SetCapture. Думаю, в данном случае это вообще не нужно. Иначе все сообщения мыши будут уходить в главное окно, в котором прямоугольники, до тех пор пока не будет вызвана ReleaseCapture. |
Сообщ.
#6
,
|
|
|
Для ясности приведу рисунки и вырезки из кода.
(сразу прошу прощения возможно некоторые переменные не понятны) Прикреплённая картинка
Прикреплённая картинка
// вырезки из кода case WM_COMMAND: switch (LOWORD(wparam)) { case IDM_PROPERTIES: DataElementProperties(hwnd); break; } case WM_LBUTTONDOWN: FormLButtonDown(hwnd); return 0; case WM_MOUSEMOVE: FormMouseMove(hwnd, e::hstatusbar); return 0; //... void FormLButtonDown(HWND hwnd) { DataElementSelectCancel(hwnd); DataElementSelectStop(hwnd); } void FormMouseMove(HWND hwnd, HWND hstatusbar) { DataElementSelect(hwnd); } //... void DataElementProperties(HWND hwnd) { int k; k = e::projectstep; if ((e::elementselectubound == 1) && (e::elementselectstop == e::elementselect[0])) { switch (e::element[k][e::elementselectstop].type) { case 0: AssemblyCreateDlg(hwnd, e::hinst); break; } } } void DataElementSelect(HWND hwnd) { int i, j, k; double d; double x[3]; double y[3]; POINT ptcursor; RECT rect; k = e::projectstep; GetCursorPos(&ptcursor); GetWindowRect(hwnd, &rect); x[0] = ptcursor.x - rect.left - 5; y[0] = ptcursor.y - rect.top - 75; e::elementselectubound = 0; for (i=0; i<e::elementubound; i++) { for (j=0; j<e::element[k][i].schpointpairubound; j++) { x[1] = e::element[k][i].schpoint[0][0+2*j]; y[1] = e::element[k][i].schpoint[1][0+2*j]; x[2] = e::element[k][i].schpoint[0][1+2*j]; y[2] = e::element[k][i].schpoint[1][1+2*j]; d = DataElementSelectDist(x, y); if ((d >= 0) && (d <= 3)) { if (e::elementselectubound < 100) { e::elementselect[e::elementselectubound] = i; e::elementselectubound++; break; } } } } RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT); } void DataElementSelectCancel(HWND hwnd) { if ((e::elementselectubound != 1) || (e::elementselectstop != e::elementselect[0])) { e::elementselectstop = -1; } RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT); } double DataElementSelectDist(double x[3], double y[3]) { double a, b, c, d, len, l1, l2; d = -1; a = y[1] - y[2]; b = x[2] - x[1]; c = y[2] * x[1] - y[1] * x[2]; len = abs(sqrt(a * a + b * b)); l1 = abs(sqrt((y[1] - y[0]) * (y[1] - y[0]) + (x[1] - x[0]) * (x[1] - x[0]))); l2 = abs(sqrt((y[2] - y[0]) * (y[2] - y[0]) + (x[2] - x[0]) * (x[2] - x[0]))); if (len != 0) { d = abs((a * x[0] + b * y[0] + c) / len); if ((l1 * l1) > (len * len + l2 * l2)) { d = l2; } if ((l2 * l2) > (len * len + l1 * l1)) { d = l1; } } return d; } void DataElementSelectStop(HWND hwnd) { switch (e::elementselectubound) { case 0: e::elementselectstop = -1; break; case 1: if (e::elementselectstop == e::elementselect[0]) { SendMessage(hwnd, WM_COMMAND, (WPARAM) IDM_PROPERTIES, NULL); } else { e::elementselectstop = e::elementselect[0]; } break; default: SelectCreateDlg(hwnd, e::hinst); break; } RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT); } |
Сообщ.
#7
,
|
|
|
Цитата Mr.Brooks @ Для ясности приведу рисунки и вырезки из кода. --- ..Получаем отмену выделения, что логично.. Нет, это просто баг в программе. Обработчик пункта меню должен быть не тем же самым, что клик левой кнопкой мыши по окну. case WM_COMMAND: switch (LOWORD(wparam)) { case IDM_PROPERTIES: DataElementProperties(hwnd); break; } // vvvvvvvvvv // return 0; // // ^^^^^^^^^^ case WM_LBUTTONDOWN: FormLButtonDown(hwnd); return 0; |
Сообщ.
#8
,
|
|
|
комментарии
Правила которыми я руководствуюсь при написании программы: 1. Функции начинаются с названия модуля(файла) в котором они расположены. 2. Функции располагаются в алфавитном порядке. 3. Глобальные переменные в пространстве имен e:: 4. Комментариев в программе не пишу принципиально. Любая функция или переменная должна иметь имя которое объясняет её назначение. Ислючение самая первая строчка с названием файла. Если комментарии есть это временно для отладки. ... Теперь конкретно к приведенному коду e::projectstep - переменная для реализации undo - redo текущий шаг = 0 пока в программе не меняется, зарезервирована. e::elementselectubound - число выделенных элементов e::elementselect[0] - массив список выделенных элементов e::elementselectstop - текущий выделенный элемент e::element[k][i].schpoint[0][0+2*j] - точки для рисования элементов SelectCreateDlg - если выделено более 1 элемента (например они расположены друг на друге) появляется диалог селектор в котором нужно выбрать один из них. AssemblyCreateDlg - диалог свойст элементов прямоугольников. ! его то и нужно отобразить при повторном клике по выделенному элементу. Добавлено Цитата // vvvvvvvvvv // return 0; // // ^^^^^^^^^^ вообще-то есть у меня return 0; для case WM_COMMAND: switch очень длинный, потерялся return 0 при копировании, извиняюсь) case WM_COMMAND: switch (LOWORD(wparam)) { case IDM_NEW: FileNew(hwnd); break; case IDM_OPEN: FileOpen(hwnd); break; case IDM_SAVE: FileSave(hwnd); break; case IDM_SAVE_AS: FileSaveAs(hwnd); break; case IDM_PRINT: MessageBox(hwnd, "PRINT", "Message", MB_OK); break; case IDM_EXIT: SendMessage(hwnd, WM_CLOSE, NULL, NULL); break; case IDM_UNDO: MessageBox(hwnd, "UNDO", "Message", MB_OK); break; case IDM_REDO: MessageBox(hwnd, "REDO", "Message", MB_OK); break; case IDM_CUT: MessageBox(hwnd, "CUT", "Message", MB_OK); break; case IDM_COPY: MessageBox(hwnd, "COPY", "Message", MB_OK); break; case IDM_PASTE: MessageBox(hwnd, "PASTE", "Message", MB_OK); break; case IDM_DELETE: MessageBox(hwnd, "DELETE", "Message", MB_OK); break; case IDM_PROPERTIES: DataElementProperties(hwnd); break; case IDM_ASSEMBLY: DataElementAdd(hwnd, 0); break; // ... case IDM_TUTORIAL: MessageBox(hwnd, "TUTORIAL", "Message", MB_OK); break; case IDM_ABOUT: AboutCreateDlg(hwnd, e::hinst); break; } return 0; |
Сообщ.
#9
,
|
|
|
Цитата Mr.Brooks @ case WM_LBUTTONDOWN: FormLButtonDown(hwnd); return 0; У тебя это сообщение, SendMessage(hwnd, WM_COMMAND, (WPARAM) IDM_PROPERTIES, NULL);, вызывается при нажатии кнопки? Лучше бы при отпускании WM_LBUTTONUP. Возможно проблема в этом. |
Сообщ.
#10
,
|
|
|
Разобрался )
Оказывается в функции DataElementProperties() которая должна отображать диалог свойств перепутались два понятия 1 выделенный элемент на котором пользователь кликнул. (он остается выделенным независимо от движения мыши) 2 выделенный элемент текущий (он зависит от координат мыши) При выборе кликом мышью по выделенному элементу п1 совпадает с п2 так как координаты мыши и выделенного элемента совпадают. При выборе через меню между п1 и п2 есть разница. Нужно сделать вот так: void DataElementProperties(HWND hwnd) { int k; k = e::projectstep; if (e::elementselectstop != -1) { switch (e::element[k][e::elementselectstop].type) { case 0: AssemblyCreateDlg(hwnd, e::hinst); break; } } } Всем спасибо. Вопрос решен. |