gtk1,gtk2: improve text cursor drawing.

gtk2: use system settings to determine blink time and use special gtk function to draw text cursor shape
(#0011887)

git-svn-id: trunk@16152 -
This commit is contained in:
paul 2008-08-20 03:31:16 +00:00
parent 508983cd7e
commit b494a1f624
2 changed files with 121 additions and 40 deletions

View File

@ -86,6 +86,11 @@ function GTK_APIWIDGETCLIENT_TYPE: Guint;
implementation implementation
const
CURSOR_ON_MULTIPLIER = 2;
CURSOR_OFF_MULTIPLIER = 1;
CURSOR_DIVIDER = 3;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// gtk_winapiwindow_internal // gtk_winapiwindow_internal
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -95,14 +100,15 @@ type
Y: Integer; Y: Integer;
Width: Integer; Width: Integer;
Height: Integer; Height: Integer;
Visible: Boolean; // Caret is on (can be visible/invisible due to Blinking) Visible: Boolean; // Caret is on (can be visible/invisible due to Blinking)
IsDrawn: Boolean; // Caret is visible at the moment IsDrawn: Boolean; // Caret is visible at the moment
Blinking: Boolean; // Caret should blink Blinking: Boolean; // Caret should blink
BlinkTime: Integer; // Blink time = show + hide time
BlinkTimeout: Integer; // Time after which if there is no user interaction happened blinking must finish
BlinkHide: boolean; // current blinking phase is Hide BlinkHide: boolean; // current blinking phase is Hide
Pixmap: PGDKPixMap; Pixmap: PGDKPixMap;
BackPixmap: PGDKPixMap; BackPixmap: PGDKPixMap;
Timer: guint; Timer: guint;
TimerInterval: guint32;
ShowHideOnFocus: boolean; // true = hide on loose focus, show on get focus ShowHideOnFocus: boolean; // true = hide on loose focus, show on get focus
Invalidated: boolean; Invalidated: boolean;
end; end;
@ -225,6 +231,9 @@ procedure GTKAPIWidgetClient_SetCaretRespondToFocus(Client: PGTKAPIWidgetClient;
procedure GTKAPIWidgetClient_GetCaretRespondToFocus(Client: PGTKAPIWidgetClient; procedure GTKAPIWidgetClient_GetCaretRespondToFocus(Client: PGTKAPIWidgetClient;
var ShowHideOnFocus: boolean); forward; var ShowHideOnFocus: boolean); forward;
function GTKAPIWidgetClient_GetCursorBlink(Client: PGTKAPIWidgetClient): gboolean; forward;
function GTKAPIWidgetClient_GetCursorBlinkTime(Client: PGTKAPIWidgetClient): gint; forward;
function GTKAPIWidgetClient_GetCursorBlinkTimeout(Client: PGTKAPIWidgetClient): gint; forward;
//----------------------------- //-----------------------------
procedure GTKAPIWidget_SetShadowType(APIWidget: PGTKAPIWidget; procedure GTKAPIWidget_SetShadowType(APIWidget: PGTKAPIWidget;
@ -336,7 +345,9 @@ begin
begin begin
Visible := False; Visible := False;
IsDrawn := False; IsDrawn := False;
Blinking := True; Blinking := GTKAPIWidgetClient_GetCursorBlink(PGTKAPIWidgetClient(Client));
BlinkTime := GTKAPIWidgetClient_GetCursorBlinkTime(PGTKAPIWidgetClient(Client));
BlinkTimeout := GTKAPIWidgetClient_GetCursorBlinkTimeout(PGTKAPIWidgetClient(Client));
X := 0; X := 0;
Y := 0; Y := 0;
Width := 1; Width := 1;
@ -471,10 +482,10 @@ begin
//@ gtk_widget_set_flags(AWidget, GTK_REALIZED); //@ gtk_widget_set_flags(AWidget, GTK_REALIZED);
{$Ifdef GTK2} {$IFNDEF GTK1}
gtk_widget_set_double_buffered(AWidget, True); // True bites caret => ToDo gtk_widget_set_double_buffered(AWidget, True); // True bites caret => ToDo
gtk_widget_set_redraw_on_allocate(AWidget, False); gtk_widget_set_redraw_on_allocate(AWidget, False);
{$EndIf} {$ENDIF}
//@ with Attributes do //@ with Attributes do
//@ begin //@ begin
@ -653,8 +664,49 @@ begin
end;} end;}
end; end;
procedure GTKAPIWidgetClient_DrawCaret(Client: PGTKAPIWidgetClient; function GTKAPIWidgetClient_GetCursorBlink(Client: PGTKAPIWidgetClient): gboolean;
CalledByTimer: boolean); {$ifndef GTK1}
var
settings: PGtkSettings;
{$endif}
begin
{$ifdef GTK1}
Result := True;
{$else}
settings := gtk_widget_get_settings(PGtkWidget(Client));
g_object_get(settings, 'gtk-cursor-blink', @Result, nil);
{$endif}
end;
function GTKAPIWidgetClient_GetCursorBlinkTime(Client: PGTKAPIWidgetClient): gint;
{$ifndef GTK1}
var
settings: PGtkSettings;
{$endif}
begin
{$ifdef GTK1}
Result := 1200;
{$else}
settings := gtk_widget_get_settings(PGtkWidget(Client));
g_object_get(settings, 'gtk-cursor-blink-time', @Result, nil);
{$endif}
end;
function GTKAPIWidgetClient_GetCursorBlinkTimeout(Client: PGTKAPIWidgetClient): gint;
{$ifndef GTK1}
var
settings: PGtkSettings;
{$endif}
begin
{$ifdef GTK1}
Result := $7FFFFFFF;
{$else}
settings := gtk_widget_get_settings(PGtkWidget(Client));
g_object_get(settings, 'gtk-cursor-blink-timeout', @Result, nil);
{$endif}
end;
procedure GTKAPIWidgetClient_DrawCaret(Client: PGTKAPIWidgetClient; CalledByTimer: boolean);
{ ShowCaret/HideCaret are used in winapi like: { ShowCaret/HideCaret are used in winapi like:
ShowCaret (paint xor) ShowCaret (paint xor)
Blinking (restore) Blinking (restore)
@ -689,45 +741,56 @@ procedure GTKAPIWidgetClient_DrawCaret(Client: PGTKAPIWidgetClient;
Blinking makes it more complicated, because a Hide triggers an OnPaint, Blinking makes it more complicated, because a Hide triggers an OnPaint,
which triggers in synedit code HideCaret+ShowCaret. which triggers in synedit code HideCaret+ShowCaret.
} }
{$IFDEF GTK1}
const const
BlinkingInterval = 500;
GC_STATE: array[Boolean] of TGtkStateType = GC_STATE: array[Boolean] of TGtkStateType =
(GTK_STATE_INSENSITIVE, GTK_STATE_NORMAL); (
GTK_STATE_INSENSITIVE,
GTK_STATE_NORMAL
);
{$ENDIF}
var var
Widget: PGTKWidget; Widget: PGTKWidget;
WidgetStyle: PGTKStyle; WidgetStyle: PGTKStyle;
HasFocus: boolean; HasFocus: boolean;
ForeGroundGC: PGdkGC;
WidgetIsPainting: Boolean; WidgetIsPainting: Boolean;
{$IFDEF GTK1}
ForeGroundGC: PGdkGC;
{$ELSE}
location: TGdkRectangle;
{$ENDIF}
begin begin
if Client = nil then begin if Client = nil then
begin
DebugLn('WARNING: [GTKAPIWidgetClient_DrawCaret] Got nil client'); DebugLn('WARNING: [GTKAPIWidgetClient_DrawCaret] Got nil client');
Exit; Exit;
end; end;
Widget := PGTKWidget(Client); Widget := PGTKWidget(Client);
WidgetStyle := gtk_widget_get_style(Widget); WidgetStyle := gtk_widget_get_style(Widget);
WidgetIsPainting:=GTKAPIWidgetClient_IsPainting(Client); WidgetIsPainting := GTKAPIWidgetClient_IsPainting(Client);
with Client^.Caret do with Client^.Caret do
begin begin
HasFocus:=gtk_widget_has_focus(Widget); HasFocus := gtk_widget_has_focus(Widget);
if WidgetIsPainting then if WidgetIsPainting then
Invalidated:=false; Invalidated := false;
{$IFDEF VerboseCaret} {$IFDEF VerboseCaret}
DebugLn(['GTKAPIWidgetClient_DrawCaret START Client=',DbgS(Client),' Timer=',Timer,' Blink=',Blinking,' BlinkHide=',BlinkHide,' Visible=',Visible,' ShowHideOnFocus=',ShowHideOnFocus,' Focus=',gtk_widget_has_focus(Widget),' IsDrawn=',IsDrawn,' W=',Width,' H=',Height,' WidgetIsPainting=',WidgetIsPainting]); DebugLn(['GTKAPIWidgetClient_DrawCaret START Client=',DbgS(Client),' Timer=',Timer,' Blink=',Blinking,' BlinkHide=',BlinkHide,' Visible=',Visible,' ShowHideOnFocus=',ShowHideOnFocus,' Focus=',gtk_widget_has_focus(Widget),' IsDrawn=',IsDrawn,' W=',Width,' H=',Height,' WidgetIsPainting=',WidgetIsPainting]);
{$ENDIF} {$ENDIF}
if IsDrawn if IsDrawn and
and ((not Visible) (
or (Blinking and BlinkHide)) (not Visible) or
then begin (Blinking and BlinkHide)
) then
begin
// hide caret (restore background) // hide caret (restore background)
if WidgetIsPainting then begin if WidgetIsPainting then
if (BackPixmap <> nil) begin
and (Widget<>nil) if (BackPixmap <> nil) and (Widget<>nil) and (WidgetStyle<>nil) then
and (WidgetStyle<>nil) begin
then begin
gdk_draw_pixmap( gdk_draw_pixmap(
Widget^.Window, Widget^.Window,
WidgetStyle^.bg_gc[GTK_STATE_NORMAL], WidgetStyle^.bg_gc[GTK_STATE_NORMAL],
@ -741,13 +804,14 @@ begin
end; end;
IsDrawn := False; IsDrawn := False;
Invalidated:=false; Invalidated:=false;
end else begin end else
begin
// paint only during painting, otherwise invalidate // paint only during painting, otherwise invalidate
{$IFDEF VerboseCaret} {$IFDEF VerboseCaret}
DebugLn(['GTKAPIWidgetClient_DrawCaret Invalidate Hide ',X,',',Y]); DebugLn(['GTKAPIWidgetClient_DrawCaret Invalidate Hide ',X,',',Y]);
{$ENDIF} {$ENDIF}
GTKAPIWidgetClient_InvalidateCaret(Client); GTKAPIWidgetClient_InvalidateCaret(Client);
IsDrawn:=false; IsDrawn := false;
end; end;
end end
else else
@ -761,8 +825,8 @@ begin
if Pixmap <> nil then if Pixmap <> nil then
Assert(False, 'Trace:TODO: [GTKAPIWidgetClient_DrawCaret] Implement bitmap'); Assert(False, 'Trace:TODO: [GTKAPIWidgetClient_DrawCaret] Implement bitmap');
if WidgetIsPainting then begin if WidgetIsPainting then
begin
//Create backbitmap if needed //Create backbitmap if needed
if (BackPixmap = nil) if (BackPixmap = nil)
and (Widget^.Window<>nil) and (Widget^.Window<>nil)
@ -805,6 +869,7 @@ begin
and (Width>0) and (Width>0)
and (Height>0) and (Height>0)
then begin then begin
{$ifdef GTK1}
// set draw function to xor // set draw function to xor
ForeGroundGC:=WidgetStyle^.fg_gc[GC_STATE[PtrUInt(Pixmap) <> 1]]; ForeGroundGC:=WidgetStyle^.fg_gc[GC_STATE[PtrUInt(Pixmap) <> 1]];
//gdk_gc_get_values(ForeGroundGC,@ForeGroundGCValues); //gdk_gc_get_values(ForeGroundGC,@ForeGroundGCValues);
@ -827,6 +892,14 @@ begin
// restore draw function // restore draw function
gdk_gc_set_function(ForeGroundGC,GDK_COPY); gdk_gc_set_function(ForeGroundGC,GDK_COPY);
end; end;
{$ELSE}
location.x := X;
location.y := Y - 1;
location.width := 0;
location.height := Height;
gtk_draw_insertion_cursor(Widget, Widget^.Window, nil, @location, true,
GTK_TEXT_DIR_LTR, false);
{$ENDIF}
end else end else
DebugLn('***: Draw Caret failed: Client=',DbgS(Client), DebugLn('***: Draw Caret failed: Client=',DbgS(Client),
' X='+dbgs(X)+' Y='+dbgs(Y)+' W='+dbgs(Width)+' H='+dbgs(Height), ' X='+dbgs(X)+' Y='+dbgs(Y)+' W='+dbgs(Width)+' H='+dbgs(Height),
@ -841,20 +914,26 @@ begin
GTKAPIWidgetClient_InvalidateCaret(Client); GTKAPIWidgetClient_InvalidateCaret(Client);
end; end;
end; end;
// stop, start timer // stop, start timer
if Visible and Blinking if Visible and Blinking and ((not ShowHideOnFocus) or HasFocus) then
and ((not ShowHideOnFocus) or HasFocus) begin
then begin if Timer = 0 then
TimerInterval:=BlinkingInterval; if IsDrawn then
if Timer=0 then begin Timer := gtk_timeout_add(BlinkTime * CURSOR_ON_MULTIPLIER div CURSOR_DIVIDER,
Timer := gtk_timeout_add(TimerInterval, @GTKAPIWidgetClient_Timer, Client); @GTKAPIWidgetClient_Timer, Client)
end; else
end else begin Timer := gtk_timeout_add(BlinkTime * CURSOR_OFF_MULTIPLIER div CURSOR_DIVIDER,
if Timer<>0 then begin @GTKAPIWidgetClient_Timer, Client)
end else
begin
if Timer <> 0 then
begin
gtk_timeout_remove(Timer); gtk_timeout_remove(Timer);
Timer:=0; Timer := 0;
end; end;
end; end;
{$IFDEF VerboseCaret} {$IFDEF VerboseCaret}
DebugLn(['GTKAPIWidgetClient_DrawCaret END Client=',DbgS(Client),' Timer=',Timer,' Blink=',Blinking,' BlinkHide=',BlinkHide,' Visible=',Visible,' ShowHideOnFocus=',ShowHideOnFocus,' Focus=',gtk_widget_has_focus(Widget),' IsDrawn=',IsDrawn,' W=',Width,' H=',Height,' WidgetIsPainting=',WidgetIsPainting]); DebugLn(['GTKAPIWidgetClient_DrawCaret END Client=',DbgS(Client),' Timer=',Timer,' Blink=',Blinking,' BlinkHide=',BlinkHide,' Visible=',Visible,' ShowHideOnFocus=',ShowHideOnFocus,' Focus=',gtk_widget_has_focus(Widget),' IsDrawn=',IsDrawn,' W=',Width,' H=',Height,' WidgetIsPainting=',WidgetIsPainting]);
{$ENDIF} {$ENDIF}
@ -1295,7 +1374,7 @@ begin
end; end;
initialization initialization
MParentClass:=nil; MParentClass := nil;
end. end.

View File

@ -119,6 +119,8 @@ function gtk_tree_path_new_from_indices(first_index: gint): PGtkTreePath; cdecl;
procedure gtk_toolbar_insert(toolbar: PGtkToolBar; item: PGtkToolItem; pos: gint); cdecl; external gtklib name 'gtk_toolbar_insert'; procedure gtk_toolbar_insert(toolbar: PGtkToolBar; item: PGtkToolItem; pos: gint); cdecl; external gtklib name 'gtk_toolbar_insert';
procedure gtk_event_box_set_visible_window(event_box: PGtkEventBox; visible_window: gboolean); cdecl; external gtklib name 'gtk_event_box_set_visible_window'; procedure gtk_event_box_set_visible_window(event_box: PGtkEventBox; visible_window: gboolean); cdecl; external gtklib name 'gtk_event_box_set_visible_window';
procedure gtk_event_box_set_above_child(event_box: PGtkEventBox; visible_window: gboolean); cdecl; external gtklib name 'gtk_event_box_set_above_child'; procedure gtk_event_box_set_above_child(event_box: PGtkEventBox; visible_window: gboolean); cdecl; external gtklib name 'gtk_event_box_set_above_child';
procedure gtk_draw_insertion_cursor(widget:PGtkWidget; drawable:PGdkDrawable; area:PGdkRectangle; location:PGdkRectangle; is_primary:gboolean;
direction:TGtkTextDirection; draw_arrow:gboolean); cdecl; external gtklib;
// gtk 2.6 // gtk 2.6
type type