Original in Russian: http://18delphi.blogspot.ru/2013/05/blog-post_8549.html
Tl3GradientWaitbar is described here - Gradient wait bar
Tl3GradientWaitbar is described here - Gradient wait bar
unit l3AsincMessageWindow; {* Output the asynchronous message window in a separate thread } interface uses Windows, Classes, Graphics, Messages, ImgList, l3GradientWaitbar ; type Tl3AsincMessageWindow = class(TThread) {* Asynchronous window with a message } private // internal variables f_Handle : HWND; f_Caption : Il3CString; f_Canvas : TCanvas; f_Progress : THandle; f_IconSize : TSize; f_IconHandle : HICON; f_TextRect : TRect; f_SizeExcludeText : TSize; f_Waitbar : Tl3GradientWaitbar; f_Size : TSize; f_Images : TCustomImageList; f_ImageIndex : Integer; f_BottomContext : Integer; f_WindowOrigin: TPoint; f_WindowExtent: TSize; f_ScreenWidth: Longint; f_WaitTimeout: Cardinal; f_Attached: Boolean; f_InPaint: Integer; private // property methods procedure pm_SetCaption(const aValue : Il3CString); {-} procedure pm_SetProgress(const Value : THandle); {-} private // internal methods procedure InitDC; {-} procedure UpdateSize; {-} procedure InitFont; {-} procedure Paint; {-} function DrawTextRect : TRect; {-} procedure PaintProgress(aInitPaint : Boolean = True); {-} function ProgressRect : TRect; {-} function BottomContext : Integer; {-} procedure RegisterClass; {* - registers the class of the window to be created. } procedure CreateWindow; {* - creates the window. } procedure DestroyWindow; {* - destroys the window. } procedure Show; {* - Shows the window; it is displayed on center of the current Application.MainForm } function ContextSpace : Integer; {-} function ContextRect : TRect; {-} procedure CalcSizeExcludeText; {-} procedure CalcTextRect; {-} protected // protected methods function CalcSize : TSize; {* - calculates the size of the form. } procedure Execute; override; {-} public // public methods constructor Create(const aCaption : Il3CString = nil; aImages : TCustomImageList = nil; aImageIndex : Integer = -1; anAttachWnd : THandle = 0; anInitialWait : Cardinal = 0); reintroduce; virtual; {-} destructor Destroy; override; {-} public // properties property Caption : Il3CString read f_Caption write pm_SetCaption; {-} property Progress : THandle read f_Progress write pm_SetProgress; {-} end;//Tl3AsincMessageWindow procedure ActivateAllAsyncWindows(anActive: Boolean); implementation uses Controls, Types, Math, SysUtils, Forms, MultiMon ; //////////////////////////////////////////////////////////////////////////////// const cClassName = 'l3AsincMessageWindow'; {* - name of the registered class. } cFrameSize = 4; {* - frame size. } cSpace = 5; {* - space between the objects. } cProgressHeight = 15; {* - progress bar height. } //////////////////////////////////////////////////////////////////////////////// var g_AllAsyncWindows: TThreadList = nil; function WindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; // Potential hazard when switching to 64 bits – pointer to Self is moved through SetWindowLong (32 bits). procedure lpDef; begin Result := DefWindowProc(hWnd, Msg, wParam, lParam); end; var l_Window: Tl3AsincMessageWindow; begin case Msg of WM_PAINT: begin l_Window := Tl3AsincMessageWindow(GetWindowLong(hWnd, GWL_USERDATA)); if Assigned(l_Window) then l_Window.Paint; Result := 0; end; WM_DESTROY: begin PostQuitMessage(0); Result := 0; end; WM_CLOSE: SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_HIDEWINDOW); else lpDef; end; end; // Class Tl3AsincMessageWindow procedure Tl3AsincMessageWindow.InitFont; begin Assert(f_Canvas <> nil); with f_Canvas.Font do begin Size := 10; Charset := RUSSIAN_CHARSET; end;//with f_Canvas.Font end; constructor Tl3AsincMessageWindow.Create(const aCaption : Il3CString = nil; aImages : TCustomImageList = nil; aImageIndex : Integer = -1; anAttachWnd : THandle = 0; anInitialWait : Cardinal = 0); var l_Rect: TRect; l_Form: TCustomForm; l_FormHandle: THandle; l_MonInfo: TMonitorInfo; begin inherited Create(True); Assert(Suspended); f_InPaint := 0; f_WaitTimeout := anInitialWait; f_Attached := (anAttachWnd <> 0); if f_Attached then begin l_FormHandle := anAttachWnd; if not GetWindowRect(l_FormHandle,l_Rect) then begin f_Attached := False; end//not GetWindowRect(l_FormHandle,l_Rect) else with l_Rect do begin f_WindowOrigin := Point(Left, Bottom); f_ScreenWidth := Right - Left; f_WindowExtent.cx := 0; f_WindowExtent.cy := 0; end;//with l_Rect end;//f_Attached if not f_Attached then begin l_Form := Application.MainForm; if Assigned(l_Form) then begin l_FormHandle := l_Form.Handle; if not GetWindowRect(l_FormHandle,l_Rect) then begin l_FormHandle := 0; GetWindowRect(Application.Handle,l_Rect); end;//not GetWindowRect(l_FormHandle,l_Rect) end//Assigned(l_Form) else begin l_FormHandle := 0; GetWindowRect(Application.Handle,l_Rect); end;//Assigned(l_Form) f_WindowOrigin := l_Rect.TopLeft; l_MonInfo.cbSize := SizeOf(l_MonInfo); GetMonitorInfo(MonitorFromPoint(f_WindowOrigin,MONITOR_DEFAULTTONEAREST), @l_MonInfo); with l_MonInfo.rcWork do f_ScreenWidth := Right - Left; if (l_FormHandle = 0) then begin f_WindowOrigin := l_MonInfo.rcWork.TopLeft; f_WindowExtent.cx := f_ScreenWidth; with l_MonInfo.rcWork do f_WindowExtent.cy := Bottom - Top; end//l_FormHandle = 0 else with l_Rect do begin f_WindowExtent.cx := Right - Left; f_WindowExtent.cy := Bottom - Top; end;//with l_Rect end;//not f_Attached f_Canvas := TCanvas.Create; // Waitbar f_Waitbar := Tl3GradientWaitbar.Create; if not f_Attached then begin // We get an icon if Assigned(aImages) and (aImageIndex <> -1) and ((aImageIndex >= 0) and (aImageIndex < aImages.Count)) then begin f_Images := aImages; f_ImageIndex := aImageIndex; f_IconSize.cx := aImages.Width; f_IconSize.cy := aImages.Height; end//Assigned(aImages).. else begin f_IconSize.cx := GetSystemMetrics(SM_CXICON); f_IconSize.cy := GetSystemMetrics(SM_CYICON); f_IconHandle := LoadIcon(0, IDI_EXCLAMATION); end;//Assigned(aImages).. end;//not f_Attached // Size with text excluded CalcSizeExcludeText; // Caption if aCaption <> nil then Caption := aCaption else Caption := str_l3mmLongOperation.AsCStr; // Size f_Size := CalcSize; // Initialized as an empty value f_BottomContext := -1; f_Handle := 0; Resume; end; destructor Tl3AsincMessageWindow.Destroy; // override; {-} begin while (f_InPaint <> 0) do Sleep(0); l3Free(f_Canvas); l3Free(f_Waitbar); f_Caption := nil; inherited; end; procedure Tl3AsincMessageWindow.CalcTextRect; begin SetRectEmpty(f_TextRect); if not f_Attached then begin // Maximum text width f_TextRect.Right := f_ScreenWidth div 2 - f_SizeExcludeText.cx; Assert(f_Canvas <> nil); // Text size DrawText(f_Canvas.Handle, PAnsiChar(l3Str(f_Caption)), -1, f_TextRect, DT_WORDBREAK or DT_CALCRECT); end;//not f_Attached end; procedure Tl3AsincMessageWindow.UpdateSize; begin if f_Handle <> 0 then begin CalcTextRect; f_Size := CalcSize; end; end; procedure Tl3AsincMessageWindow.pm_SetCaption(const aValue : Il3CString); {-} begin if not l3Same(aValue, f_Caption) then begin f_Caption := aValue; UpdateSize; end;//not l3Same(aValue, f_Caption) end; procedure Tl3AsincMessageWindow.Show; {* - Shows the window; it is displayed on center of the current Application.MainForm } var lTop : Integer; lLeft : Integer; begin // Waitbar Inc(f_InPaint); try with ProgressRect do begin Assert(f_Waitbar <> nil); f_Waitbar.SetBounds(0, 0, Right - Left, Bottom - Top); end;//ProgressRect f_Waitbar.Speed := 1; finally Dec(f_InPaint); end;//try..finally // Position if f_Attached then begin lTop := f_WindowOrigin.y; lLeft := f_WindowOrigin.x; end//f_Attached else begin lTop := f_WindowOrigin.y + ((f_WindowExtent.cy div 2) - (f_Size.cy div 2)); lLeft := f_WindowOrigin.x + ((f_WindowExtent.cx div 2) - (f_Size.cx div 2)); end;//f_Attached SetWindowPos(f_Handle, 0, lLeft, lTop, f_Size.cx, f_Size.cy, SWP_SHOWWINDOW or SWP_NOACTIVATE); end; function Tl3AsincMessageWindow.ContextRect : TRect; begin if f_Attached then SetRectEmpty(Result) else begin GetClientRect(f_Handle, Result); OffsetRect(Result, -Result.Left, -Result.Top); InflateRect(Result, -ContextSpace, -ContextSpace); end; end; function Tl3AsincMessageWindow.ContextSpace : Integer; begin Result := cFrameSize + cSpace; end; procedure Tl3AsincMessageWindow.CalcSizeExcludeText; begin if not f_Attached then begin // From the frame to components Inc(f_SizeExcludeText.cx, ContextSpace * 2); Inc(f_SizeExcludeText.cy, ContextSpace * 2); // Icon Inc(f_SizeExcludeText.cx, f_IconSize.cx); Inc(f_SizeExcludeText.cy, f_IconSize.cy); // Space to text Inc(f_SizeExcludeText.cx, cSpace); end else begin f_SizeExcludeText.cx := f_ScreenWidth; f_SizeExcludeText.cy := cFrameSize; end; end; function Tl3AsincMessageWindow.CalcSize : TSize; begin Result := f_SizeExcludeText; // Progress // Frame Inc(Result.cy, cFrameSize); // Progress Inc(Result.cy, cProgressHeight); // Caption Width Inc(Result.cx, f_TextRect.Right); // Caption Height if f_TextRect.Bottom > f_IconSize.cy then begin Dec(Result.cy, f_IconSize.cy); Inc(Result.cy, f_TextRect.Bottom); end; end; function Tl3AsincMessageWindow.BottomContext : Integer; begin // Calculate one time if f_BottomContext = -1 then begin if f_Attached then f_BottomContext := 0 else f_BottomContext := ContextSpace + Max(f_IconSize.cy, f_TextRect.Bottom) + cSpace; end; // Result := f_BottomContext; end; function Tl3AsincMessageWindow.ProgressRect : TRect; begin Result := Rect(0, 0, f_Size.cx, f_Size.cy); InflateRect(Result, -cFrameSize, -cFrameSize); if not f_Attached then Result.Top := Result.Bottom - cProgressHeight; InflateRect(Result, -2, -2); end; procedure Tl3AsincMessageWindow.PaintProgress(aInitPaint : Boolean = True); var lPStruct : TPaintStruct; begin Inc(f_InPaint); try if (f_Waitbar = nil) or (f_Canvas = nil) then Exit; Assert(f_Waitbar <> nil); f_Waitbar.BackBuf.Canvas.Lock; try f_Waitbar.ManualProgress(1); Assert(f_Waitbar <> nil); if aInitPaint then BeginPaint(f_Handle, lPStruct); try Assert(f_Canvas <> nil); with ProgressRect do BitBlt(f_Canvas.Handle, Left, Top, Right - Left, Bottom - Top, f_Waitbar.BackBuf.Canvas.Handle, 0, 0, cmSrcCopy); finally if aInitPaint then EndPaint(f_Handle, lPStruct); end;//try..finally finally Assert(f_Waitbar <> nil); f_Waitbar.BackBuf.Canvas.Unlock; end;//try..finally finally Dec(f_InPaint); end;//try..finally end; function Tl3AsincMessageWindow.DrawTextRect : TRect; begin Result.Right := f_Size.cx - ContextSpace; Result.Left := Result.Right - f_TextRect.Right; Result.Top := ContextSpace; Result.Bottom := BottomContext - cSpace; end; procedure Tl3AsincMessageWindow.Paint; {-} var lRect : TRect; lPStruct : TPaintStruct; lUpdate : Boolean; lFlags : Integer; lY : Integer; begin Inc(f_InPaint); try if (f_Canvas = nil) then Exit; lUpdate := GetUpdateRect(f_Handle, lRect, False); if EqualRect(lRect, ProgressRect) then begin PaintProgress; Exit; end;//EqualRect(lRect, ProgressRect) // Client output area GetClientRect(f_Handle, lRect); // Output if lUpdate then BeginPaint(f_Handle, lPStruct); try // Loading Assert(f_Canvas <> nil); with f_Canvas do begin Brush.Color := clBtnFace; FillRect(lRect); end;//with f_Canvas // Outer frame Assert(f_Canvas <> nil); DrawEdge(f_Canvas.Handle, lRect, BDR_RAISEDINNER, BF_RECT); if not f_Attached then begin // Context frame InflateRect(lRect, -cFrameSize, -cFrameSize); lRect.Bottom := BottomContext; Assert(f_Canvas <> nil); DrawEdge(f_Canvas.Handle, lRect, BDR_SUNKENINNER, BF_RECT); end//not f_Attached else InflateRect(lRect, -cFrameSize, 0); // Progress bar frame lRect.Top := BottomContext + cFrameSize; lRect.Bottom := lRect.Top + cProgressHeight; Assert(f_Canvas <> nil); DrawEdge(f_Canvas.Handle, lRect, BDR_SUNKENINNER, BF_RECT); if not f_Attached then begin // Output area lRect := ContextRect; // Icon lY := ((lRect.Bottom - lRect.Top) - f_IconSize.cy) div 2; Assert(f_Canvas <> nil); if Assigned(f_Images) then f_Images.Draw(f_Canvas, ContextSpace, lY, f_ImageIndex) else Windows.DrawIcon(f_Canvas.Handle, ContextSpace, lY, f_IconHandle); end;//not f_Attached // Progress PaintProgress(False); if not f_Attached then begin // Caption if not l3IsNil(f_Caption) then begin lRect := DrawTextRect; if f_IconSize.cy > f_TextRect.Bottom then begin lFlags := DT_SINGLELINE or DT_VCENTER; end else lFlags := DT_WORDBREAK; Assert(f_Canvas <> nil); Windows.DrawText(f_Canvas.Handle, PAnsiChar(l3Str(f_Caption)), -1, lRect, lFlags); end;//if not l3IsNil(f_Caption) then end; finally if lUpdate then EndPaint(f_Handle, lPStruct); end;//try..finally finally Dec(f_InPaint); end;//try..finally end; procedure Tl3AsincMessageWindow.RegisterClass; {* - registers the class of the window to be created. } var l_Class: TWndClass; begin if not GetClassInfo(hInstance, cClassName, l_Class) then begin l3FillChar(l_Class, SizeOf(l_Class), 0); l_Class.style := CS_OWNDC or CS_NOCLOSE or CS_HREDRAW or CS_VREDRAW; l_Class.lpfnWndProc := @WindowProc; l_Class.lpszClassName := cClassName; l_Class.hInstance := hInstance; Windows.RegisterClass(l_Class); end; end; procedure Tl3AsincMessageWindow.InitDC; begin Assert(f_Canvas <> nil); f_Canvas.Handle := GetDC(f_Handle); InitFont; UpdateSize; end; procedure Tl3AsincMessageWindow.CreateWindow; begin // Class registration RegisterClass; // Window creation f_Handle := Windows.CreateWindow(cClassName, '', WS_POPUP, Integer(CW_USEDEFAULT), 0, Integer(CW_USEDEFAULT), 0, 0, 0, hInstance, nil); SetWindowLong(f_Handle, GWL_USERDATA, Integer(Self)); if not f_Attached then begin SetWindowPos(f_Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE); g_AllAsyncWindows.Add(Pointer(f_Handle)); end;//not f_Attached // DC InitDC; // Show the window Show; end; procedure Tl3AsincMessageWindow.DestroyWindow; begin if f_Handle = 0 then Exit; if not f_Attached then g_AllAsyncWindows.Remove(Pointer(f_Handle)); SetWindowLong(f_Handle, GWL_USERDATA, 0); SendMessage(f_Handle, WM_PAINT, 0, 0); Windows.DestroyWindow(f_Handle); end; procedure Tl3AsincMessageWindow.Execute; var l_Message : TMsg; l_Rect : TRect; l_Time : Cardinal; procedure lpProgress; const cDelay = 1; begin if (GetTickCount - cDelay >= l_Time) then begin l_Rect := ProgressRect; Inc(f_InPaint); try Windows.RedrawWindow(f_Handle, @l_Rect, 0, RDW_INVALIDATE); finally Dec(f_InPaint); end;//try..finally l_Time := GetTickCount; end;//GetTickCount - cDelay >= l_Time end; begin while not Terminated and (f_WaitTimeout > 0) do begin Sleep(100); if Terminated then exit; if f_WaitTimeout > 100 then Dec(f_WaitTimeout, 100) else begin Sleep(f_WaitTimeout); Break; end; end; if Terminated then exit; CreateWindow; try l_Time := GetTickCount; repeat if Terminated then DestroyWindow; if PeekMessage(l_Message, 0, 0, 0, PM_REMOVE) then begin if (l_Message.Message = WM_QUIT) or ((l_Message.Message = WM_CLOSE) and (l_Message.hWnd = f_Handle)) then begin f_Handle := 0; Break; end; TranslateMessage(l_Message); DispatchMessage(l_Message); end; if not Terminated then lpProgress; until False; finally DestroyWindow; end;//try..finally end; procedure Tl3AsincMessageWindow.pm_SetProgress(const Value: THandle); begin f_Progress := Value; end; procedure FinalizeAllAsyncWindows; begin FreeAndNil(g_AllAsyncWindows); end; procedure ActivateAllAsyncWindows(anActive: Boolean); var l_IDX: Integer; const cInsertAfter: array [Boolean] of HWND = (HWND_NOTOPMOST, HWND_TOPMOST); begin with g_AllAsyncWindows.LockList do try for l_IDX := Count-1 downto 0 do SetWindowPos(THandle(Items[l_IDX]), cInsertAfter[anActive], 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE); finally g_AllAsyncWindows.UnlockList; end; end; initialization g_AllAsyncWindows := TThreadList.Create; l3System.AddExitProc(FinalizeAllAsyncWindows); end.
triad -> thread
ОтветитьУдалить