unit PersistentForm; interface uses Classes, SysUtils, Forms, RegistryEx, LCLIntf, Registry, Controls, ComCtrls, ExtCtrls, LCLType; type { TPersistentForm } TPersistentForm = class(TComponent) private FEntireVisible: Boolean; FMinVisiblePart: Integer; FRegistryContext: TRegistryContext; FResizeEventOccured: Boolean; procedure LoadControl(Control: TControl); procedure SaveControl(Control: TControl); procedure WindowStateChange(Sender: TObject); public FormRestoredSize: TRect; FormWindowState: TWindowState; FormFullScreen: Boolean; Form: TForm; procedure LoadFromRegistry(RegistryContext: TRegistryContext); procedure SaveToRegistry(RegistryContext: TRegistryContext); function CheckEntireVisible(Rect: TRect): TRect; function CheckPartVisible(Rect: TRect; Part: Integer): TRect; procedure Load(Form: TForm; DefaultMaximized: Boolean = False; DefaultFullScreen: Boolean = False); procedure Save(Form: TForm); constructor Create(AOwner: TComponent); override; procedure SetFullScreen(State: Boolean); property RegistryContext: TRegistryContext read FRegistryContext write FRegistryContext; published property MinVisiblePart: Integer read FMinVisiblePart write FMinVisiblePart; property EntireVisible: Boolean read FEntireVisible write FEntireVisible; end; procedure Register; implementation procedure Register; begin RegisterComponents('Common', [TPersistentForm]); end; { TPersistentForm } procedure TPersistentForm.LoadControl(Control: TControl); var I: Integer; WinControl: TWinControl; begin if Control is TListView then begin with Form, TRegistryEx.Create do try RootKey := RegistryContext.RootKey; OpenKey(RegistryContext.Key + '\Forms\' + Form.Name + '\' + Control.Name, True); for I := 0 to TListView(Control).Columns.Count - 1 do begin if ValueExists('ColWidth' + IntToStr(I)) then TListView(Control).Columns[I].Width := ReadInteger('ColWidth' + IntToStr(I)); end; finally Free; end; end; if (Control is TPanel) then begin with Form, TRegistryEx.Create do try RootKey := RegistryContext.RootKey; OpenKey(RegistryContext.Key + '\Forms\' + Form.Name + '\' + Control.Name, True); if (TPanel(Control).Align = alRight) or (TPanel(Control).Align = alLeft) then begin if ValueExists('Width') then TPanel(Control).Width := ReadInteger('Width'); end; if (TPanel(Control).Align = alTop) or (TPanel(Control).Align = alBottom) then begin if ValueExists('Height') then TPanel(Control).Height := ReadInteger('Height'); end; finally Free; end; end; if Control is TWinControl then begin WinControl := TWinControl(Control); if WinControl.ControlCount > 0 then begin for I := 0 to WinControl.ControlCount - 1 do begin if WinControl.Controls[I] is TControl then begin LoadControl(WinControl.Controls[I]); end; end; end; end; end; procedure TPersistentForm.SaveControl(Control: TControl); var I: Integer; WinControl: TWinControl; begin if Control is TListView then begin with Form, TRegistryEx.Create do try RootKey := RegistryContext.RootKey; OpenKey(RegistryContext.Key + '\Forms\' + Form.Name + '\' + Control.Name, True); for I := 0 to TListView(Control).Columns.Count - 1 do begin WriteInteger('ColWidth' + IntToStr(I), TListView(Control).Columns[I].Width); end; finally Free; end; end; if (Control is TPanel) then begin with Form, TRegistryEx.Create do try RootKey := RegistryContext.RootKey; OpenKey(RegistryContext.Key + '\Forms\' + Form.Name + '\' + Control.Name, True); if (TPanel(Control).Align = alRight) or (TPanel(Control).Align = alLeft) then begin WriteInteger('Width', TPanel(Control).Width); end; if (TPanel(Control).Align = alTop) or (TPanel(Control).Align = alBottom) then begin WriteInteger('Height', TPanel(Control).Height); end; finally Free; end; end; if Control is TWinControl then begin WinControl := TWinControl(Control); if WinControl.ControlCount > 0 then begin for I := 0 to WinControl.ControlCount - 1 do begin if WinControl.Controls[I] is TControl then begin SaveControl(WinControl.Controls[I]); end; end; end; end; end; procedure TPersistentForm.LoadFromRegistry(RegistryContext: TRegistryContext); begin with TRegistryEx.Create do try RootKey := RegistryContext.RootKey; OpenKey(RegistryContext.Key + '\Forms\' + Form.Name, True); // Restored size FormRestoredSize.Left := ReadIntegerWithDefault('RestoredLeft', FormRestoredSize.Left); FormRestoredSize.Top := ReadIntegerWithDefault('RestoredTop', FormRestoredSize.Top); FormRestoredSize.Right := ReadIntegerWithDefault('RestoredWidth', FormRestoredSize.Right - FormRestoredSize.Left) + FormRestoredSize.Left; FormRestoredSize.Bottom := ReadIntegerWithDefault('RestoredHeight', FormRestoredSize.Bottom - FormRestoredSize.Top) + FormRestoredSize.Top; // Other state FormWindowState := TWindowState(ReadIntegerWithDefault('WindowState', Integer(FormWindowState))); FormFullScreen := ReadBoolWithDefault('FullScreen', FormFullScreen); finally Free; end; end; procedure TPersistentForm.SaveToRegistry(RegistryContext: TRegistryContext); begin with Form, TRegistryEx.Create do try RootKey := RegistryContext.RootKey; OpenKey(RegistryContext.Key + '\Forms\' + Form.Name, True); // Restored size WriteInteger('RestoredWidth', FormRestoredSize.Right - FormRestoredSize.Left); WriteInteger('RestoredHeight', FormRestoredSize.Bottom - FormRestoredSize.Top); WriteInteger('RestoredTop', FormRestoredSize.Top); WriteInteger('RestoredLeft', FormRestoredSize.Left); // Other state WriteInteger('WindowState', Integer(FormWindowState)); WriteBool('FullScreen', FormFullScreen); finally Free; end; end; function TPersistentForm.CheckEntireVisible(Rect: TRect): TRect; var Width: Integer; Height: Integer; begin Result := Rect; Width := Rect.Right - Rect.Left; Height := Rect.Bottom - Rect.Top; if Result.Left < (Screen.DesktopLeft) then begin Result.Left := Screen.DesktopLeft; Result.Right := Screen.DesktopLeft + Width; end; if Result.Right > (Screen.DesktopLeft + Screen.DesktopWidth) then begin Result.Left := Screen.DesktopLeft + Screen.DesktopWidth - Width; Result.Right := Screen.DesktopLeft + Screen.DesktopWidth; end; if Result.Top < Screen.DesktopTop then begin Result.Top := Screen.DesktopTop; Result.Bottom := Screen.DesktopTop + Height; end; if Result.Bottom > (Screen.DesktopTop + Screen.DesktopHeight) then begin Result.Top := Screen.DesktopTop + Screen.DesktopHeight - Height; Result.Bottom := Screen.DesktopTop + Screen.DesktopHeight; end; end; function TPersistentForm.CheckPartVisible(Rect: TRect; Part: Integer): TRect; var Width: Integer; Height: Integer; begin Result := Rect; Width := Rect.Right - Rect.Left; Height := Rect.Bottom - Rect.Top; if Result.Right < (Screen.DesktopLeft + Part) then begin Result.Left := Screen.DesktopLeft + Part - Width; Result.Right := Screen.DesktopLeft + Part; end; if Result.Left > (Screen.DesktopLeft + Screen.DesktopWidth - Part) then begin Result.Left := Screen.DesktopLeft + Screen.DesktopWidth - Part; Result.Right := Screen.DesktopLeft + Screen.DesktopWidth - Part + Width; end; if Result.Bottom < (Screen.DesktopTop + Part) then begin Result.Top := Screen.DesktopTop + Part - Height; Result.Bottom := Screen.DesktopTop + Part; end; if Result.Top > (Screen.DesktopTop + Screen.DesktopHeight - Part) then begin Result.Top := Screen.DesktopTop + Screen.DesktopHeight - Part; Result.Bottom := Screen.DesktopTop + Screen.DesktopHeight - Part + Height; end; end; procedure TPersistentForm.Load(Form: TForm; DefaultMaximized: Boolean = False; DefaultFullScreen: Boolean = False); begin Self.Form := Form; // Set default FormRestoredSize := Bounds((Screen.Width - Form.Width) div 2, (Screen.Height - Form.Height) div 2, Form.Width, Form.Height); FormWindowState := Form.WindowState; FormFullScreen := DefaultFullScreen; LoadFromRegistry(RegistryContext); if (FormWindowState = wsMaximized) or DefaultMaximized then begin // Restore to maximized state Form.WindowState := wsNormal; if not EqualRect(FormRestoredSize, Form.BoundsRect) then Form.BoundsRect := FormRestoredSize; Form.WindowState := wsMaximized; end else begin // Restore to normal state Form.WindowState := wsNormal; if FEntireVisible then FormRestoredSize := CheckEntireVisible(FormRestoredSize) else if FMinVisiblePart > 0 then FormRestoredSize := CheckPartVisible(FormRestoredSize, FMinVisiblePart); if not EqualRect(FormRestoredSize, Form.BoundsRect) then Form.BoundsRect := FormRestoredSize; end; if FormFullScreen then SetFullScreen(True); LoadControl(Form); end; procedure TPersistentForm.Save(Form: TForm); begin Self.Form := Form; if not FormFullScreen then begin FormWindowState := Form.WindowState; if FormWindowState = wsMaximized then begin FormRestoredSize := Bounds(Form.RestoredLeft, Form.RestoredTop, Form.RestoredWidth, Form.RestoredHeight); end else if FormWindowState = wsNormal then begin FormRestoredSize := Bounds(Form.Left, Form.Top, Form.Width, Form.Height); end; end; SaveToRegistry(RegistryContext); SaveControl(Form); end; constructor TPersistentForm.Create(AOwner: TComponent); begin inherited; if AOwner is TForm then Form := TForm(AOwner) else Form := nil; FMinVisiblePart := 50; FRegistryContext.RootKey := HKEY_CURRENT_USER; end; procedure TPersistentForm.SetFullScreen(State: Boolean); {$IFDEF UNIX} var OldHandler: TNotifyEvent; var I: Integer; {$ENDIF} begin if State then begin FormFullScreen := True; if Form.WindowState = wsMaximized then begin FormRestoredSize := Bounds(Form.RestoredLeft, Form.RestoredTop, Form.RestoredWidth, Form.RestoredHeight); end else if Form.WindowState = wsNormal then begin FormRestoredSize := Bounds(Form.Left, Form.Top, Form.Width, Form.Height); end; FormWindowState := Form.WindowState; {$IFDEF WINDOWS} Form.BorderStyle := bsNone; {$ENDIF} Form.WindowState := wsFullscreen; {$IFDEF UNIX} // Workaround on Linux, WindowState is rewriten by WMSize event to wsNormal. // We need for that even to occure OldHandler := Form.OnWindowStateChange; Form.OnWindowStateChange := WindowStateChange; FResizeEventOccured := False; for I := 0 to 10 do begin if FResizeEventOccured then Break; Application.ProcessMessages; Sleep(1); end; Form.OnWindowStateChange := OldHandler; FormFullScreen := True; {$ENDIF} end else begin FormFullScreen := False; Form.WindowState := wsNormal; {$IFDEF WINDOWS} Form.BorderStyle := bsSizeable; {$ENDIF} if FormWindowState = wsNormal then begin Form.WindowState := wsNormal; Form.BoundsRect := FormRestoredSize; end else if FormWindowState = wsMaximized then begin Form.BoundsRect := FormRestoredSize; Form.WindowState := wsMaximized; end; end; end; procedure TPersistentForm.WindowStateChange(Sender: TObject); begin Form.WindowState := wsFullscreen; FResizeEventOccured := True; end; end.