-
Окно WS_POPUP прорисовываю самостоятельно, в т.ч. свой заголовок с кнопками сворачивания/разворачивания/закрытия (дальше - СРЗ). Обработкой сообщения WM_NCHITTEST заставил систему поверить, что мой заголовок - это и есть заголовок окна. Теперь окно перетаскивается за "заголовок" без дополнительных телодвижений в программе.
Стоит задача: обработать случай, когда щелчок попадает по одной из нарисованных мной кнопок СРЗ.
Мои действия: 1. Пишу обработчик WM_NCLBUTTONUP. Обнаруживаю, что сообщение не приходит. MSDN+Google: нужно обработать WM_NCLBUTTONDOWN, чтобы система не перехватывала ввод от мыши и не думала, что пользователь начинает перетаскивать окно. 2. Добавляю обработчик WM_LBUTTONDOWN, возвращаю 0 в соответствии с MSDN, чтобы дать понять, что обрабатываю сам. Получаю закономерную реакцию: кнопки нажимаются, а окно становится неподвижным. 3. Изменяю обработчик WM_LBUTTONDOWN так, чтобы 0 возвращался только при условии, что мышь на одной из кнопок СРЗ, в противном случае передаю сообщение на растерзание DefWindowProc(). Кнопки нажимаются, окно перетаскивается. Но после того, как перетаскивание случилось, кнопки уже не реагируют. 4. MSDN+Google подсказывают красивое на первый взгляд решение: не дать системе опомниться и в обработчике WM_NCHITTEST для областей, соответствующих кнопкам, возвращать HTBORDER, а не HTCAPTION. Результат: в зависимости от того, что возвращается вместо HTCAPTION, поведение меняется, но желаемого добиться не удаётся. Как частный случай: если возвращать HTCLOSE - система при щелчке нагло прорисовывает стандартную кнопку закрытия окна поверх нарисованной мною.
Есть ли вообще шансы обойтись без ручной обработки перетаскивания окна? Каким способом вообще лучше всего реализовать такую функциональность?
P.S. По традиции, системы: все Win32.
-
Возвращай HTCAPTION только если ты находишься в области своего заголовка, но не своих кнопок, в случае кнопок возвращай HTCLIENT и обрабатывай нажатия самостоятельно (только SetCapture не надо делать)
-
Виноват, SetCapture надо делать
-
Упс, это ж тогда обрабатывать обычное WM_LBUTTONUP? Работает, спс.
-
Без SetCapture() почему-то %)
-
ProgRAMmer Dimonych © (05.02.10 01:36) [4]
> Без SetCapture() почему-то %)
Попробуй нажать левую клавишу мыши на кнопке и не отпуская, увести мышь в другую область своего окна, за область своего окна, на заголовок своего окна и понаблюдать за поведением.
Зачем нужно вызывать SetCapture - если ты нажмешь левой клавишей мыши в области кнопки управления окном и начнешь перемещать мышь, не отпуская нажатой клавиши, без SetCapture каждое мышиное событие будет посылать сообщения WM_NCHITTEST, WM_SETCURSOR, WM_(NC)MOUSEMOVE, пока курсор находится в области твоего окна. При уходе мыши из твоего окна, мышиные сообщения твоему окну не посылаются и у тебя останется состояние "зависшего WM_LBUTTONDOWN".
Если же при обработке WM_LBUTTONDOWN вызвать SetCapture, то во-первых, при движении мыши не будет посылаться WM_NCHITTEST, во-вторых, сообщения о перемещениях и состоянии клавиш мыши будут посылаться твоему окну, вне зависимости от того, где находится курсор мыши.
До тех пор, пока ты не отпустишь кнопку.
-
Я когда-то писал пример для самостоятельного рисования заголовка с кнопками: unit Popup2;
interface
uses
Windows, Messages, SysUtils,
Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TCapturedButton = (cbtNone, cbtClose, cbtMinimize);
TfPopup2 = class(TForm)
ClientPanel: TPanel;
CloseButton: TButton;
procedure FormPaint(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure CloseButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormDeactivate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
private
FCaptionRect : TRect;
FCapturedButton : TCapturedButton;
FMouseInCloseButton : Boolean;
FMouseInMinimizeButton : Boolean;
FCloseButtonRect : TRect;
FMinimizeButtonRect : TRect;
FWindowActive : Boolean;
FWindowMinimized : Boolean;
FSavedHeight : Integer;
procedure WMHCHitTest (var Message : TWMNCHitTest); message WM_NCHITTEST;
procedure MinimizeButtonClick;
end;
var
fPopup2: TfPopup2;
implementation
procedure TfPopup2.FormPaint(Sender: TObject);
var
ARect : TRect;
CaptionFlags : UINT;
MinButtonKind : UINT;
begin
CaptionFlags := DC_GRADIENT OR DC_SMALLCAP OR DC_TEXT;
if FWindowActive then
CaptionFlags := CaptionFlags OR DC_ACTIVE;
ARect := ClientRect;
DrawEdge(Canvas.Handle, ARect, BDR_RAISED, BF_RECT);
DrawCaption(Handle, Canvas.Handle, FCaptionRect, CaptionFlags);
if FWindowMinimized then
MinButtonKind := DFCS_CAPTIONMAX
else
MinButtonKind := DFCS_CAPTIONMIN;
if (FCapturedButton = cbtClose) AND (FMouseInCloseButton) then
DrawFrameControl(Canvas.Handle, FCloseButtonRect, DFC_CAPTION, DFCS_CAPTIONCLOSE OR DFCS_PUSHED)
else
DrawFrameControl(Canvas.Handle, FCloseButtonRect, DFC_CAPTION, DFCS_CAPTIONCLOSE);
if (FCapturedButton = cbtMinimize) AND (FMouseInMinimizeButton) then
DrawFrameControl(Canvas.Handle, FMinimizeButtonRect, DFC_CAPTION, MinButtonKind OR DFCS_PUSHED)
else
DrawFrameControl(Canvas.Handle, FMinimizeButtonRect, DFC_CAPTION, MinButtonKind);
end;
procedure TfPopup2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
procedure TfPopup2.CloseButtonClick(Sender: TObject);
begin
Close();
end;
procedure TfPopup2.FormCreate(Sender: TObject);
begin
SetRect(FCaptionRect, GetSystemMetrics(SM_CXSIZEFRAME), GetSystemMetrics(SM_CYSIZEFRAME),
Width - GetSystemMetrics(SM_CXSIZEFRAME),
GetSystemMetrics(SM_CYSMCAPTION)+GetSystemMetrics(SM_CYSIZEFRAME)-1);
FCloseButtonRect := FCaptionRect;
InflateRect(FCloseButtonRect, -1, -1);
FCloseButtonRect.Left := FCloseButtonRect.Right -
(FCloseButtonRect.Bottom - FCloseButtonRect.Top);
FMinimizeButtonRect := FCloseButtonRect;
OffsetRect(FMinimizeButtonRect, - (FCloseButtonRect.Right - FCloseButtonRect.Left) - 2, 0);
end;
procedure TfPopup2.FormActivate(Sender: TObject);
begin
FWindowActive := true;
InvalidateRect(Handle, @FCaptionRect, false);
end;
procedure TfPopup2.FormDeactivate(Sender: TObject);
begin
FWindowActive := false;
InvalidateRect(Handle, @FCaptionRect, false);
end;
procedure TfPopup2.WMHCHitTest (var Message : TWMNCHitTest);
begin
if PtInRect (FCaptionRect,
ScreenToClient(SmallPointToPoint(Message.Pos))) and NOT
PtInRect (FCloseButtonRect,
ScreenToClient(SmallPointToPoint(Message.Pos))) and not
PtInRect (FMinimizeButtonRect,
ScreenToClient(SmallPointToPoint(Message.Pos))) then
Message.Result := HTCAPTION
else
inherited;
end;
procedure TfPopup2.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button <> mbLeft then
Exit;
if NOT PtInRect(FCaptionRect, Point(X,Y)) then
Exit;
if PtInRect(FCloseButtonRect, Point(X,Y)) then begin
FCapturedButton := cbtClose;
SetCapture(Handle);
FMouseInCloseButton := true;
end else if PtInRect(FMinimizeButtonRect, Point(X,Y)) then begin
FCapturedButton := cbtMinimize;
FMouseInMinimizeButton := true;
SetCapture(Handle);
end;
if FCapturedButton <> cbtNone then
InvalidateRect(Handle, @FCaptionRect, false);
end;
procedure TfPopup2.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
OldButton : TCapturedButton;
begin
if Button <> mbLeft then
Exit;
if FCapturedButton = cbtNone then
Exit;
OldButton := cbtNone;
if PtInRect(FCloseButtonRect, Point(X,Y)) then
OldButton := cbtClose
else if PtInRect(FMinimizeButtonRect, Point(X,Y)) then
OldButton := cbtMinimize;
ReleaseCapture();
FCapturedButton := cbtNone;
if OldButton <> cbtNone then begin
InvalidateRect(Handle, @FCaptionRect, false);
case OldButton of
cbtClose:
Close();
cbtMinimize:
MinimizeButtonClick;
end;
end;
end;
procedure TfPopup2.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if FCapturedButton = cbtNone then
Exit;
FMouseInCloseButton := PtInRect(FCloseButtonRect, Point(X,Y));
FMouseInMinimizeButton := PtInRect(FMinimizeButtonRect, Point(X,Y));
InvalidateRect(Handle, @FCloseButtonRect, false);
InvalidateRect(Handle, @FMinimizeButtonRect, false);
end;
procedure TfPopup2.MinimizeButtonClick;
begin
if NOT FWindowMinimized then begin
FSavedHeight := Height;
Height := GetSystemMetrics(SM_CYSIZEFRAME)*2 +
FCaptionRect.Bottom - FCaptionRect.Top;
end else
Height := FSavedHeight;
FWindowMinimized := NOT FWindowMinimized;
Invalidate;
end;
end.
-
Спасибо за пример, сейчас попробую вчитаться. Мне, наверное, показалось, что всё работает нормально, потому что пока что никакой визуально наблюдаемой реакции на нажатие (без отпускания) и наведение для этих кнопок не реализовано... Так что надо будет действительно покопаться как следует.
|