From 9793627ce5c61f1e78c3f73293aca9fc701d2fb7 Mon Sep 17 00:00:00 2001 From: micha Date: Sat, 12 Nov 2005 23:02:05 +0000 Subject: [PATCH] implement float spinedit for win32 (issue #1386) git-svn-id: trunk@8139 - --- lcl/include/spinedit.inc | 51 +++++++-------- lcl/interfaces/win32/win32callback.inc | 82 ++++++++++++++++++++----- lcl/interfaces/win32/win32proc.pp | 12 ++++ lcl/interfaces/win32/win32wsspin.pp | 71 +++++++++++++-------- lcl/interfaces/win32/win32wsstdctrls.pp | 16 +---- lcl/spin.pp | 31 +++++----- 6 files changed, 168 insertions(+), 95 deletions(-) diff --git a/lcl/include/spinedit.inc b/lcl/include/spinedit.inc index e0fdc01d6c..68ca401d4e 100644 --- a/lcl/include/spinedit.inc +++ b/lcl/include/spinedit.inc @@ -24,11 +24,12 @@ begin if Value>fMaxValue then Value:=fMaxValue; if (not HandleAllocated) then exit; if ([csLoading,csDestroying]*ComponentState<>[]) then begin - fValueNeedsUpdate:=true; + FUpdatePending := true; exit; end; TWSCustomFloatSpinEditClass(WidgetSetClass).UpdateControl(Self); - fValueNeedsUpdate:=false; + FValueChanged := true; + FUpdatePending := false; end; function TCustomFloatSpinEdit.ValueIsStored: boolean; @@ -37,11 +38,14 @@ begin end; procedure TCustomFloatSpinEdit.TextChanged; +var + lPrevValue: single; begin - if Value=fLastValueOnChange then exit; - fLastValueOnChange:=Value; - Modified := True; - FValueEmpty:=false; + lPrevValue := FValue; + FValueChanged := true; + if Value = lPrevValue then exit; + Modified := true; + FValueEmpty := false; if HandleAllocated and (not (csLoading in ComponentState)) then Change; end; @@ -52,11 +56,6 @@ begin UpdateControl; end; -function TCustomFloatSpinEdit.ClimbRateIsStored: boolean; -begin - Result := FClimbRate <> 1; -end; - function TCustomFloatSpinEdit.GetModified: Boolean; begin Result := FModified; @@ -137,10 +136,10 @@ begin UpdateControl; end; -procedure TCustomFloatSpinEdit.SetClimbRate(const Num : Single); +procedure TCustomFloatSpinEdit.SetIncrement(const NewIncrement: single); begin - if fClimbRate = Num then exit; - fClimbRate := Num; + if NewIncrement = FIncrement then exit; + FIncrement := NewIncrement; UpdateControl; end; @@ -161,7 +160,7 @@ end; procedure TCustomFloatSpinEdit.Loaded; begin inherited Loaded; - if fValueNeedsUpdate then UpdateControl; + if FUpdatePending then UpdateControl; end; procedure TCustomFloatSpinEdit.Change; @@ -176,16 +175,17 @@ Procedure TCustomFloatSpinEdit.SetValue(const num : Single); begin if FValue = Num then exit; FValue := Num; - fLastValueOnChange := FValue; - fValueNeedsUpdate:=true; + FUpdatePending := true; UpdateControl; end; {-----------------------------------------------------------------------------} Function TCustomFloatSpinEdit.GetValue: Single; begin - if HandleAllocated and (not fValueNeedsUpdate) then begin + if HandleAllocated and FValueChanged then + begin FValue := TWSCustomFloatSpinEditClass(WidgetSetClass).GetValue(Self); + FValueChanged := false; end; Result := fValue; end; @@ -204,12 +204,13 @@ begin inherited Create(TheOwner); fCompStyle := csSpinEdit; - fClimbRate := 1; + FIncrement := 1; fDecimals := 2; fValue := 0; - fValueNeedsUpdate := true; fMinValue := 0; fMaxValue := 100; + FUpdatePending := true; + FValueChanged := true; SetInitialBounds(0,0,50,20); ParentColor := false; @@ -261,9 +262,9 @@ end; { TCustomSpinEdit } -function TCustomSpinEdit.GetClimbRate: integer; +function TCustomSpinEdit.GetIncrement: integer; begin - Result:=round(FClimbRate); + Result:=round(FIncrement); end; function TCustomSpinEdit.GetMaxValue: integer; @@ -281,10 +282,10 @@ begin Result:=round(inherited GetValue); end; -procedure TCustomSpinEdit.SetClimbRate(const AValue: integer); +procedure TCustomSpinEdit.SetIncrement(const AValue: integer); begin - if ClimbRate=AValue then exit; - inherited SetClimbRate(AValue); + if Increment = AValue then exit; + inherited SetIncrement(AValue); end; procedure TCustomSpinEdit.SetMaxValue(const AValue: integer); diff --git a/lcl/interfaces/win32/win32callback.inc b/lcl/interfaces/win32/win32callback.inc index 0b36656bf0..1b4bb5fec4 100644 --- a/lcl/interfaces/win32/win32callback.inc +++ b/lcl/interfaces/win32/win32callback.inc @@ -440,10 +440,11 @@ Var ScrollbarHandle := HWND(LParam); if ScrollbarHandle<>0 then lWinControl := GetWindowInfo(ScrollbarHandle)^.WinControl; - if lWinControl is TCustomTrackBar - then begin - LMessage.Msg := LM_CHANGED; - Exit; + if lWinControl <> nil then + begin + if lWinControl is TCustomTrackBar then + LMessage.Msg := LM_CHANGED; + exit; end; PLMsg:=@LMScroll; @@ -586,6 +587,42 @@ Var begin Result := WindowInfo^.isComboEdit and (ComboBoxHandleSizeWindow = Windows.GetParent(Window)); end; + + procedure HandleSpinEditChange(ASpinEdit: TCustomFloatSpinEdit); + var + lWindowInfo: PWindowInfo; + lNewValue: single; + begin + lWindowInfo := GetWindowInfo(ASpinEdit.Handle); + if lWindowInfo = @DefaultWindowInfo then exit; + lNewValue := StrToFloatDef(ASpinEdit.Text, lWindowInfo^.spinValue); + if lNewValue <> lWindowInfo^.spinValue then + begin + lWindowInfo^.spinValue := lNewValue; + LMessage.Msg := CM_TEXTCHANGED; + end; + end; + + procedure HandleSpinEditDeltaPos(AUpDownMsg: PNMUpDown); + var + lSpinEdit: TCustomFloatSpinEdit; + spinHandle: HWND; + newValue: single; + begin + lSpinEdit := TCustomFloatSpinEdit(WindowInfo^.WinControl); + newValue := WindowInfo^.spinValue + AUpDownMsg^.iDelta * lSpinEdit.Increment; + if (lSpinEdit.MinValue <> 0) or (lSpinEdit.MaxValue <> 0) then + begin + if newValue > lSpinEdit.MaxValue then + newValue := lSpinEdit.MaxValue; + if newValue < lSpinEdit.MinValue then + newValue := lSpinEdit.MinValue; + end; + spinHandle := AUpDownMsg^.hdr.hwndFrom; + UpdateFloatSpinEditText(spinHandle, NewValue, lSpinEdit.DecimalPlaces); + Windows.SendMessage(spinHandle, UDM_SETPOS32, 0, 500); + WindowInfo^.spinValue := newValue; + end; Begin Assert(False, 'Trace:WindowProc - Start'); @@ -749,18 +786,22 @@ Begin BN_CLICKED: LMessage.Msg := LM_CLICKED; BN_KILLFOCUS: LMessage.Msg := LM_EXIT; end - else if (lWinControl is TCustomEdit) or (lWinControl is TCustomFloatSpinEdit) then + else if (lWinControl is TCustomEdit) then case Hi(WParam) of - EN_CHANGE: LMessage.Msg := CM_TEXTCHANGED; + EN_CHANGE: LMessage.Msg := CM_TEXTCHANGED; + end + else if (lWinControl is TCustomFloatSpinEdit) then + case Hi(WParam) of + EN_CHANGE: HandleSpinEditChange(TCustomFloatSpinEdit(lWinControl)); end else if (lWinControl is TCustomMemo) then case Hi(WParam) of // multiline edit doesn't send EN_CHANGE, so use EN_UPDATE - EN_UPDATE: LMessage.Msg := CM_TEXTCHANGED; + EN_UPDATE: LMessage.Msg := CM_TEXTCHANGED; end else if (lWinControl is TCustomListBox) then case Hi(WParam) of - LBN_SELCHANGE: LMessage.Msg := LM_SELCHANGE; + LBN_SELCHANGE: LMessage.Msg := LM_SELCHANGE; end else if lWinControl is TCustomCombobox then case Hi(WParam) of @@ -1215,13 +1256,20 @@ Begin End; WM_NOTIFY: Begin - if PNMHdr(LParam)^.code=MCN_SELCHANGE then begin - LMessage.Msg := LM_CHANGED; - WindowInfo := GetWindowInfo(PNMHdr(LParam)^.hwndFrom); - if WindowInfo^.WinControl<>nil then - lWinControl := WindowInfo^.WinControl; - end - else begin + WindowInfo := GetWindowInfo(PNMHdr(LParam)^.hwndFrom); + case PNMHdr(LParam)^.code of + MCN_SELCHANGE: + begin + LMessage.Msg := LM_CHANGED; + if (WindowInfo <> nil) and (WindowInfo^.WinControl <> nil) then + lWinControl := WindowInfo^.WinControl; + end; + UDN_DELTAPOS: + begin + if WindowInfo <> nil then + HandleSpinEditDeltaPos(PNMUpDown(LParam)); + end; + else PLMsg:=@LMNotify; With LMNotify Do Begin @@ -1238,9 +1286,9 @@ Begin idFrom := Windows.SendMessage(HWndFrom, TCM_GETCURSEL, 0, 0); end; end; - End; + end; end; - End; + end; WM_PAINT: Begin SendPaintMessage; diff --git a/lcl/interfaces/win32/win32proc.pp b/lcl/interfaces/win32/win32proc.pp index 07f17ed638..2d6b9c9e83 100644 --- a/lcl/interfaces/win32/win32proc.pp +++ b/lcl/interfaces/win32/win32proc.pp @@ -54,6 +54,8 @@ Type DrawItemIndex: integer; // in case of listbox, when handling WM_DRAWITEM DrawItemSelected: boolean;// whether this item is selected LB_GETSEL not uptodate yet MouseX, MouseY: word; // noticing spurious WM_MOUSEMOVE messages + case integer of + 0: (spinValue: single); end; function WM_To_String(WM_Message: Integer): string; @@ -96,6 +98,7 @@ procedure EnableApplicationWindows(Window: HWND); procedure AddToChangedMenus(Window: HWnd); procedure RedrawMenus; function MeasureText(const AWinControl: TWinControl; Text: string; var Width, Height: integer): boolean; +function GetControlText(AHandle: HWND): string; type PDisableWindowsInfo = ^TDisableWindowsInfo; @@ -1081,6 +1084,15 @@ begin ReleaseDC(winHandle, canvasHandle); end; +function GetControlText(AHandle: HWND): string; +var + TextLen: dword; +begin + TextLen := GetWindowTextLength(AHandle); + SetLength(Result, TextLen); + GetWindowText(AHandle, PChar(Result), TextLen + 1); +end; + {$IFDEF ASSERT_IS_ON} {$UNDEF ASSERT_IS_ON} diff --git a/lcl/interfaces/win32/win32wsspin.pp b/lcl/interfaces/win32/win32wsspin.pp index b68bd6c400..d1b7860dbc 100644 --- a/lcl/interfaces/win32/win32wsspin.pp +++ b/lcl/interfaces/win32/win32wsspin.pp @@ -35,7 +35,7 @@ uses //////////////////////////////////////////////////// Spin, Controls, LCLType, //////////////////////////////////////////////////// - WSSpin, WSLCLClasses, Windows, Win32Int, WinExt, + WSSpin, WSLCLClasses, Windows, Win32Int, WinExt, Win32Proc, Win32WSStdCtrls, Win32WSControls; type @@ -52,6 +52,7 @@ type const AParams: TCreateParams): HWND; override; class function GetSelStart(const ACustomFloatSpinEdit: TCustomFloatSpinEdit): integer; override; class function GetSelLength(const ACustomFloatSpinEdit: TCustomFloatSpinEdit): integer; override; + class function GetText(const AWinControl: TWinControl; var AText: String): Boolean; override; class function GetValue(const ACustomFloatSpinEdit: TCustomFloatSpinEdit): single; override; class procedure SetSelStart(const ACustomFloatSpinEdit: TCustomFloatSpinEdit; NewStart: integer); override; @@ -70,25 +71,43 @@ type end; +procedure UpdateFloatSpinEditText(const ASpinHandle: HWND; const ANewValue: single; + const ADecimalPlaces: integer); + implementation +uses + SysUtils; + { TWin32WSCustomFloatSpinEdit } +function GetBuddyWindow(AHandle: HWND): HWND; +begin + Result := SendMessage(AHandle, UDM_GETBUDDY, 0, 0) +end; + procedure UpdateFloatSpinEditControl(const Handle: HWND; const AFloatSpinEdit: TCustomFloatSpinEdit); var - minval, maxval: integer; + lWindowInfo: PWindowInfo; begin - // initialize extremes - minval := Trunc(AFloatSpinEdit.MinValue); - maxval := Trunc(AFloatSpinEdit.MaxValue); - if (minval = 0) and (maxval = 0) then + lWindowInfo := GetWindowInfo(Handle); + if lWindowInfo <> @DefaultWindowInfo then begin - minval := low(integer); - maxval := high(integer); + lWindowInfo^.spinValue := AFloatSpinEdit.Value; + UpdateFloatSpinEditText(Handle, AFloatSpinEdit.Value, AFloatSpinEdit.DecimalPlaces); end; - SendMessage(Handle, UDM_SETRANGE32, minval, maxval); - SendMessage(Handle, UDM_SETPOS32, 0, LParam(Trunc(AFloatSpinEdit.Value))); +end; + +procedure UpdateFloatSpinEditText(const ASpinHandle: HWND; const ANewValue: single; + const ADecimalPlaces: integer); +var + editHandle: HWND; + newValueText: string; +begin + editHandle := GetBuddyWindow(ASpinHandle); + newValueText := FloatToStrF(ANewValue, ffFixed, 20, ADecimalPlaces); + Windows.SendMessage(editHandle, WM_SETTEXT, 0, Windows.LPARAM(PChar(newValueText))); end; function TWin32WSCustomFloatSpinEdit.CreateHandle(const AWinControl: TWinControl; @@ -102,15 +121,15 @@ begin with Params do begin Buddy := CreateWindowEx(WS_EX_CLIENTEDGE, 'EDIT', StrCaption, Flags Or ES_AUTOHSCROLL, Left, Top, Width, Height, Parent, HMENU(Nil), HInstance, Nil); - Window := CreateUpDownControl(Flags or DWORD(WS_BORDER or UDS_ALIGNRIGHT or UDS_NOTHOUSANDS or UDS_ARROWKEYS or UDS_WRAP or UDS_SETBUDDYINT), + Window := CreateUpDownControl(Flags or DWORD(WS_BORDER or UDS_ALIGNRIGHT or UDS_ARROWKEYS), 0, 0, // pos - ignored for buddy 0, 0, // size - ignored for buddy Parent, 0, HInstance, Buddy, - 0, 0, 0); - UpdateFloatSpinEditControl(Window, TCustomFloatSpinEdit(AWinControl)); + 1000, 0, 500); end; // create window FinishCreateWindow(AWinControl, Params, true); + UpdateFloatSpinEditControl(Params.Window, TCustomFloatSpinEdit(AWinControl)); // init buddy Params.SubClassWndProc := @WindowProc; WindowCreateInitBuddy(AWinControl, Params); @@ -135,35 +154,40 @@ end; function TWin32WSCustomFloatSpinEdit.GetSelStart( const ACustomFloatSpinEdit: TCustomFloatSpinEdit): integer; begin - Result := EditGetSelStart(SendMessage(ACustomFloatSpinEdit.Handle, - UDM_GETBUDDY, 0, 0)); + Result := EditGetSelStart(GetBuddyWindow(ACustomFloatSpinEdit.Handle)); end; function TWin32WSCustomFloatSpinEdit.GetSelLength( const ACustomFloatSpinEdit: TCustomFloatSpinEdit): integer; begin - Result := EditGetSelLength(SendMessage(ACustomFloatSpinEdit.Handle, - UDM_GETBUDDY, 0, 0)); + Result := EditGetSelLength(GetBuddyWindow(ACustomFloatSpinEdit.Handle)); +end; + +function TWin32WSCustomFloatSpinEdit.GetText(const AWinControl: TWinControl; + var AText: string): boolean; +begin + Result := AWinControl.HandleAllocated; + if not Result then + exit; + AText := GetControlText(GetBuddyWindow(AWinControl.Handle)); end; function TWin32WSCustomFloatSpinEdit.GetValue( const ACustomFloatSpinEdit: TCustomFloatSpinEdit): single; begin - Result := SendMessage(ACustomFloatSpinEdit.Handle, UDM_GETPOS32, 0, 0); + Result := GetWindowInfo(ACustomFloatSpinEdit.Handle)^.spinValue; end; procedure TWin32WSCustomFloatSpinEdit.SetSelStart( const ACustomFloatSpinEdit: TCustomFloatSpinEdit; NewStart: integer); begin - EditSetSelStart(SendMessage(ACustomFloatSpinEdit.Handle, UDM_GETBUDDY, 0, 0), - NewStart); + EditSetSelStart(GetBuddyWindow(ACustomFloatSpinEdit.Handle), NewStart); end; procedure TWin32WSCustomFloatSpinEdit.SetSelLength( const ACustomFloatSpinEdit: TCustomFloatSpinEdit; NewLength: integer); begin - EditSetSelLength(SendMessage(ACustomFloatSpinEdit.Handle, UDM_GETBUDDY, 0, 0), - NewLength); + EditSetSelLength(GetBuddyWindow(ACustomFloatSpinEdit.Handle), NewLength); end; procedure TWin32WSCustomFloatSpinEdit.ShowHide(const AWinControl: TWinControl); @@ -172,12 +196,11 @@ var begin // call inherited TWin32WSWinControl.ShowHide(AWinControl); - Buddy := SendMessage(AWinControl.Handle, UDM_GETBUDDY, 0, 0); + Buddy := GetBuddyWindow(AWinControl.Handle); if AWinControl.HandleObjectShouldBeVisible then ShowWindow(Buddy, SW_SHOW) else ShowWindow(Buddy, SW_HIDE); - end; procedure TWin32WSCustomFloatSpinEdit.UpdateControl( diff --git a/lcl/interfaces/win32/win32wsstdctrls.pp b/lcl/interfaces/win32/win32wsstdctrls.pp index 381554366e..da7e780014 100644 --- a/lcl/interfaces/win32/win32wsstdctrls.pp +++ b/lcl/interfaces/win32/win32wsstdctrls.pp @@ -667,17 +667,11 @@ begin end; function TWin32WSCustomComboBox.GetText(const AWinControl: TWinControl; var AText: string): boolean; -var - Handle: HWND; - TextLen: dword; begin Result := AWinControl.HandleAllocated; if not Result then exit; - Handle := AWinControl.Handle; - TextLen := GetWindowTextLength(Handle); - SetLength(AText, TextLen); - GetWindowText(Handle, PChar(AText), TextLen + 1); + AText := GetControlText(AWinControl.Handle); end; procedure TWin32WSCustomComboBox.SetArrowKeysTraverseList(const ACustomComboBox: TCustomComboBox; @@ -815,17 +809,11 @@ begin end; function TWin32WSCustomEdit.GetText(const AWinControl: TWinControl; var AText: string): boolean; -var - TextLen: dword; - Handle: HWND; begin Result := AWinControl.HandleAllocated; if not Result then exit; - Handle := AWinControl.Handle; - TextLen := GetWindowTextLength(Handle); - SetLength(AText, TextLen); - GetWindowText(Handle, PChar(AText), TextLen + 1); + AText := GetControlText(AWinControl.Handle); end; procedure TWin32WSCustomEdit.SetCharCase(const ACustomEdit: TCustomEdit; NewCase: TEditCharCase); diff --git a/lcl/spin.pp b/lcl/spin.pp index e929b4d78d..07219871ca 100644 --- a/lcl/spin.pp +++ b/lcl/spin.pp @@ -37,19 +37,18 @@ type TCustomFloatSpinEdit = class(TWinControl) private - fClimbRate: Single; - fDecimals: Integer; - fLastValueOnChange: single; + FIncrement: single; + FDecimals: integer; FMaxValue: single; FMinValue: single; FModified: boolean; FOnChange: TNotifyEvent; FSelLength: integer; FSelStart: integer; - fValue: Single; + FValue: Single; FValueEmpty: boolean; - fValueNeedsUpdate: boolean; - function ClimbRateIsStored: boolean; + FUpdatePending: boolean; + FValueChanged: boolean; function GetModified: Boolean; function GetSelLength: integer; function GetSelStart: integer; @@ -70,7 +69,7 @@ type procedure SetDecimals(Num: Integer); function GetValue: Single; procedure SetValue(const Num: Single); - procedure SetClimbRate(const Num: Single); + procedure SetIncrement(const NewIncrement: single); procedure InitializeWnd; override; procedure FinalizeWnd; override; procedure Loaded; override; @@ -91,7 +90,7 @@ type property Text; public property DecimalPlaces: Integer read FDecimals write SetDecimals default 2; - property ClimbRate: Single read FClimbRate write SetClimbRate default 1; + property Increment: Single read FIncrement write SetIncrement default 1; property MinValue: single read FMinValue write SetMinValue default 0; property MaxValue: single read FMaxValue write SetMaxValue default 100; property TabStop default true; @@ -109,6 +108,7 @@ type property Constraints; property DecimalPlaces; property Enabled; + property Increment; property MaxValue; property MinValue; property OnChange; @@ -137,21 +137,21 @@ type TCustomSpinEdit = class(TCustomFloatSpinEdit) private - function GetClimbRate: integer; + function GetIncrement: integer; function GetMaxValue: integer; function GetMinValue: integer; function GetValue: integer; - procedure SetClimbRate(const AValue: integer); + procedure SetIncrement(const AValue: integer); procedure SetMaxValue(const AValue: integer); procedure SetMinValue(const AValue: integer); procedure SetValue(const AValue: integer); public constructor Create(TheOwner: TComponent); override; public - property Value: integer read GetValue write SetValue; - property MinValue: integer read GetMinValue write SetMinValue; - property MaxValue: integer read GetMaxValue write SetMaxValue; - property ClimbRate: integer read GetClimbRate write SetClimbRate; + property Value: integer read GetValue write SetValue default 0; + property MinValue: integer read GetMinValue write SetMinValue default 0; + property MaxValue: integer read GetMaxValue write SetMaxValue default 100; + property Increment: integer read GetIncrement write SetIncrement default 1; end; @@ -162,9 +162,10 @@ type property Align; property Anchors; property BorderSpacing; - property ClimbRate; + property ClimbRate: integer write SetIncrement stored false; // TODO: remove, deprecated property Constraints; property Enabled; + property Increment; property MaxValue; property MinValue; property OnChange;