GTK3 Interface Workshop

Zur Vorstellung von Komponenten und Units für Lazarus
Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

af0815 hat geschrieben:
So 18. Dez 2022, 07:46
Wobei einmal der Focus auf CreateCaret und DestroyCaret liegt. Weil für den Rest muss ich begreifen wie ich da auf die Verwaltungsstrukturen zugreife. Besonders die Zurordnung HWND zu Gtk3Widget.
Sehr gut. Bin gespannt.
Eine Frage ist ja auch, ob man das Caret irgendwie direkt auf das mit HWND spezifizierte Objekt zeichnet, oder ob Caret ein eigenes Widget oder Window sein soll, welches man nur verschiebt und ein-/ausblendet.
In jedem Fall glaube ich, dass es nur ein einziges, systemweites Caret geben muss. Man könnte das Caret Objekt/Handle in einer globalen Variable halten. Man muss nur ggf. feststellen oder speichern, welches Control/HWND das Caret gerade besitzt.
Funktionen wie GetCaretPos geben ja weder ein Window- noch eine Caret- Handle mit.
Siehe https://learn.microsoft.com/en-us/windo ... reatecaret
Diese Funktion wollen wir ja nachbilden.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: GTK3 Interface Workshop

Beitrag von af0815 »

theo hat geschrieben:
So 18. Dez 2022, 08:49
...
In jedem Fall glaube ich, dass es nur ein einziges Caret geben muss. Man könnte das Caret Objekt/Handle in einer globalen Variable halten.
Siehe https://learn.microsoft.com/en-us/windo ... reatecaret
Diese Funktion wollen wir ja nachbilden.
Schau mal in die Sourcen von QT5 hinein, qtcaret.pas. Da braucht man nicht so sehr in die WinWelt blicken. :D Ich glaube wenn es geht sollte man sich am funktionierenden QT5 und kommenden QT6 orientieren.

BTW: Ich habe mit dem aktuellen main von fpc und Lazarus ein Problem mit den CodeTools. Der findet einen vom fpc neu verwendeten Modeswitch nicht und behauptet deswegen, das der Code nicht gültig ist. Jetzt musst ich den fpc mal auf fixes downgraden.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

af0815 hat geschrieben:
So 18. Dez 2022, 09:08
Schau mal in die Sourcen von QT5 hinein, qtcaret.pas. Da braucht man nicht so sehr in die WinWelt blicken. :D Ich glaube wenn es geht sollte man sich am funktionierenden QT5 und kommenden QT6 orientieren.
Das sehe ich nicht ganz so.
CreateCaret ist nun mal ein Windows API Funktion und was die tut oder tun soll, steht bei MS.
Wenn man die Funktion nachbilden will, sind das wichtige Infos.
Natürlich kann man bei Qt-LCL reinschielen wie es dort gelöst wurde, aber beschreiben ist das Ganze bei der WinAPI.
Z.B.
The nWidth and nHeight parameters specify the caret's width and height, in logical units; the exact width and height, in pixels, depend on the window's mapping mode.

CreateCaret automatically destroys the previous caret shape, if any, regardless of the window that owns the caret. The caret is hidden until the application calls the ShowCaret function to make the caret visible.

The system provides one caret per queue. A window should create a caret only when it has the keyboard focus or is active. The window should destroy the caret before losing the keyboard focus or becoming inactive.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: GTK3 Interface Workshop

Beitrag von af0815 »

Ich weis jetzt einmal, das es unter Windows aktuell sinnlos ist, ich habe gtk3 unter Windows am Rechner (auch gtk2), das wird zwar erzeugt, aber dann bemängelt er, das es keine libgtk3.so.0 am Rechner gibt. Daher weis ich, das es nicht unter Windows gehen wird, weil nichts dafür hergerichtet wurde.

-----

Was ich jetzt gerade versuche heraus zu bekommen ist, wie bzw. wo ist der Übergang zwischen Synedit und dem Widgetset. Weil vieles ist ja in Synedit enthalten, warum bzw. wo wird da auf das jeweilige Widgetset gegangen, mir fehlt da noch der LInk. Ich muss mir mal das Puzzle zusammensetzen und probieren zu verstehen.

Mir ist schon klar, das die LCL aus der VCL Welt, und die ist mal WIndows only gewesen, hervorgegangen ist. Dafür ist die LCL dann angepasst worden, das auch verschiedene Widgetsets (mehr oder weniger gut) unterstützt werden. Nachdem QT5 btw. QT6 herausgeschmissen werden, orientiere ich mich auch dort. Gerade weil einer der Hauptentwickler die Basis für alle 3 gelegt hat und der auf der jeweiligen Plattform das nicht neu entwickeln wird, sondern die Schnittstellen relativ gleich halten wird. Deswegen schau ich mir die Sachen dort auch an und HWND ist ein typischer Ausdruck aus der Windowswelt. Und mit der WinAPI habe ich schon einiges in der Vergangenheit ausgefochten (in Delphi 3-7).

Die Caretbehandlung unter QT5 sieht sehr and die WinAPI angelehnt an, das einzige ist, dort wird dann der Kontext hergerichtet für die direkten Zeichenroutinen aus der API von QT.

Ich bin am Studieren und die Infos mal auf die Reihe zu bringen :-)
Zuletzt geändert von af0815 am So 18. Dez 2022, 10:14, insgesamt 1-mal geändert.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

af0815 hat geschrieben:
So 18. Dez 2022, 10:01
Ich weis jetzt einmal, das es unter Windows aktuell sinnlos ist, ich habe gtk3 unter Windows am Rechner (auch gtk2), das wird zwar erzeugt, aber dann bemängelt er, das es keine libgtk3.so.0 am Rechner gibt. Daher weis ich, das es nicht unter Windows gehen wird, weil nichts dafür hergerichtet wurde.
Unter Windows muss das mMn auch nicht laufen, darauf würde ich (momentan) keine Zeit verschwenden.
Du hast doch eine Linux Installation, oder?

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: GTK3 Interface Workshop

Beitrag von af0815 »

theo hat geschrieben:
So 18. Dez 2022, 10:08
Unter Windows muss das mMn auch nicht laufen, darauf würde ich (momentan) keine Zeit verschwenden.
Du hast doch eine Linux Installation, oder?
Ja, sicher erst auf Debian Bullseye aufgesetzt in VMs auf verschiedenen Rechnern. Das ist nicht das Problem. Ich habe es nur mal kurz versucht, weil ich wegen XTensa den msys64 sowieso auf dem einen Rechner habe und dort kurz mal gtk3 eingespielt habe. Die Rechner sind entsprechend mit CPU (4 phys Core min und 16GB RAM min) ausgestattet, so das die VM auch fast nativ schnell ist.

Der Grund ist auch, unter Windows habe ich eine professionelle Umgebung mit allen Tools und das ist meine tägliche Arbeitsplattform. Linux ist eher zweite Wahl und für mich nicht so gut ausgestattet und auch weniger Erfahrung, was die Tools angeht. Unter Lazarus/FPC ist das kein Problem, aber wenn es tiefer geht, so werden halt die Defizite für mich spürbarer.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

Habe eben noch etwas herausgefunden.
Die Idee von Warf wurde tatsächlich für MUI umgesetzt.
Mach mal in synedit.inc als Hack:

Code: Alles auswählen

{$IFdef LCLGTK3}
 {$DEFINE LCLMui}
{$ENDIF}      
Dann blinkt da ein Caret dank "TSynEditScreenCaretPainterInternal".
Ich kann es zwar nicht mit den Pfeiltasten verschieben, aber es kommt beim Tippen mit und reagiert auf die Maus-Positionierung.

Das löst unser Problem nicht, aber ich dachte, man sollte es wissen.

Verändert das unsere Prioritäten?

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: GTK3 Interface Workshop

Beitrag von af0815 »

Nö, es geht ja einmal um das zusammentragen an Infos.

Ich bekomme auch langsam den Link: syneditpointclasses.pas

Code: Alles auswählen

function TSynEditScreenCaretPainterSystem.CreateCaret(w, h: Integer): Boolean;
begin
  ...
  Result := LCLIntf.CreateCaret(Handle, 0, w, h);
end;
dort wird in der winapi.inc das widgetset aufgerufen

Code: Alles auswählen

function CreateCaret(Handle : HWND; Bitmap : hBitmap; width, Height : Integer) : Boolean;
begin
  Result := WidgetSet.CreateCaret(Handle, Bitmap, width, Height);
end;
und damit landen wir in der

Code: Alles auswählen

function TGtk3WidgetSet.CreateCaret(Handle: HWND; Bitmap: hBitmap; width,
  Height: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CreateCaret not implemented ...');
  {$ENDIF}
  Result := inherited CreateCaret(Handle, Bitmap, width, Height);
  //Result:= CreateCaret(TGtk3Widget(Handle), Bitmap, width, Height);
end;
Ok langsam wird es klarer und auch warum der Vorwurf auf der Mailingliste ist, das da über Umwege die winAPI emuliert wird und noch ein Layer zusätzlich aufgeladen wird.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: GTK3 Interface Workshop

Beitrag von PascalDragon »

Warf hat geschrieben:
Sa 17. Dez 2022, 21:50
Wundert mich aber dann, da das anscheinend auch bei gtk2 und qt der Fall ist, das man das dann nicht als option im TSynEdit package einbaut statt immer die selbe lösung pro widgetset zu bauen
Aber grundsätzlich sollte das dann recht einfach sein da es die ganzen zeichenprimitiven schon gibt
Weil es auch andere Komponenten geben kann, die einen Caret brauchen können. Ist dies im WidgetSet implementiert, so heißt das, dass dies nicht für jede Komponente wieder und wieder implementiert werden muss.
FPC Compiler Entwickler

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: GTK3 Interface Workshop

Beitrag von Warf »

af0815 hat geschrieben:
So 18. Dez 2022, 07:46
Besonders die Zurordnung HWND zu Gtk3Widget.
Also von dem was ich gesehen hab ist das Handle lediglich das PGTKXXX (also der gtk struct pointer, nicht die TGTK3XXX widget klasse, sieht man an der fehlenden 3)

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

Warf hat geschrieben:
So 18. Dez 2022, 16:11
Also von dem was ich gesehen hab ist das Handle lediglich das PGTKXXX (also der gtk struct pointer, nicht die TGTK3XXX widget klasse, sieht man an der fehlenden 3)
Was meinst du genau?
Wenn ich in gtk3winapi.inc:

Code: Alles auswählen

function TGtk3WidgetSet.CreateCaret(Handle: HWND; Bitmap: hBitmap; width,
  Height: Integer): Boolean;  
dieses mache

Code: Alles auswählen

DebugLn(TObject(Handle).ClassName);  
dann bekomme ich "TGtk3CustomControl" vom Synedit im Debug Output.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: GTK3 Interface Workshop

Beitrag von af0815 »

Ich probiere mit folgenden herum (ist teilweise von QT 5 geklaut)

winapi.h

Code: Alles auswählen

function TGtk3WidgetSet.CreateCaret(Handle: HWND; Bitmap: hBitmap; width,
  Height: Integer): Boolean;
var
  Widget: TGtk3Widget;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CreateCaret not implemented ...');
  {$ENDIF}
  //Result := inherited CreateCaret(Handle, Bitmap, width, Height);
  Widget := TGtk3Widget(Handle);
  //if widget <> nil then
  Result:= gtk3Caret.CreateCaret(Widget, Bitmap, width, Height);
end;

LazGtk3

Code: Alles auswählen

type

  PGtkPoint = ^TGtkPoint;
  TGtkPoint = packed record
    x : LongInt;
    y : LongInt;
  end;


  GtkPaintDeviceH = class(TObject) end;
  GtkPixmapH = class(GtkPaintDeviceH) end;

  function GtkPoint(X,Y:integer): TGtkPoint;
  
  ..
  
 implementation

function GtkPoint(X,Y:integer): TGtkPoint;
begin
  Result.x:=X;
  Result.y:=Y;
end;

 

Code: Alles auswählen

{
 /***************************************************************************
                        GtkCaret.pas  -  Gtk Caret Emulation
                       -------------------------------------

 copyright (c)

 adopted for Lazarus and gtk3 by Lazarus German Forum

 ***************************************************************************/

 *****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************
}
unit gtk3Caret;

{$mode ObjFPC}{$H+}

interface

{$DEFINE VerboseGtkCaret}

{$I gtk3defines.inc}

uses
  // bindings
  LazGtk3, LazGdk3,
  // Free Pascal
  Classes, SysUtils, Types,
  // Widgetset
  Gtk3Widgets,
  // LCL
  LCLType, LCLIntf, Graphics, ExtCtrls;

type

  { TEmulatedCaret }

  TEmulatedCaret = class(TComponent)
  private
    FCaretDirty: Boolean;
    FCaretDirtyPos: TGtkPoint;  // TQtPoint { TODO -oANdi : Gtk3 ? }
    FTimer: TTimer;
    FOldRect: TRect;
    FWidget: TGtkWidget;
    FPixmap: GtkPixmapH;
    FLastValidWidth, FLastValidHeight: Integer;
    FWidth, FHeight: Integer;
    FPos: TGtkPoint;    // TQtPoint { TODO -oANdi : Gtk3 ? }
    FVisible: Boolean;
    FVisibleState: Boolean;
    FRespondToFocus: Boolean;
    FCritSect: TCriticalSection;
    procedure SetPos(const Value: TGtkPoint); // TQtPoint { TODO -oANdi : Gtk3 ? }
  protected
    procedure DoTimer(Sender: TObject);
    procedure DrawCaret; virtual;
    function CreateColorPixmap(Color: PtrUInt): GtkPixmapH;
    procedure SetWidget(AWidget: TGtk3Widget);
    procedure UpdateCaret(const AForceUpdate: Boolean = False);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure Lock;
    procedure Unlock;

    function CreateCaret(AWidget: TGtk3Widget; Pixmap: GtkPixmapH; Width, Height: Integer): Boolean;
    function DestroyCaret: Boolean;

    function IsValid: Boolean;

    function Show(AWidget: TGtk3Widget): Boolean;
    function Hide: Boolean;

    property CaretDirty: Boolean read FCaretDirty;
    property Timer: TTimer read FTimer;
    property Pos: TGtkPoint read FPos write SetPos;
    property RespondToFocus: Boolean read FRespondToFocus write FRespondToFocus;
  end;

function CreateCaret(Widget: TGtk3Widget; Pixmap: GtkPixmapH; Width, Height: Integer): Boolean; overload;
function CreateCaret(Widget: TGtk3Widget; ColorCaret: PtrUInt; Width, Height: Integer): Boolean; overload;
function HideCaret(Widget: TGtk3Widget): Boolean;
function ShowCaret(Widget: TGtk3Widget): Boolean;
function SetCaretPos(X, Y: Integer): Boolean;
function GetCaretPos(var Pt: TPoint): Boolean;
function GetGtkCaretRespondToFocus: Boolean;
procedure SetGtkCaretRespondToFocus(Value: Boolean);
function DestroyCaret: Boolean;
procedure DrawCaret;
procedure DestroyGlobalCaret;
function GlobalCaretDirty: Boolean;


implementation
uses
  gtk3int
  {$IFDEF VerboseGtkCaret}
  ,LCLProc
  {$ENDIF}
  ;
var
  GlobalCaret: TEmulatedCaret = nil;

procedure GlobalCaretNeeded;
begin
  if GlobalCaret = nil then
    GlobalCaret := TEmulatedCaret.Create(nil);
end;

function GlobalCaretDirty: Boolean;
begin
  Result := False;
  if GlobalCaret <> nil then
    Result := GlobalCaret.CaretDirty;
end;

procedure DrawCaret;
begin
  GlobalCaretNeeded;
  if Assigned(GlobalCaret) then
  begin
    GlobalCaret.Lock;
    try
      GlobalCaret.DrawCaret;
    finally
      GlobalCaret.Unlock;
    end;
  end;
end;

procedure DestroyGlobalCaret;
begin
  FreeAndNil(GlobalCaret);
end;

function CreateCaret(Widget: TGtk3Widget; Pixmap: GtkPixmapH; Width, Height: Integer): Boolean;
begin
  GlobalCaretNeeded;
  GlobalCaret.Lock;
  try
    Result := GlobalCaret.CreateCaret(Widget, Pixmap, Width, Height);
  finally
    GlobalCaret.Unlock;
  end;
end;

function CreateCaret(Widget: TGtk3Widget; ColorCaret: PtrUInt; Width, Height: Integer): Boolean;
begin
  Result := CreateCaret(Widget, GtkPixmapH(ColorCaret), Width, Height);
end;

function GetCaretBlinkTime: Cardinal;
var
  FlashTime: Integer;
begin
  FlashTime := 600; { TODO -oAndi : What Flashtime ? } // GtkApplication_cursorFlashTime;
  if FlashTime > 0 then
    Result := FlashTime div 2
  else
    Result := 600; // our default value
end;

function SetCaretBlinkTime(uMSeconds: Cardinal): LongBool;
begin
  Result := True;
  try
    // GtkApplication_setCursorFlashTime(uMSeconds);  { TODO -oAndi : CursorFlashTime ? }
    if assigned(GlobalCaret) then
    begin
      GlobalCaret.Lock;
      try
        GlobalCaret.Timer.Interval := GetCaretBlinkTime;
      finally
        GlobalCaret.Unlock;
      end;
    end;
  except
    Result := False;
  end;
end;

function HideCaret(Widget: TGtk3Widget): Boolean;
begin
  GlobalCaretNeeded;
  if Assigned(GlobalCaret) then
  begin
    GlobalCaret.Lock;
    try
      Result := GlobalCaret.Hide;
    finally
      GlobalCaret.Unlock;
    end;
  end
  else
    Result := false;
end;

function ShowCaret(Widget: TGtk3Widget): Boolean;
begin
  GlobalCaretNeeded;
  GlobalCaret.Lock;
  try
    Result := GlobalCaret.Show(Widget);
  finally
    GlobalCaret.Unlock;
  end;
end;

function SetCaretPos(X, Y: Integer): Boolean;
begin
  Result := True;
  GlobalCaretNeeded;
  GlobalCaret.Lock;
  try
    GlobalCaret.Pos := GtkPoint(X, Y);
  finally
    GlobalCaret.Unlock;
  end;
end;

function GetCaretPos(var Pt: TPoint): Boolean;
begin
  Result := True;
  GlobalCaretNeeded;
  GlobalCaret.Lock;
  try
    with GlobalCaret.Pos do
    begin
      Pt.x := X;
      Pt.y := Y;
    end;
  finally
    GlobalCaret.Unlock;
  end;
end;

function GetQtCaretRespondToFocus: Boolean;
begin
  if Assigned(GlobalCaret) then
    Result := GlobalCaret.RespondToFocus
  else
    Result := False;
end;

procedure SetQtCaretRespondToFocus(Value: Boolean);
begin
  if Assigned(GlobalCaret) then
    GlobalCaret.RespondToFocus := Value;
end;

function DestroyCaret: Boolean;
begin
  if Assigned(GlobalCaret) then
  begin
    GlobalCaret.Lock;
    try
      Result := GlobalCaret.DestroyCaret;
    finally
      GlobalCaret.Unlock;
    end;
  end
  else
    Result := False;
end;

{ TEmulatedCaret }

constructor TEmulatedCaret.Create(AOwner: TComponent);
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.Create(AsUTF8String)');
  {$ENDIF}
  inherited Create(AOwner);
  InitializeCriticalSection(FCritSect);

  FLastValidWidth := 0;
  FLastValidHeight := 0;
  FCaretDirty := False;
  FOldRect := Rect(0, 0, 1, 1);
  FPos := GtkPoint(0, 0);
  FCaretDirtyPos := FPos;

  FTimer := TTimer.Create(self);
  FTimer.Enabled := False;
  FTimer.Interval := GetCaretBlinkTime;
  FTimer.OnTimer := @DoTimer;

  FRespondToFocus := False;
end;

destructor TEmulatedCaret.Destroy;
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.Destroy()');
  {$ENDIF}
  DestroyCaret;
  DeleteCriticalSection(FCritSect);
  inherited Destroy;
end;

function TEmulatedCaret.CreateCaret(AWidget: TGtk3Widget; Pixmap: GtkPixmapH;
  Width, Height: Integer): Boolean;
begin
  DestroyCaret;
  SetWidget(AWidget);
  FWidth := Width;
  FHeight := Height;
  if FWidth > 0 then
    FLastValidWidth := FWidth;
  if FHeight > 0 then
    FLastValidHeight := FHeight;

  if PtrUInt(Pixmap) > $FFFF then
    FPixmap := GtkPixmap_create(Pixmap)
  else
    FPixmap := CreateColorPixmap(PtrUInt(Pixmap));

  {$IFDEF VerboseGtkCaret}
  writeln('TEmulatedCaret.CreateCaret IsValid=',IsValid,' FVis=',FVisible,
    ' FVisState=',FVisibleState,' FPixmap=',dbghex(PtrUInt(FPixmap)),
    ' FWidth=',FWidth,' FHeight=',FHeight{,' FWidget=',dbghex(PtrUInt(FWidget))});
  {$ENDIF}

  Result := Assigned(FPixmap);
  FTimer.Enabled := True;
end;

function TEmulatedCaret.DestroyCaret: Boolean;
begin
  {$IFDEF VerboseGtkCaret}
  writeln('TEmulatedCaret.DestroyCaret IsValid=',IsValid,' FVis=',FVisible,
    ' FVisState=',FVisibleState,' FPixmap=',dbghex(PtrUInt(FPixmap)),
    ' FWidth=',FWidth,' FHeight=',FHeight{,' FWidget=',dbghex(PtrUInt(FWidget))});
  {$ENDIF}
  FTimer.Enabled := False;
  Hide;
  if Assigned(FPixmap) then
    GtkPixmap_destroy(FPixmap);
  FWidget := nil;
  FPixmap := nil;
  FWidth := 0;
  FHeight := 0;
  Result := not IsValid;
end;

procedure TEmulatedCaret.DrawCaret;
var
  R: TRect;
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.DrawCaret IsValid=',IsValid,' FVis=',FVisible,
    ' FVisState=',FVisibleState,' FPixmap=',dbghex(PtrUInt(FPixmap)),
    ' FWidth=',FWidth,' FHeight=',FHeight,' FWidget=',dbghex(PtrUInt(FWidget)));
  {$ENDIF}
  if IsValid and FVisible and FVisibleState then
  begin
    FCaretDirty := False;
    R := Rect(0, 0, QPixmap_width(FPixmap), QPixmap_height(FPixmap));
    TQtDeviceContext(FWidget.Context).save;
    TQtDeviceContext(FWidget.Context).setCompositionMode(QPainterRasterOp_NotSourceXorDestination);
    TQtDeviceContext(FWidget.Context).drawPixmap(PGtkPoint(@FPos), FPixmap, PRect(@R));
    TQtDeviceContext(FWidget.Context).restore;
  end;
end;

function TEmulatedCaret.Show(AWidget: TGtk3Widget): Boolean;
var
  Pt: TGtkPoint;
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.Show AWidget=',dbghex(PtrUInt(AWidget)));
  {$ENDIF}
  if FWidget <> AWidget then
  begin
    {$IFDEF VerboseQtCaret}
    writeln('TEmulatedCaret.Show Widget changed IsValid=',IsValid);
    {$ENDIF}
    Hide;
    SetWidget(AWidget);
    if (FCaretDirty or (FPixmap=nil))
    and (AWidget <> nil) and (FWidget.Context<>0) then
    begin
      CreateCaret(FWidget, nil, FLastValidWidth, FLastValidHeight);
      if (FCaretDirtyPos.X > 0) or (FCaretDirtyPos.Y > 0) then
      begin
        if (FWidget.LastCaretPos.X >= 0) and (FWidget.LastCaretPos.Y >= 0) then
          Pt := FWidget.LastCaretPos
        else
          Pt := FPos;
        SetPos(FCaretDirtyPos);
        FCaretDirtyPos := GtkPoint(0, 0);
        QCoreApplication_processEvents(QEventLoopExcludeUserInpAsUTF8StringutEvents);
        SetPos(Pt);
      end else
        SetPos(FPos);
    end;
  end;
  Result := Assigned(FPixmap);
  FVisible := Result;
  SetPos(FPos);
  //writeln('TEmulatedCaret.Show END Result=',Result,' IsValid=',IsValid,' Handle=',QtWidgetSet.IsValidHandle(HWND(FWidget)),' FPixmap=',FPixmap<>nil,' FWidget.Context=',FWidget.Context<>0);
end;

function TEmulatedCaret.Hide: Boolean;
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.Hide IsValid=',IsValid,' FVisible=',FVisible);
  {$ENDIF}
  Result := IsValid;
  if Result and FVisible then
  begin
    FVisible := False;
    UpdateCaret;
  end;
end;

procedure TEmulatedCaret.SetPos(const Value: TGtkPoint);
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.SetPos FWidget=',dbghex(PtrUInt(FWidget)),' X=',Value.X,
  ' Y=',Value.Y,' OldX=',FPos.X,' OldY=',FPos.Y,' Dirty=',FCaretDirty);
  {$ENDIF}

  if not QtWidgetSet.IsValidHandle(HWND(FWidget)) or (FWidget.Widget = nil) then
  begin
    // oops, our caret is dirty here.
    FCaretDirtyPos := FPos;
    FCaretDirty := True;
    FPos := Value;
    exit;
  end;

  if ((FPos.x <> Value.x) or (FPos.y <> Value.y)) or FCaretDirty then
  begin
    FWidget.LastCaretPos := FPos;
    FPos := Value;
    FTimer.Enabled := False;
    FVisibleState := FWidget.Context = 0;
    {$note remove complete property RespondToFocus after testing}
    // if RespondToFocus and not FCaretDirty then
    if not FCaretDirty then
      UpdateCaret(True);
    if FCaretDirty then
    begin
      FVisible := True;
      if FWidget.Context <> 0 then
        DoTimer(FTimer);
      FTimer.Enabled := True;
    end;
  end else
  begin
    if FWidget.Context = 0 then
    begin
      FVisibleState := True;
      FTimer.Enabled := False;
      UpdateCaret;
    end else
    begin
      if not FTimer.Enabled then
        FTimer.Enabled := True;
    end;
  end;
end;

procedure TEmulatedCaret.DoTimer(Sender: TObject);
begin
  FVisibleState := not FVisibleState;
  if FVisible then
    UpdateCaret;
end;

procedure TEmulatedCaret.Lock;
begin
  {$note TEmulatedCaret.Lock()
    remove this routine later, after
   testing on all platforms}
  // EnterCriticalSection(FCritSect);
end;

procedure TEmulatedCaret.Unlock;
begin
  {$note TEmulatedCaret.UnLock()
   remove this routine later, after
   testing on all platforms}
  // LeaveCriticalSection(FCritSect);
end;

function TEmulatedCaret.CreateColorPixmap(Color: PtrUInt): GtkPixmapH;
var
  QC: TGtkColor;
  AColor: TColor;
begin
  {$IFDEF VerboseQtCaret}
  writeln('TEmulatedCaret.CreateColorPixmap FWidget=',dbghex(PtrUInt(FWidget)),' Width=',FWidth,
    ' FHeight=',FHeight);
  {$ENDIF}
  if (FWidth <= 0) or (FHeight <= 0) then
    Result := nil
  else
  begin
    case Color of
      0: AColor := clBlack;
      1: AColor := clGray;
    else
    begin
      Result := nil;
      Exit;
    end;
    end;
    Result := QPixmap_create(FWidth, FHeight);
    try
      QColor_fromRgb(@QC,Red(AColor),Green(AColor),Blue(AColor));
      QPixmap_fill(Result, @QC);
    except
      QPixmap_destroy(Result);
      Result := nil;
    end;
  end;
end;

function TEmulatedCaret.IsValid: Boolean;
begin
  Result := QtWidgetSet.IsValidHandle(HWND(FWidget));
  if Result then
    Result := (FPixmap <> nil) and (FWidget.Context <> 0);
end;

procedure TEmulatedCaret.SetWidget(AWidget: TGtk3Widget);
begin
  if FWidget <> nil then
    FWidget.HasCaret := False;

  if AWidget is TQtCustomControl then
    AWidget := TQtCustomControl(AWidget).viewport;

  FWidget := AWidget;
  if FWidget <> nil then
    FWidget.HasCaret := True;
end;

procedure TEmulatedCaret.UpdateCaret(const AForceUpdate: Boolean = False);
var
  R: TRect;
begin
  if not Gtk3WidgetSet.IsValidHandle(HWND(FWidget)) then
    exit;
  if (FWidget.Widget <> nil) then
  begin
    if FPos.X < 0 then
      FPos.X := 0;
    if FPos.Y < 0 then
      FPos.Y := 0;
    R.Left := FPos.x;
    R.Top := FPos.y;
    R.Right := R.Left + FWidth + 2;
    R.Bottom := R.Top + FHeight + 2;
    if (FWidget.Context = 0) then
    begin
      if not EqualRect(FOldRect, R) then
        FWidget.Update(@FOldRect);
      FWidget.Update(@R);
      FOldRect := R;
    end else
    begin
      if AForceUpdate and not FVisible and not FTimer.Enabled then
        FTimer.Enabled := True;
    end;
  end;
end;

end.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

af0815 hat geschrieben:
So 18. Dez 2022, 17:11
Ich probiere mit folgenden herum (ist teilweise von QT 5 geklaut)
Gut.
Und? Geht sich's aus? :D

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: GTK3 Interface Workshop

Beitrag von af0815 »

theo hat geschrieben:
So 18. Dez 2022, 17:28
Gut.
Und? Geht sich's aus? :D
Nein, noch nicht, winni hat mich da irgendwie verunsichert, vielleicht sitze ich am falschen Pferd. Aber ok, Widgetsets waren niemals meine Stärke:-)
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: GTK3 Interface Workshop

Beitrag von theo »

af0815 hat geschrieben:
So 18. Dez 2022, 17:55
Nein, noch nicht, winni hat mich da irgendwie verunsichert,
Meinst du Warf?
Wieso denn?
Das Handle ist ein TGtk3CustomControl.

Antworten