TEdit Cursor automatisch ändern

Rund um die LCL und andere Komponenten
Antworten
TBug
Beiträge: 177
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

TEdit Cursor automatisch ändern

Beitrag von TBug »

Erst einmal ein herzliches Hallo in dieses Forum.

Ich habe von TEdit eine Komponente abgeleitet in welcher unter anderem auch der Cursor automatisch geändert werden soll, wenn die Property ReadOnly gesetzt wurde.

Code: Alles auswählen

 
type
  TMyEdit = class(TCustomEdit)
   private
    FExtCursorReadOnly: TCursor;
   protected 
    procedure SetExtCursorReadOnly(AValue: TCursor);
    procedure LMSetCursor(var AMessage: TLMSetCursor); message LM_SETCURSOR;
   public
    constructor Create(AOwner: TComponent); override;
   published
    property ExtCursorReadOnly: TCursor read FExtCursorReadOnly write SetExtCursorReadOnly default crDefault;
  end;


dazu rufe ich die Message LM_SETCURSOR ab und reagiere darauf wie folgt:

Code: Alles auswählen

 
procedure TMyEdit.LMSetCursor(var AMessage: TLMSetCursor);
var
  lDoInherited: Boolean;
begin
  lDoInherited := true;
  if (not (csDesigning in ComponentState)) then
   begin
    if ((ReadOnly) and (FExtCursorReadOnly <> crDefault)) then
     begin
      LclIntf.SetCursor(Screen.Cursors[FExtCursorReadOnly]);
      AMessage.Result := 1;
      lDoInherited := false;
     end;
   end;
 
  if (lDoInherited) then
   begin
    inherited;
   end;
end;
 


Das läuft auch perfekt, solange die Property Cursor den Wert crDefault hat.

Ist in Cursor ein anderer Wert gespeichert, dann wird der "ReadOnlyCursor" beim Berühren der Umrandung des Editfelds angezeigt, aber sobald man mit der Maus in das Editfeld hineingelangt erscheint der Cursor, welcher unter der Property Cursor eingestellt ist.

Was übersehe ich da, oder gibt es eine andere Möglichkeit?

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TEdit Cursor automatisch ändern

Beitrag von Michl »

Willkommen im deutschen Lazarusforum!

Warum nutzt du denn nicht gleich das Property TEdit.Cursor, wenn sich die LCL sowieso schon um den Cursor kümmert?

à la:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.ReadOnly := not Edit1.ReadOnly;
  if Edit1.ReadOnly then
    Edit1.Cursor := crNone  //oder sonstiger Cursor
  else
    Edit1.Cursor := crDefault;
end;

Könntest du auch so in deinem eigenen TEdit verankern.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

TBug
Beiträge: 177
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: TEdit Cursor automatisch ändern

Beitrag von TBug »

Michl hat geschrieben:Willkommen im deutschen Lazarusforum!

Dankeschön!
Willkommen zu sein ist immer etwas schönes.

Michl hat geschrieben:Warum nutzt du denn nicht gleich das Property TEdit.Cursor, wenn sich die LCL sowieso schon um den Cursor kümmert?

Weil ich mir das Leben erleichter möchte.

Wenn ich eine zusätzliche Property habe, dann kann ich beide Cursor im ObjectInspector einstellen und der Code der Komponente kümmert sich automatisch darum, welcher angezeigt werden soll, ohne im Programm mich darum kümmern zu müssen.

Michl hat geschrieben:Könntest du auch so in deinem eigenen TEdit verankern.

Eher schlecht, dann dann würde die Property Cursor bei "ReadOnly = true" bei einer Abfrage im Programm ja nicht den Cursor zurückgeben, welcher im OI eigentlich eingestellt wurde, sondern den, welcher für "ReadOnly" gedacht war. Und ich wollte die ursprünglichen Propertys nicht verfälschen.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TEdit Cursor automatisch ändern

Beitrag von Michl »

Ich glaube, da musst du mit deiner Problemstellung etwas weiter ausholen. Bis jetzt verstehe ich nur Bahnhof.

Änderst du per Message den Cursor eines Controls, sollte das Control, bei einer Abfrage, dann den geänderten Cursor zurückgeben. Würde es das nicht, würde ich das einen Bug nennen.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

TBug
Beiträge: 177
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: TEdit Cursor automatisch ändern

Beitrag von TBug »

Michl hat geschrieben:Ich glaube, da musst du mit deiner Problemstellung etwas weiter ausholen. Bis jetzt verstehe ich nur Bahnhof.

Dann hole ich einmal ganz weit aus.

Jedes TEdit, oder auch andere Eingabe-Komponenten besitzt die Property Cursor und ReadOnly.

In der Property "Cursor" wird der Cursor hinterlegt, welcher angezeigt wird, wenn sich die Maus über der Komponente befindet und dabei die Komponente "Enabled" ist.

Nun habe ich die neue Property "CursorReadOnly". In dieser wird der Cursor hinterlegt, welcher angezeigt wird, wenn die Komponente "ReadOnly" und "Enabled" ist.

Die Messsage LM_SETCURSOR wird ausgelöst, wenn der Cursor gesetzt werden muss, also wenn sich die Maus vom Formular auf die Komponente bewegt. In dieser Nachricht soll nun der Cursor eben auf den Cursor gesetzt werden, welcher in der Property "CursorReadOnly" hinterlegt ist und nicht auf den Cursor, welcher in der Property "Cursor" steht.

In Delphi funktioniert dies hervorragend. Auch mit Lazarus, aber eben nur, wenn der Cursor in der Property "Cursor" auf "crDefault" steht.
Ich weiss eben nicht, wo da noch und an wen eine Nachricht gesendet wird, wenn sich die Maus von der Umrandung des Editfeldes in das Editfeld bewegt.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TEdit Cursor automatisch ändern

Beitrag von Michl »

Wozu das Ganze, habe ich zwar immer noch nicht verstanden, aber mal deinen Code in ein Minimalbsp eingefügt und getestet. Bei mir funktioniert er so, wie du ihn beschreibst (Windows7 64bit, Lazarus 1.4.2 und 1.5 jeweils 32).

Unter welchem System entwickelst du?

Anbei das Testprojekt.

EDIT:
TBug hat geschrieben:Auch mit Lazarus, aber eben nur, wenn der Cursor in der Property "Cursor" auf "crDefault" steht.
Das habe ich überlesen und sehe das jetzt auch...
Dateianhänge
MyEdit.zip
(3.38 KiB) 48-mal heruntergeladen

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

TBug
Beiträge: 177
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: TEdit Cursor automatisch ändern

Beitrag von TBug »

Michl hat geschrieben:Wozu das Ganze, habe ich zwar immer noch nicht verstanden,

Damit der Benutzer gleich sieht, wenn er mit der Maus darüber fahrt, dass dieses Feld ReadOnly ist, also keine Eingaben erlaubt sind.

Das Einpacken in die Komponente hat den Sinn, dass es erheblich Programmieraufwand spart.
Einfach nur "ReadOnly" wärend des Programmablauf setzen und alles andere macht die Komponente selbst.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TEdit Cursor automatisch ändern

Beitrag von Michl »

edit: gelöscht

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

wp_xyz
Beiträge: 4889
Registriert: Fr 8. Apr 2011, 09:01

Re: TEdit Cursor automatisch ändern

Beitrag von wp_xyz »

Ich hab Michls Projekt genommen und die Komponente mit der Standard-Property "Cursor" realisiert. Geht auch (Win 7); beide Lösungen funktionieren allerdings nicht unter Linux/qt.

Generell: Ist dir bewusst, dass trotz aktiviertem ReadOnly das TEdit-Control weiterhin noch für Mauseingaben zugänglich ist? Du kannst also durchaus noch den Text mit der Maus markieren,um ihn zum Beispiel in die Zwischenablage zu kopieren (was ich für sehr sinnvoll erachte). Da ist es natürlich wenig hilfreich, wenn plötzlich der Mauszeiger weg ist oder sich ins abschreckende "Verboten"-Icon meiner Demo verwandelt... Bin mir nicht sicher, ob du dir dein Projekt gut überlegt hast.
Dateianhänge
MyEdit-Cursor.zip
(2.12 KiB) 50-mal heruntergeladen
Zuletzt geändert von wp_xyz am Do 3. Sep 2015, 00:26, insgesamt 1-mal geändert.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TEdit Cursor automatisch ändern

Beitrag von Michl »

Jetzt war wp schneller :roll:

Habe jetzt aber auch noch eine Lösung, wie ich deine Anforderung verstanden habe (so in der Art, wie ich das im ersten Post gemeint hatte - ist aber ein ziemliches Gewurstel - Win7 getestet):

Code: Alles auswählen

  TMyEdit = class(TCustomEdit)
  private
    FExtCursorReadOnly: TCursor;
    FCurrentCursor: TCursor;
    function GetMyCursor: TCursor;
    procedure SetMyCursor(AValue: TCursor);
  protected
    procedure SetExtCursorReadOnly(AValue: TCursor);
    procedure SetReadOnly(Value: Boolean); override;
  published
    property ExtCursorReadOnly: TCursor read FExtCursorReadOnly write SetExtCursorReadOnly default crDefault;
    property Cursor: TCursor read GetMyCursor write SetMyCursor default crDefault;
  end;
 
...
 
function TMyEdit.GetMyCursor: TCursor;
begin
  Result := FCurrentCursor;
end;
 
procedure TMyEdit.SetMyCursor(AValue: TCursor);
begin
  FCurrentCursor := AValue;
  if ReadOnly then Exit;
  inherited SetCursor(AValue);
end;
 
procedure TMyEdit.SetReadOnly(Value: Boolean);
begin
  inherited SetReadOnly(Value);
  if Value then
    SetCursor(FExtCursorReadOnly)
  else
    Cursor := FCurrentCursor;
end;
 
procedure TMyEdit.SetExtCursorReadOnly(AValue: TCursor);
begin
  if FExtCursorReadOnly = AValue then Exit;
  FExtCursorReadOnly := AValue;
end;   
Dateianhänge
MyEditAlternativ.zip
(3.97 KiB) 60-mal heruntergeladen

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

TBug
Beiträge: 177
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.4 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: TEdit Cursor automatisch ändern

Beitrag von TBug »

Ersteinmal herzlichen Dank an Euch beide.

Die Lösung mit dem "verbiegen" der Propertys Cursor und ReadOnly hatte ich schon. Aber das finde ich nicht sehr elegant.
Vor allem ist es ein imenser zusätzlicher Aufwand, welchen ich bei der Portierung meiner Delphi-Komponenten nach Lazarus noch zusätzlich machen muss,

Ausserdem ist in Eueren Ausführungen ein kleiner Fehler. Wenn bereits die Property ReadOnly auf true gesetzt ist und dann der ExtCursorReadOnly gesetzt wird, dann wird Cursor nicht aktuallisiert.

So sah meine erste Lösung aus, nachdem ich merkte, dass die Benutzung der Messages durch irgendetwas nicht korrekt ausgelöst wird. Um das zu finden muss ich mich wohl erst noch weiter in die LCL einarbeiten und die Widgets einmal komplett verstehen.

Code: Alles auswählen

 
unit HGMemo;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, Controls, StdCtrls, LclType;
 
type
  THGMemo = class(TMemo)
   private
    FEnhCursor: TCursor;
    FExtCursorReadOnly: TCursor;
   protected
    function GetEnhCursor(): TCursor; virtual;
    function GetEnhReadOnly(): Boolean; virtual;
    procedure SetEnhCursor(AValue: TCursor); virtual;
    procedure SetEnhReadOnly(AValue: Boolean); virtual;
    procedure SetExtCursorReadOnly(AValue: TCursor); virtual;
    procedure UpdateInhCursor(); virtual;
   public
    constructor Create(AOwner: TComponent); override;
   published
    property Cursor: TCursor read GetEnhCursor write SetEnhCursor;
    property ExtCursorReadOnly: TCursor read FExtCursorReadOnly write SetExtCursorReadOnly default crDefault;
    property ReadOnly read GetEnhReadOnly write SetEnhReadOnly;
  end;
 
procedure Register;
 
implementation
 
constructor THGMemo.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEnhCursor := inherited Cursor;
  FExtCursorReadOnly := crDefault;
  UpdateInhCursor();
end;
 
function THGMemo.GetEnhCursor(): TCursor;
begin
  Result := FEnhCursor;
end;
 
function THGMemo.GetEnhReadOnly(): Boolean;
begin
  Result := inherited ReadOnly;
end;
 
procedure THGMemo.SetEnhCursor(AValue: TCursor);
begin
  if (FEnhCursor <> AValue) then
   begin
    FEnhCursor := AValue;
    UpdateInhCursor();
   end;
end;
 
procedure THGMemo.SetEnhReadOnly(AValue: Boolean);
begin
  if (inherited ReadOnly <> AValue) then
   begin
    inherited ReadOnly := AValue;
    UpdateInhCursor();
   end;
end;
 
procedure THGMemo.SetExtCursorReadOnly(AValue: TCursor);
begin
  if (FExtCursorReadOnly <> AValue) then
   begin
    FExtCursorReadOnly := AValue;
    UpdateInhCursor();
   end;
end;
 
procedure THGMemo.UpdateInhCursor();
begin
  if ((ReadOnly) and (FExtCursorReadOnly <> crDefault)) then
   begin
    inherited Cursor := FExtCursorReadOnly;
   end
  else
   begin
    inherited Cursor := FEnhCursor;
   end;
end;
 
procedure Register;
begin
  RegisterComponents('HG Edit', [THGMemo]);
end;
 
end.
 


wp_xyz hat geschrieben:Generell: Ist dir bewusst, dass trotz aktiviertem ReadOnly das TEdit-Control weiterhin noch für Mauseingaben zugänglich ist? Du kannst also durchaus noch den Text mit der Maus markieren,um ihn zum Beispiel in die Zwischenablage zu kopieren (was ich für sehr sinnvoll erachte). Da ist es natürlich wenig hilfreich, wenn plötzlich der Mauszeiger weg ist oder sich ins abschreckende "Verboten"-Icon meiner Demo verwandelt...


In meiner Delphi-Version dieser Komponenten habe ich dies so gelöst, dass beim Drücken der Maustasten der Cursor wieder umgeschalten wird. Es gibt dort zum Beispiel auch noch eine Property "CopyProtected" welche das Kopieren verhindert.

wp_xyz hat geschrieben:Bin mir nicht sicher, ob du dir dein Projekt gut überlegt hast.

Da gibt es nichts zu überlegen.

Wenn ich Lazarus verwenden möchte, dann muss ich wohl oder übel meine eigenen Delphi-Komponenten nach Lazarus portieren.

Das die Umstellung etwas Arbeit nach sich zieht war mir schon bewußt. Aber mit solchen Klimmzügen habe ich nicht gerechnet. Und dies schon bei den 0815-Komponenten. Wie soll es dann erst bei den Aufwändigeren sein, die mit echter Semi-Transparenz oder NC-Areas ausgerüstet sind.


Hat denn niemand eine Idee, weshalb/wo/von wen der Cursor wieder auf den Cursor gesetzt wird, welcher in der Property Cursor gepeichert ist?

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TEdit Cursor automatisch ändern

Beitrag von Michl »

TBug hat geschrieben:Wenn bereits die Property ReadOnly auf true gesetzt ist und dann der ExtCursorReadOnly gesetzt wird, dann wird Cursor nicht aktuallisiert.
Na gut, das habe ich nicht einbezogen, wäre aber bei meiner Lösung durch eine Zeile erledigt:

Code: Alles auswählen

procedure TMyEdit.SetExtCursorReadOnly(AValue: TCursor);
begin
  if FExtCursorReadOnly = AValue then Exit;
  FExtCursorReadOnly := AValue;
  if ReadOnly then SetCursor(FExtCursorReadOnly); //<--
end;     
Wobei ich noch weitere Bugs bzw. Ungereimtheiten nicht ausschließen würde.

TBug hat geschrieben:Hat denn niemand eine Idee, weshalb/wo/von wen der Cursor wieder auf den Cursor gesetzt wird, welcher in der Property Cursor gepeichert ist?
Ahnung habe ich davon nicht. Gespeichert wird es im Control selbst im FCursor, soweit ich das gestern sehen konnte.
Das ursprüngliche Problem dürfte unter Windows 32bit im TWindowProcHelper.HandleSetCursor (win32callback) zu suchen sein, da dort die Differenzierung zwischen ACursor = crDefault und ACursor <> crDefault gemacht wird. Einfach mal GetCursor ableiten und

Code: Alles auswählen

function TMyEdit.GetCursor: TCursor;
begin
  Result := inherited GetCursor;
end//<-- hier
einen Breakpoint setzen und schauen, wo der Cursor überall abgefragt wird.

Weiter habe ich da jetzt nicht geforscht und werde mich da jetzt auch ausklinken, da es dann wahrscheinlich zu noch mehr Gewurstel führt. Das Ganze dürfte noch viel schwieriger werden, wenn es plattformunabhängig werden soll.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Antworten