Browse Source

* Rework to use input event, add LCL key handling

Michaël Van Canneyt 5 months ago
parent
commit
d401917253

+ 1 - 0
demo/Edit/editdemo.lpi

@@ -38,6 +38,7 @@
       <Unit>
       <Unit>
         <Filename Value="editdemo.lpr"/>
         <Filename Value="editdemo.lpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
+        <UnitName Value="EditDemo"/>
       </Unit>
       </Unit>
       <Unit>
       <Unit>
         <Filename Value="mainunit.pp"/>
         <Filename Value="mainunit.pp"/>

+ 1 - 0
demo/Edit/editdemo.lpr

@@ -2,6 +2,7 @@ program EditDemo;
 
 
 uses
 uses
   {$IFDEF Unix}
   {$IFDEF Unix}
+  cwstring,
   cthreads,
   cthreads,
   {$ENDIF}
   {$ENDIF}
   Fresnel, // initializes the widgetset
   Fresnel, // initializes the widgetset

+ 29 - 3
src/base/fresnel.dom.pas

@@ -1447,7 +1447,9 @@ type
     function PageToContentPos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; virtual; overload; // content of viewport to content box of El
     function PageToContentPos(El: TFresnelElement; const x, y: TFresnelLength): TFresnelPoint; virtual; overload; // content of viewport to content box of El
     function PageToContentPos(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; virtual; overload; // content of viewport to content box of El
     function PageToContentPos(El: TFresnelElement; const p: TFresnelPoint): TFresnelPoint; virtual; overload; // content of viewport to content box of El
     procedure WSMouseXY(WSData: TFresnelMouseEventInit; MouseEventId: TEventID); virtual;
     procedure WSMouseXY(WSData: TFresnelMouseEventInit; MouseEventId: TEventID); virtual;
-    procedure WSKey(WSData: TFresnelKeyEventInit; KeyEventId: TEventID); virtual;
+    // Return true if default was prevented
+    function WSKey(WSData: TFresnelKeyEventInit; KeyEventId: TEventID) : boolean; virtual;
+    function WSInput(WSData: TFresnelInputEventInit) : boolean; virtual;
     property VPApplication: IFresnelVPApplication read FVPApplication;
     property VPApplication: IFresnelVPApplication read FVPApplication;
     property DPI[IsHorizontal: boolean]: TFresnelLength read GetDPI write SetDPI;
     property DPI[IsHorizontal: boolean]: TFresnelLength read GetDPI write SetDPI;
     property ScrollbarWidth[IsHorizontal: boolean]: TFresnelLength read GetScrollbarWidth write SetScrollbarWidth;
     property ScrollbarWidth[IsHorizontal: boolean]: TFresnelLength read GetScrollbarWidth write SetScrollbarWidth;
@@ -6185,7 +6187,7 @@ begin
     end;
     end;
 end;
 end;
 
 
-procedure TFresnelViewport.WSKey(WSData: TFresnelKeyEventInit; KeyEventId: TEventID);
+function TFresnelViewport.WSKey(WSData: TFresnelKeyEventInit; KeyEventId: TEventID): boolean;
 
 
 var
 var
   lFocus : TFresnelElement;
   lFocus : TFresnelElement;
@@ -6201,7 +6203,31 @@ begin
   if not lEvtClass.InheritsFrom(TFresnelKeyEvent) then
   if not lEvtClass.InheritsFrom(TFresnelKeyEvent) then
      Raise EFresnel.CreateFmt('%s is not a descendent of class TFresnelKeyEvent',[lEvtClass.ClassName]);
      Raise EFresnel.CreateFmt('%s is not a descendent of class TFresnelKeyEvent',[lEvtClass.ClassName]);
   lEvt:=lKeyEvtClass.Create(WSData);
   lEvt:=lKeyEvtClass.Create(WSData);
-  Bubble(lFocus,lEvt);
+  try
+    Bubble(lFocus,lEvt);
+    Result:=lEvt.DefaultPrevented;
+  finally
+    lEvt.Free;
+  end;
+end;
+
+function TFresnelViewport.WSInput(WSData: TFresnelInputEventInit): boolean;
+var
+  lFocus : TFresnelElement;
+  lEvt : TFresnelInputEvent;
+
+
+begin
+  lFocus:=FFocusedElement;
+  if not Assigned(lFocus) then
+    lFocus:=Self;
+  lEvt:=TFresnelInputEvent.Create(WSData);
+  try
+    Bubble(lFocus,lEvt);
+    Result:=lEvt.DefaultPrevented;
+  finally
+    lEvt.Free;
+  end;
 end;
 end;
 
 
 constructor TFresnelViewport.Create(AOwner: TComponent);
 constructor TFresnelViewport.Create(AOwner: TComponent);

+ 146 - 112
src/base/fresnel.edit.pp

@@ -85,6 +85,8 @@ Type
     function DoDispatchEvent(aEvent: TAbstractEvent): Integer; override;
     function DoDispatchEvent(aEvent: TAbstractEvent): Integer; override;
     procedure HandleFocusChange(GotFocus: Boolean);
     procedure HandleFocusChange(GotFocus: Boolean);
     procedure HandleKeyDown(aEvent: TFresnelKeyEvent); virtual;
     procedure HandleKeyDown(aEvent: TFresnelKeyEvent); virtual;
+    procedure HandleInput(aEvent: TFresnelInputEvent); virtual;
+    procedure HandleKeyUp(aEvent: TFresnelKeyEvent); virtual;
     procedure HandleMouseDown(aEvent : TFresnelMouseEvent); virtual;
     procedure HandleMouseDown(aEvent : TFresnelMouseEvent); virtual;
     procedure HandleMouseMove(aEvent: TFresnelMouseEvent); virtual;
     procedure HandleMouseMove(aEvent: TFresnelMouseEvent); virtual;
     procedure HandleMouseUp(aEvent : TFresnelMouseEvent); virtual;
     procedure HandleMouseUp(aEvent : TFresnelMouseEvent); virtual;
@@ -229,124 +231,148 @@ end;
 procedure TEdit.HandleKeyDown(aEvent: TFresnelKeyEvent);
 procedure TEdit.HandleKeyDown(aEvent: TFresnelKeyEvent);
 
 
 var
 var
-  lKeyUTF8 : String;
-  lKeyLen : Integer;
   lChanged : Boolean;
   lChanged : Boolean;
   lOffset : Integer;
   lOffset : Integer;
-  pInsert,pStart : PAnsiChar;
 
 
 begin
 begin
-  lChanged:=False;
   // Printable character has numkey >0
   // Printable character has numkey >0
   if (aEvent.NumKey>0) and not (aEvent.CtrlKey) then
   if (aEvent.NumKey>0) and not (aEvent.CtrlKey) then
-    begin
-    if ReadOnly then
-      exit;
-    DoDeleteSelection;
-    lKeyUTF8:=UnicodeToUTF8(aEvent.NumKey);
-    pStart:=PAnsiChar(FValue);
-    pInsert:=UTF8CodepointStart(pStart,Length(FValue),FCursorPos);
-    Insert(lKeyUTF8,FValue,PtrInt(PInsert-PStart)+1);
-    if FCursorPos<=FSelectionEnd then
-      Inc(FSelectionEnd,1);
-    if FCursorPos<=FSelectionStart then
-      Inc(FSelectionStart,1);
-    Inc(FCursorPos,1);
-    EventDispatcher.DispatchEvent(evtChange);
-    lChanged:=True;
-    end
-  else
-    begin
-    lChanged:=True;
-    Case aEvent.NumKey of
-      TKeyCodes.BackSpace:
+    exit;
+  lChanged:=True;
+  Case aEvent.NumKey of
+    TKeyCodes.ArrowLeft :
+      begin
+      if FCursorPos>0 then
         begin
         begin
-        if FSelectionStart=FSelectionEnd then
-          begin
-          if FCursorPos>0 then
-            begin
-            lOffset:=UTF8FindNearestCharStart(PChar(FValue),Length(FValue),FCursorPos-1);
-            lKeyLen:=UTF8CodepointSize(@FValue[lOffset+1]);
-            Delete(FValue,lOffSet+1,lKeyLen);
-            FCursorPos:=lOffSet;
-            FSelectionStart:=FCursorPos;
-            FSelectionEnd:=FCursorPos;
-            end
-          end
+        if aEvent.CtrlKey then
+          lOffset:=PrevWordStartOffset-FCursorPos
         else
         else
-          begin
-          DoDeleteSelection;
+          lOffset:=-1; // Todo: calc length of codepoint in bytes
+        FCursorPos:=FCursorPos+lOffset;
+        FSelectionStart:=FCursorPos;
+        if not aEvent.ShiftKey then
           FSelectionEnd:=FSelectionStart;
           FSelectionEnd:=FSelectionStart;
-          FCursorPos:=FSelectionStart;
-          end;
         end;
         end;
-      TKeyCodes.Delete:
+      end;
+    TKeyCodes.ArrowRight :
+      begin
+      if FCursorPos<Length(FValue) then
         begin
         begin
-        if FSelectionStart=FSelectionEnd then
-          begin
-          lKeyLen:=UTF8CodepointSize(@FValue[FCursorPos+1]);
-          Delete(FValue,FCursorPos+1,lKeyLen);
-          end
+        if aEvent.CtrlKey then
+          lOffset:=NextWordEndOffset-FCursorPos
         else
         else
-          begin
-          lKeyLen:=Abs(FSelectionStart-FSelectionEnd);
-          DoDeleteSelection;
-          if FSelectionStart<FSelectionEnd then
-            FSelectionEnd:=FSelectionStart
-          else
-            FSelectionStart:=FSelectionEnd;
-          end;
-        FCursorPos:=FSelectionEnd;
-        end;
-      TKeyCodes.ArrowLeft :
-        begin
-        if FCursorPos>0 then
-          begin
-          if aEvent.CtrlKey then
-            lOffset:=PrevWordStartOffset-FCursorPos
-          else
-            lOffset:=-1; // Todo: calc length of codepoint in bytes
-          FCursorPos:=FCursorPos+lOffset;
-          FSelectionStart:=FCursorPos;
-          if not aEvent.ShiftKey then
-            FSelectionEnd:=FSelectionStart;
-          end;
-        end;
-      TKeyCodes.ArrowRight :
-        begin
-        if FCursorPos<Length(FValue) then
-          begin
-          if aEvent.CtrlKey then
-            lOffset:=NextWordEndOffset-FCursorPos
-          else
-            lOffset:=1;
-          Inc(FCursorPos,lOffset);
-          FSelectionEnd:=FCursorPos;
-          if not aEvent.ShiftKey then
-            FSelectionStart:=FSelectionEnd;
-          end
-        end;
-      TKeyCodes.Home:
-        begin
-        FCursorPos:=0;
-        FSelectionEnd:=0;
+          lOffset:=1;
+        Inc(FCursorPos,lOffset);
+        FSelectionEnd:=FCursorPos;
         if not aEvent.ShiftKey then
         if not aEvent.ShiftKey then
-          FSelectionStart:=0;
-        end;
-      TKeyCodes.End_:
+          FSelectionStart:=FSelectionEnd;
+        end
+      end;
+    TKeyCodes.Home:
+      begin
+      FCursorPos:=0;
+      FSelectionEnd:=0;
+      if not aEvent.ShiftKey then
+        FSelectionStart:=0;
+      end;
+    TKeyCodes.End_:
+      begin
+      FCursorPos:=Length(FValue);
+      FSelectionEnd:=FCursorPos;
+      if not aEvent.ShiftKey then
+        FSelectionStart:=FCursorPos;
+      end;
+  else
+    lChanged:=False;
+  end;
+  if lChanged then
+    EditParamsChanged;
+//  Writeln('Text now: "',FValue,'", Selection now: "',GetSelectionText,'"');
+end;
+
+procedure TEdit.HandleInput(aEvent: TFresnelInputEvent);
+
+var
+  lKeyLen : Integer;
+  lKeyUTF8 : String;
+  pInsert,pStart : PAnsiChar;
+  lChanged : Boolean;
+  lOffset : Integer;
+
+begin
+  // Writeln('In input, using data : "',aEvent.Data,'": ',aEvent.FresnelInputType);
+  if ReadOnly then
+    exit;
+  lChanged:=True;
+  Case aEvent.FresnelInputType of
+  fitDeleteContentBackward:
+    begin
+    if FSelectionStart=FSelectionEnd then
+      begin
+      if FCursorPos>0 then
         begin
         begin
-        FCursorPos:=Length(FValue);
+        lOffset:=UTF8FindNearestCharStart(PChar(FValue),Length(FValue),FCursorPos-1);
+        lKeyLen:=UTF8CodepointSize(@FValue[lOffset+1]);
+        Delete(FValue,lOffSet+1,lKeyLen);
+        FCursorPos:=lOffSet;
+        FSelectionStart:=FCursorPos;
         FSelectionEnd:=FCursorPos;
         FSelectionEnd:=FCursorPos;
-        if not aEvent.ShiftKey then
-          FSelectionStart:=FCursorPos;
-        end;
+        end
+      end
     else
     else
-      lChanged:=False;
+      begin
+      DoDeleteSelection;
+      FSelectionEnd:=FSelectionStart;
+      FCursorPos:=FSelectionStart;
+      end;
+    end;
+  fitDeleteContentForward:
+    begin
+    if FSelectionStart=FSelectionEnd then
+      begin
+      lKeyLen:=UTF8CodepointSize(@FValue[FCursorPos+1]);
+      Delete(FValue,FCursorPos+1,lKeyLen);
+      end
+    else
+      begin
+      lKeyLen:=Abs(FSelectionStart-FSelectionEnd);
+      DoDeleteSelection;
+      if FSelectionStart<FSelectionEnd then
+        FSelectionEnd:=FSelectionStart
+      else
+        FSelectionStart:=FSelectionEnd;
+      end;
+    FCursorPos:=FSelectionEnd;
     end;
     end;
+  fitInsertText,
+  fitInsertCompositionText,
+  fitInsertFromPaste:
+    begin
+    DoDeleteSelection;
+    lKeyUTF8:=aEvent.Data;
+    pStart:=PAnsiChar(FValue);
+    pInsert:=UTF8CodepointStart(pStart,Length(FValue),FCursorPos);
+    Insert(lKeyUTF8,FValue,PtrInt(PInsert-PStart)+1);
+    if FCursorPos<=FSelectionEnd then
+      Inc(FSelectionEnd,1);
+    if FCursorPos<=FSelectionStart then
+      Inc(FSelectionStart,1);
+    Inc(FCursorPos,1);
     end;
     end;
+  else
+    lChanged:=False;
+  end;
   if lChanged then
   if lChanged then
+    begin
+    EventDispatcher.DispatchEvent(evtChange);
     EditParamsChanged;
     EditParamsChanged;
-  Writeln('Text now: "',FValue,'", Selection now: "',GetSelectionText,'"');
+    end;
+  // Writeln('Text now: "',FValue,'", Selection now: "',GetSelectionText,'"');
+end;
+
+procedure TEdit.HandleKeyUp(aEvent: TFresnelKeyEvent);
+begin
+  // Do nothing for the moment.
 end;
 end;
 
 
 procedure TEdit.HandleMouseMove(aEvent : TFresnelMouseEvent);
 procedure TEdit.HandleMouseMove(aEvent : TFresnelMouseEvent);
@@ -542,7 +568,7 @@ begin
       end
       end
     else
     else
       begin
       begin
-      Writeln('Prem brak');
+      // Writeln('Premature break');
       break; // we can safely break after last visible character is found
       break; // we can safely break after last visible character is found
       end;
       end;
     Inc(lCharNum);
     Inc(lCharNum);
@@ -562,7 +588,7 @@ begin
 
 
   FVisibleText := Copy(lText, lFirstVisibleIndex, lLastVisibleIndex - lFirstVisibleIndex);
   FVisibleText := Copy(lText, lFirstVisibleIndex, lLastVisibleIndex - lFirstVisibleIndex);
   FDrawOffset := FTextOffset - FDrawOffset;
   FDrawOffset := FTextOffset - FDrawOffset;
-  Writeln('Calc cursor at: ',FCursorX,' text width : ',Font.TextSize(lText).x,' offset : ', FTextOffset);
+  //Writeln('Calc cursor at: ',FCursorX:5:2,' text width : ',Font.TextSize(lText).x:5:2,' offset : ', FTextOffset);
 
 
   // Write('Value: "',FValue,'", Visible: "',FVisibleText,'"');
   // Write('Value: "',FValue,'", Visible: "',FVisibleText,'"');
   // Write(', Sel: [Char:',FSelectionStart:2,' -',FSelectionEnd:5,', Pos:',FSelectionStartX:5:2,' -',FSelectionEndX:5:2,']');
   // Write(', Sel: [Char:',FSelectionStart:2,' -',FSelectionEnd:5,', Pos:',FSelectionStartX:5:2,' -',FSelectionEndX:5:2,']');
@@ -611,7 +637,7 @@ begin
       break;
       break;
     Inc(lCharNum);
     Inc(lCharNum);
     end;
     end;
-  Writeln('CalcCharOffset : ',Result);
+  // Writeln('CalcCharOffset : ',Result);
 end;
 end;
 
 
 Function TEdit.CalcXOffset(aCharPos: Integer; aUseOffset: Boolean = true; aUseDrawText : Boolean = False) : TFresnelLength;
 Function TEdit.CalcXOffset(aCharPos: Integer; aUseOffset: Boolean = true; aUseDrawText : Boolean = False) : TFresnelLength;
@@ -742,6 +768,7 @@ var
   lFresnelEvt : TFresnelEvent absolute aEvent;
   lFresnelEvt : TFresnelEvent absolute aEvent;
   lKeyEvent : TFresnelKeyEvent absolute aEvent;
   lKeyEvent : TFresnelKeyEvent absolute aEvent;
   lMouseEvent : TFresnelMouseEvent absolute aEvent;
   lMouseEvent : TFresnelMouseEvent absolute aEvent;
+  lInputEvent : TFresnelInputEvent absolute aEvent;
 
 
 begin
 begin
   Result:=inherited DoDispatchEvent(aEvent);
   Result:=inherited DoDispatchEvent(aEvent);
@@ -753,15 +780,18 @@ begin
    evtBlur,
    evtBlur,
    evtFocus:
    evtFocus:
      HandleFocusChange(aEvent.EventID=evtFocus);
      HandleFocusChange(aEvent.EventID=evtFocus);
-   evtKeyDown
-   {, evtKeyUp
-    , evtKeyPress
-   }:
+   evtKeyDown,
+   evtKeyUp:
      if (aEvent is TFresnelKeyEvent) then
      if (aEvent is TFresnelKeyEvent) then
        begin
        begin
-       if aEvent.EventID=evtKeyDown then
-         HandleKeyDown(lKeyEvent);
+       Case aEvent.EventID of
+         evtKeyDown: HandleKeyDown(lKeyEvent);
+         evtKeyUp: HandleKeyUp(lKeyEvent);
+       end;
        end;
        end;
+   evtInput:
+     HandleInput(lInputEvent);
+
    evtMouseDown:
    evtMouseDown:
      if (aEvent is TFresnelMouseEvent) then
      if (aEvent is TFresnelMouseEvent) then
        HandleMouseDown(lMouseEvent);
        HandleMouseDown(lMouseEvent);
@@ -827,10 +857,13 @@ begin
   // Selection background.
   // Selection background.
   RSel:=R;
   RSel:=R;
   SelWidth:=Abs(FSelectionStartX-FSelectionEndX);
   SelWidth:=Abs(FSelectionStartX-FSelectionEndX);
-  RSel.Left:=RSel.Left+{lLeftSideMargin+}FSelectionStartX;
-  RSel.Right:=RSel.Left+SelWidth;
-  lBackColor:=fpimage.colDkBlue; // GetComputedColor(fcaSelectionBackGroundColor,colTransparent);
-  aRenderer.FillRect(fpimage.colDkBlue,RSel);
+  if SelWidth>0 then
+    begin
+    RSel.Left:=RSel.Left+{lLeftSideMargin+}FSelectionStartX;
+    RSel.Right:=RSel.Left+SelWidth;
+    lBackColor:=fpimage.colDkBlue; // GetComputedColor(fcaSelectionBackGroundColor,colTransparent);
+    aRenderer.FillRect(fpimage.colDkBlue,RSel);
+    end;
   lHaveShadow:=GetComputedTextShadow(lOffsetX, lOffsetY, lRadius, lShadowColor);
   lHaveShadow:=GetComputedTextShadow(lOffsetX, lOffsetY, lRadius, lShadowColor);
   if lHaveShadow then
   if lHaveShadow then
     aRenderer.AddTextShadow(lOffsetX,lOffsetY,lShadowColor,lRadius);
     aRenderer.AddTextShadow(lOffsetX,lOffsetY,lShadowColor,lRadius);
@@ -838,6 +871,7 @@ begin
   lTopSideMargin:=GetComputedLength(fcaPaddingTop);
   lTopSideMargin:=GetComputedLength(fcaPaddingTop);
   if FSelectionStart<>FSelectionEnd then
   if FSelectionStart<>FSelectionEnd then
     begin
     begin
+    // Writeln('Have sel');
     if FSelectionStart>0 then
     if FSelectionStart>0 then
       aRenderer.TextOut(R.Left+lLeftSideMargin,R.Top+lTopSideMargin,Font,lColorFP,Copy(lCaption,1,1+FSelectionStart));
       aRenderer.TextOut(R.Left+lLeftSideMargin,R.Top+lTopSideMargin,Font,lColorFP,Copy(lCaption,1,1+FSelectionStart));
     aRenderer.TextOut(RSel.Left,RSel.Top+lTopSideMargin,Font,colWhite,Copy(lCaption,1+FSelectionStart,FSelectionEnd-FSelectionStart));
     aRenderer.TextOut(RSel.Left,RSel.Top+lTopSideMargin,Font,colWhite,Copy(lCaption,1+FSelectionStart,FSelectionEnd-FSelectionStart));
@@ -847,7 +881,7 @@ begin
   else
   else
     begin
     begin
     aRenderer.TextOut(R.Left+lLeftSideMargin,R.Top+lTopSideMargin,Font,lColorFP,lCaption);
     aRenderer.TextOut(R.Left+lLeftSideMargin,R.Top+lTopSideMargin,Font,lColorFP,lCaption);
-    Writeln('Cursor at: ',FCursorX,' text width : ',Font.TextSize(lCaption).x);
+    // Writeln('Cursor at: ',FCursorX:5:2,' text width : ',Font.TextSize(lCaption).x:5:2);
     end;
     end;
 
 
   if FCursorVisible then
   if FCursorVisible then

+ 26 - 17
src/base/fresnel.events.pas

@@ -31,7 +31,7 @@ Const
   evtUnknown = 0;
   evtUnknown = 0;
   evtKeyDown = 1;
   evtKeyDown = 1;
   evtKeyUp = 2;
   evtKeyUp = 2;
-  evtKeyPress = 3;
+  evtInput = 3;
   evtEnter = 4;
   evtEnter = 4;
   evtLeave = 5;
   evtLeave = 5;
   evtClick = 6;
   evtClick = 6;
@@ -56,6 +56,7 @@ Const
   evtFocus = 25;
   evtFocus = 25;
   evtBlur = 26;
   evtBlur = 26;
 
 
+
   evtLastControlEvent = evtBlur;
   evtLastControlEvent = evtBlur;
 
 
   evtAllMouse = [evtMouseMove,evtMouseDown,evtMouseUp];
   evtAllMouse = [evtMouseMove,evtMouseDown,evtMouseUp];
@@ -321,21 +322,21 @@ Type
   // https://rawgit.com/w3c/input-events/v1/index.html#interface-InputEvent-Attributes
   // https://rawgit.com/w3c/input-events/v1/index.html#interface-InputEvent-Attributes
 
 
   TFresnelInputType = (
   TFresnelInputType = (
-   insertText,insertReplacementText,insertLineBreak,
-   insertParagraph,insertOrderedList,insertUnorderedList,
-   insertHorizontalRule,insertFromYank,insertFromDrop,
-   insertFromPaste,insertFromPasteAsQuotation,insertTranspose,
-   insertCompositionText,insertLink,
-   deleteWordBackward,deleteWordForward,deleteSoftLineBackward,
-   deleteSoftLineForward,deleteEntireSoftLine,deleteHardLineBackward,
-   deleteHardLineForward,deleteByDrag,deleteByCut,
-   deleteContent,deleteContentBackward,deleteContentForward,
-   historyUndo,historyRedo,
-   formatBold,formatItalic,formatUnderline,formatStrikeThrough,
-   formatSuperscript,formatSubscript,formatJustifyFull,formatJustifyCenter,
-   formatJustifyRight,formatJustifyLeft,formatIndent, formatOutdent,
-   formatRemove,formatSetBlockTextDirection,formatSetInlineTextDirection,
-   formatBackColor,formatFontColor,formatFontName
+    fitInsertText, fitInsertReplacementText, fitInsertLineBreak,
+    fitInsertParagraph, fitInsertOrderedList, fitInsertUnorderedList,
+    fitInsertHorizontalRule, fitInsertFromYank, fitInsertFromDrop,
+    fitInsertFromPaste, fitInsertFromPasteAsQuotation, fitInsertTranspose,
+    fitInsertCompositionText, fitInsertLink,
+    fitDeleteWordBackward, fitDeleteWordForward, fitDeleteSoftLineBackward,
+    fitDeleteSoftLineForward, fitDeleteEntireSoftLine, fitDeleteHardLineBackward,
+    fitDeleteHardLineForward, fitDeleteByDrag, fitDeleteByCut,
+    fitDeleteContent, fitDeleteContentBackward, fitDeleteContentForward,
+    fitHistoryUndo, fitHistoryRedo,
+    fitFormatBold, fitFormatItalic, fitFormatUnderline, fitFormatStrikeThrough,
+    fitFormatSuperscript, fitformatSubscript, fitFormatJustifyFull, fitFormatJustifyCenter,
+    fitFormatJustifyRight, fitFormatJustifyLeft, fitFormatIndent, fitFormatOutdent,
+    fitFormatRemove, fitFormatSetBlockTextDirection, fitFormatSetInlineTextDirection,
+    fitFormatBackColor, fitFormatFontColor, fitFormatFontName
   );
   );
   TFLInputType = TFresnelInputType;
   TFLInputType = TFresnelInputType;
   TFresnelInputEventInit = record
   TFresnelInputEventInit = record
@@ -351,6 +352,7 @@ Type
     function GetInputType: string;
     function GetInputType: string;
   Public
   Public
     Constructor Create(aInit : TFresnelInputEventInit);
     Constructor Create(aInit : TFresnelInputEventInit);
+    class function FresnelEventID: TEventID; override;
     Property Data : String Read FInit.Data;
     Property Data : String Read FInit.Data;
     Property FresnelInputType : TFresnelInputType Read FInit.inputtype;
     Property FresnelInputType : TFresnelInputType Read FInit.inputtype;
     Property InputType : string Read GetInputType;
     Property InputType : string Read GetInputType;
@@ -461,9 +463,15 @@ end;
 
 
 constructor TFresnelInputEvent.Create(aInit: TFresnelInputEventInit);
 constructor TFresnelInputEvent.Create(aInit: TFresnelInputEventInit);
 begin
 begin
+  Inherited Create(Nil,FresnelEventID);
   FInit:=AInit;
   FInit:=AInit;
 end;
 end;
 
 
+class function TFresnelInputEvent.FresnelEventID: TEventID;
+begin
+  Result:=evtInput;
+end;
+
 { TFresnelBlurEvent }
 { TFresnelBlurEvent }
 
 
 class function TFresnelBlurEvent.FresnelEventID: TEventID;
 class function TFresnelBlurEvent.FresnelEventID: TEventID;
@@ -506,6 +514,7 @@ begin
   Result:=evtKeyUp;
   Result:=evtKeyUp;
 end;
 end;
 
 
+
 { TFresnelKeyEvent }
 { TFresnelKeyEvent }
 
 
 function TFresnelKeyEvent.GetShiftKey(AIndex: Integer): Boolean;
 function TFresnelKeyEvent.GetShiftKey(AIndex: Integer): Boolean;
@@ -683,7 +692,7 @@ begin
   R(TFresnelFocusOutEvent);
   R(TFresnelFocusOutEvent);
   R(TFresnelKeyDownEvent);
   R(TFresnelKeyDownEvent);
   R(TFresnelKeyUpEvent);
   R(TFresnelKeyUpEvent);
-//  R(TFresnelKeyEvent);
+  R(TFresnelInputEvent);
 end;
 end;
 
 
 class destructor TFresnelEventDispatcher.Done;
 class destructor TFresnelEventDispatcher.Done;

+ 19 - 1
src/base/fresnel.widgetset.pas

@@ -20,7 +20,7 @@ unit Fresnel.WidgetSet;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, Fresnel.Classes, Fresnel.DOM, Fresnel.Renderer;
+  Classes, SysUtils, Fresnel.Keys, Fresnel.Events, Fresnel.Classes, Fresnel.DOM, Fresnel.Renderer;
 
 
 type
 type
   TFreHandle = Pointer;
   TFreHandle = Pointer;
@@ -39,6 +39,7 @@ type
     procedure SetFormBounds(const AValue: TFresnelRect); virtual; abstract;
     procedure SetFormBounds(const AValue: TFresnelRect); virtual; abstract;
     procedure SetVisible(const AValue: boolean); virtual; abstract;
     procedure SetVisible(const AValue: boolean); virtual; abstract;
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    function NumKeyToInputType(aKey: Integer; aShift: TShiftState; out aType: TFresnelInputType): boolean; virtual;
   public
   public
     function GetClientSize: TFresnelPoint; virtual; abstract;
     function GetClientSize: TFresnelPoint; virtual; abstract;
     procedure Invalidate; virtual;
     procedure Invalidate; virtual;
@@ -121,6 +122,23 @@ begin
   InvalidateRect(aRect);
   InvalidateRect(aRect);
 end;
 end;
 
 
+function TFresnelWSForm.NumKeyToInputType(aKey : Integer; aShift: TShiftState; out aType : TFresnelInputType) : boolean;
+
+begin
+  // Return true if we have a correct input type
+  Result:=True;
+  case aKey of
+    TKeyCodes.BackSpace : aType:=fitDeleteContentBackward;
+    TKeyCodes.Delete : aType:=fitDeleteContentForward;
+  else
+    if aKey<0 then
+      Result:=False
+    else
+      aType:=fitInsertText
+  end;
+end;
+
+
 { TFresnelWidgetSet }
 { TFresnelWidgetSet }
 
 
 procedure TFresnelWidgetSet.SetOptions(AValue: TWidgetSetOptions);
 procedure TFresnelWidgetSet.SetOptions(AValue: TWidgetSetOptions);

+ 93 - 10
src/gtk3/fresnel.gtk3.pas

@@ -41,7 +41,9 @@ type
     FClientRect: TRect;
     FClientRect: TRect;
     FForm: TFresnelCustomForm;
     FForm: TFresnelCustomForm;
     FWindow: PGtkWindow;
     FWindow: PGtkWindow;
+    FIMContext : PGtkIMMulticontext;
     procedure SetForm(const AValue: TFresnelCustomForm);
     procedure SetForm(const AValue: TFresnelCustomForm);
+    procedure CreateIMContext;
   protected
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation);
     procedure Notification(AComponent: TComponent; Operation: TOperation);
       override;
       override;
@@ -51,9 +53,11 @@ type
     procedure SetFormBounds(const AValue: TFresnelRect); override;
     procedure SetFormBounds(const AValue: TFresnelRect); override;
     procedure SetCaption(AValue: TFresnelCaption); override;
     procedure SetCaption(AValue: TFresnelCaption); override;
     procedure SetVisible(const AValue: boolean); override;
     procedure SetVisible(const AValue: boolean); override;
+
   public
   public
     function GetClientSize: TFresnelPoint; override;
     function GetClientSize: TFresnelPoint; override;
     function GtkEventDraw(AContext: Pcairo_t): Boolean; virtual;
     function GtkEventDraw(AContext: Pcairo_t): Boolean; virtual;
+    function GtkIMECommit(aText : string) : Boolean;
     function GtkEventKeyDown(Event: PGdkEvent): Boolean; virtual;
     function GtkEventKeyDown(Event: PGdkEvent): Boolean; virtual;
     function GtkEventKeyUp(Event: PGdkEvent): Boolean; virtual;
     function GtkEventKeyUp(Event: PGdkEvent): Boolean; virtual;
     function GtkEventMouseXY(Event: PGdkEvent): Boolean; virtual; // MouseUp/Move/Down
     function GtkEventMouseXY(Event: PGdkEvent): Boolean; virtual; // MouseUp/Move/Down
@@ -74,6 +78,7 @@ type
     function CreateGtkWindow: PGtkWindow; virtual;
     function CreateGtkWindow: PGtkWindow; virtual;
     function GetWindowState: TGdkWindowState; virtual;
     function GetWindowState: TGdkWindowState; virtual;
     property Window: PGtkWindow read FWindow;
     property Window: PGtkWindow read FWindow;
+    property IMContext : PGtkIMMulticontext Read FIMContext;
 
 
     property Form: TFresnelCustomForm read FForm write SetForm;
     property Form: TFresnelCustomForm read FForm write SetForm;
   end;
   end;
@@ -112,6 +117,8 @@ function GdkModifierStateToShiftState(const AState: TGdkModifierType): TShiftSta
 
 
 implementation
 implementation
 
 
+uses UTF8Utils;
+
 var
 var
   ThreadSync_PipeIn, ThreadSync_PipeOut: cint;
   ThreadSync_PipeIn, ThreadSync_PipeOut: cint;
   ThreadSync_GIOChannel: PGIOChannel;
   ThreadSync_GIOChannel: PGIOChannel;
@@ -163,6 +170,19 @@ begin
   Result := aForm.GtkEvent_Event(Event);
   Result := aForm.GtkEvent_Event(Event);
 end;
 end;
 
 
+function Gtk3WidgetRealize(AWidget: PGtkWidget; Data: gpointer): gboolean; cdecl;
+
+var
+  lForm : TGtk3WSForm;
+
+begin
+  if AWidget=nil then ;
+  if Data = nil then exit;
+  lForm := TGtk3WSForm(Data);
+  lForm.CreateIMContext;
+  result:=true;
+end;
+
 procedure Gtk3WidgetHide({%H-}AWidget: PGtkWidget; Data: gpointer); cdecl;
 procedure Gtk3WidgetHide({%H-}AWidget: PGtkWidget; Data: gpointer); cdecl;
 var
 var
   aForm: TGtk3WSForm;
   aForm: TGtk3WSForm;
@@ -203,6 +223,25 @@ begin
   aForm.GtkEventSizeAllocate(AGdkRect);
   aForm.GtkEventSizeAllocate(AGdkRect);
 end;
 end;
 
 
+
+procedure Gtk3IMContextCommit(aContext: PGtkIMMulticontext; aStr : PAnsiChar; aData: gpointer);
+
+var
+  aForm: TGtk3WSForm;
+  lStr : String;
+
+begin
+  if aContext=nil then ;
+  if aData = nil then exit;
+  if astr=nil then exit;
+  lStr:=aStr;
+  aForm:=TGtk3WSForm(aData);
+  aForm.GtkIMECommit(lStr);
+end;
+
+
+
+
 function RectFromGdkRect(const AGdkRect: TGdkRectangle): TRect;
 function RectFromGdkRect(const AGdkRect: TGdkRectangle): TRect;
 begin
 begin
   with AGdkRect do
   with AGdkRect do
@@ -425,6 +464,20 @@ begin
     Include(Result,ssCtrl);
     Include(Result,ssCtrl);
 end;
 end;
 
 
+Function GDKKeyEventToKeyEventInit(Event: PGdkEvent) : TFresnelKeyEventInit;
+
+begin
+  Result:=Default(TFresnelKeyEventInit);
+  Result.ShiftState:=GdkModifierStateToShiftState(Event^.key.state);
+  // This will handle unicode.
+  Result.NumKey:=GdkKeyToFresnelKey(Event^.Key.keyval);
+  if Result.NumKey>0 then
+    Result.Key:=UnicodeToUTF8(Result.NumKey)
+  else
+    Result.Key:=TKeyCodes.Names[Result.NumKey];
+end;
+
+
 { TGtk3WSForm }
 { TGtk3WSForm }
 
 
 procedure TGtk3WSForm.SetFormBounds(const AValue: TFresnelRect);
 procedure TGtk3WSForm.SetFormBounds(const AValue: TFresnelRect);
@@ -463,6 +516,13 @@ begin
     FreeNotification(FForm);
     FreeNotification(FForm);
 end;
 end;
 
 
+procedure TGtk3WSForm.CreateIMContext;
+begin
+  FIMContext:=gtk_im_multicontext_new();
+  gtk_im_context_set_client_window(FIMContext, gtk_widget_get_window(FWindow));
+  g_signal_connect_data(FIMContext, 'commit', TGCallback(@Gtk3IMContextCommit), Self, Nil, G_CONNECT_DEFAULT);
+end;
+
 function TGtk3WSForm.GetVisible: boolean;
 function TGtk3WSForm.GetVisible: boolean;
 begin
 begin
   Result:=(FWindow^.window<>nil)
   Result:=(FWindow^.window<>nil)
@@ -472,6 +532,7 @@ end;
 procedure TGtk3WSForm.GtkEventDestroy;
 procedure TGtk3WSForm.GtkEventDestroy;
 begin
 begin
   FWindow:=Nil;
   FWindow:=Nil;
+  g_object_unref(FIMContext);
   FreeAndNil(FForm);
   FreeAndNil(FForm);
 end;
 end;
 
 
@@ -522,18 +583,40 @@ begin
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 
+function TGtk3WSForm.GtkIMECommit(aText: string): Boolean;
+
+var
+  lInput : TFresnelInputEventInit;
+
+begin
+  lInput:=Default(TFresnelInputEventInit);
+  lInput.InputType:=fitInsertText;
+  lInput.Data:=aText;
+  Result:=Form.WSInput(lInput);
+end;
+
 
 
 function TGtk3WSForm.GtkEventKeyDown(Event: PGdkEvent): Boolean;
 function TGtk3WSForm.GtkEventKeyDown(Event: PGdkEvent): Boolean;
 var
 var
   lInit : TFresnelKeyEventInit;
   lInit : TFresnelKeyEventInit;
+  lInput : TFresnelInputEventInit;
 
 
 begin
 begin
   if Event=nil then exit;
   if Event=nil then exit;
-  lInit:=Default(TFresnelKeyEventInit);
-  lInit.ShiftState:=GdkModifierStateToShiftState(Event^.key.state);
-  // This will handle unicode.
-  lInit.NumKey:=GdkKeyToFresnelKey(Event^.Key.keyval);
-  Form.WSKey(lInit,evtKeyDown);
+  if Assigned(FIMContext) then
+    if (gtk_im_context_filter_keypress(FIMContext,PGdkEventKey(Event))) then
+      Exit(FALSE);
+  lInit:=GDKKeyEventToKeyEventInit(Event);
+  if not Form.WSKey(lInit,evtKeyDown) then
+    begin
+    lInput:=Default(TFresnelInputEventInit);
+    if NumKeyToInputType(lInit.NumKey,lInit.ShiftState,lInput.InputType) then
+      begin
+      if (lInit.NumKey>0) then
+        lInput.Data:=UnicodeToUTF8(lInit.NumKey);
+      Form.WSInput(lInput);
+      end;
+    end;
   Result:=True;
   Result:=True;
 end;
 end;
 
 
@@ -543,11 +626,10 @@ var
 
 
 begin
 begin
   if Event=nil then exit;
   if Event=nil then exit;
-  lInit:=Default(TFresnelKeyEventInit);
-  lInit.ShiftState:=GdkModifierStateToShiftState(Event^.key.state);
-  // This will handle unicode.
-  lInit.NumKey:=GdkKeyToFresnelKey(Event^.Key.keyval);
-  lInit.Key := TKeyCodes.Names[lInit.NumKey];
+  if Assigned(FIMContext) then
+    if (gtk_im_context_filter_keypress(FIMContext,PGdkEventKey(Event))) then
+      Exit(FALSE);
+  lInit:=GDKKeyEventToKeyEventInit(Event);
   Form.WSKey(lInit,evtKeyUp);
   Form.WSKey(lInit,evtKeyUp);
   Result:=True;
   Result:=True;
 end;
 end;
@@ -795,6 +877,7 @@ begin
   g_signal_connect_data(Window,'destroy', TGCallback(@Gtk3WidgetDestroy), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'destroy', TGCallback(@Gtk3WidgetDestroy), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'draw', TGCallback(@Gtk3DrawWidget), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'draw', TGCallback(@Gtk3DrawWidget), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'event', TGCallback(@Gtk3WidgetEvent), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'event', TGCallback(@Gtk3WidgetEvent), Self, nil, G_CONNECT_DEFAULT);
+  g_signal_connect_data(Window,'realize', TGCallback(@Gtk3WidgetRealize), Self, nil, G_CONNECT_DEFAULT);
   //g_signal_connect_data(Window,'button-press-event', TGCallback(@Gtk3WidgetButtonPressEvent), Self, nil, G_CONNECT_DEFAULT);
   //g_signal_connect_data(Window,'button-press-event', TGCallback(@Gtk3WidgetButtonPressEvent), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'hide', TGCallback(@Gtk3WidgetHide), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'hide', TGCallback(@Gtk3WidgetHide), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'map', TGCallback(@Gtk3WidgetMap), Self, nil, G_CONNECT_DEFAULT);
   g_signal_connect_data(Window,'map', TGCallback(@Gtk3WidgetMap), Self, nil, G_CONNECT_DEFAULT);

+ 166 - 0
src/include/fresnel.wintofresnelkey.inc

@@ -0,0 +1,166 @@
+Function KeyCodeToUnicode(aKey,aFlags : DWord) : Word;
+
+var
+  lState : array[0..255] of byte;
+  lChars : array[0..1] of unicodechar; // Buffer for Unicode character (including null terminator)
+  lRes : Integer;
+//  lLayout : HKL;
+
+begin
+  if (aKey>VK_0) and (aKey<VK_Z) then
+    Result:=Ord(akey)
+  else
+    Result:=0;
+(*
+  Result:=0;
+  if (GetKeyboardState(@lState)<>0)  then
+    exit;
+  lLayout:=GetKeyboardLayout(0); // 0 means the current thread's input locale.
+  lRes:=ToUnicodeEx(
+        aKey,          // Virtual-key code
+        ((aFlags shr 16) and $FF), // Scan code
+        @lState,   // Keyboard-state array
+        @lChars,     // Buffer for translated character
+        2,               // Size of buffer
+        1,               // Flags (1 means translate dead keys)
+        lLayout  // Current keyboard layout
+  );
+  if (lRes=1) then
+    Result:=Ord(lChars[0]); // Return the Unicode character
+  else if (lRes = -1) then
+    begin
+    // Dead-key character; ToUnicodeEx will have stored the dead key state.
+    // So we must call ToUnicodeEx again with a space to get the composed character.
+    lRes:=ToUnicodeEx(
+        VK_SPACE,
+        MapVirtualKeyEx(VK_SPACE, MAPVK_VK_TO_VSC, lLayout),
+        @lState,
+        @lChars,
+        2,
+        1,
+        lLayout
+    );
+    if (res=1) then
+      Result:=Ord(lChars([0])); // Return the composed character.
+    end;
+*)
+end;
+
+Function WinKeyToFresnelKey(aKey,aFlags : DWord) : Integer;
+
+begin
+  Result:=KeyCodeToUnicode(aKey,aFlags);
+  if Result<>0 then
+    exit;
+  Case aKey of
+    VK_BACK : Result:=TKeyCodes.BackSpace;
+    VK_TAB : Result:=TKeyCodes.Tab;
+    VK_CLEAR : Result:=TKeyCodes.Clear;
+    VK_RETURN : Result:=TKeyCodes.Enter;
+    VK_LSHIFT,
+    VK_RSHIFT,
+    VK_SHIFT : Result:=TKeyCodes.Shift;
+    VK_LCONTROL,
+    VK_RCONTROL,
+    VK_CONTROL 	: Result:=TKeyCodes.Control;
+    VK_LMENU,
+    VK_RMENU,
+    VK_MENU 	: Result:=TKeyCodes.Alt;
+    VK_PAUSE 	: Result:=TKeyCodes.Pause;
+    VK_CAPITAL 	: Result:=TKeyCodes.CapsLock;
+    VK_KANA 	: Result:=TKeyCodes.KanaMode;
+//    VK_HANGUL 	: Result:=TKeyCodes.HangulMode;
+    VK_JUNJA 	: Result:=TKeyCodes.JunjaMode;
+    VK_FINAL 	: Result:=TKeyCodes.FinalMode;
+    VK_HANJA 	: Result:=TKeyCodes.HanjaMode;
+//    VK_KANJI 	: Result:=TKeyCodes.KanjiMode;
+    VK_ESCAPE 	: Result:=TKeyCodes.Escape;
+    VK_CONVERT 	: Result:=TKeyCodes.Convert ;
+    VK_NONCONVERT 	: Result:=TKeyCodes.NonConvert;
+    VK_ACCEPT 	: Result:=TKeyCodes.Accept ;
+    VK_MODECHANGE 	: Result:=TKeyCodes.ModeChange;
+    VK_SPACE 	: Result:=Ord(' ');
+    VK_PRIOR 	: Result:=TKeyCodes.PageUp;
+    VK_NEXT 	: Result:=TKeyCodes.PageDown;
+    VK_END 	: Result:=TKeyCodes.End_;
+    VK_HOME 	: Result:=TKeyCodes.Home;
+    VK_LEFT 	: Result:=TKeyCodes.ArrowLeft;
+    VK_UP 	: Result:=TKeyCodes.ArrowUp;
+    VK_RIGHT 	: Result:=TKeyCodes.ArrowRight;
+    VK_DOWN 	: Result:=TKeyCodes.ArrowDown;
+    VK_SELECT 	: Result:=TKeyCodes.Select ;
+    VK_EXECUTE 	: Result:=TKeyCodes.Execute ;
+    VK_SNAPSHOT 	: Result:=TKeyCodes.PrintScreen ;
+    VK_INSERT 	: Result:=TKeyCodes.Insert ;
+    VK_DELETE 	: Result:=TKeyCodes.Delete ;
+    VK_HELP 	: Result:=TKeyCodes.Help ;
+    VK_LWIN 	: Result:=TKeyCodes.Meta ;
+    VK_RWIN 	: Result:=TKeyCodes.Meta ;
+    VK_APPS 	: Result:=TKeyCodes.Contextmenu ;
+    VK_SLEEP 	: Result:=TKeyCodes.StandBy ;
+    VK_NUMPAD0 	: Result:=Ord('0');
+    VK_NUMPAD1 	: Result:=Ord('1')+(aKey-VK_NUMPAD1);
+    VK_MULTIPLY 	: Result:=TKeyCodes.Multiply ;
+    VK_ADD 	: Result:=TKeyCodes.Add ;
+    VK_SEPARATOR 	: Result:=TKeyCodes.Separator ;
+    VK_SUBTRACT 	: Result:=TKeyCodes.Subtract ;
+    VK_DECIMAL 	: Result:=TKeyCodes.Decimal ;
+    VK_DIVIDE 	: Result:=TKeyCodes.Divide ;
+    VK_F1..VK_F24 	: Result:=TKeyCodes.F1+(aKey-VK_F1);
+    VK_NUMLOCK 	: Result:=TKeyCodes.NumLock ;
+    VK_SCROLL 	: Result:=TKeyCodes.ScrollLock ;
+    VK_BROWSER_BACK 	: Result:=TKeyCodes.BrowserBack;
+    VK_BROWSER_FORWARD 	: Result:=TKeyCodes.BrowserForward;
+    VK_BROWSER_REFRESH 	: Result:=TKeyCodes.BrowserRefresh;
+    VK_BROWSER_STOP 	: Result:=TKeyCodes.BrowserStop;
+    VK_BROWSER_SEARCH 	: Result:=TKeyCodes.Browsersearch;
+    VK_BROWSER_FAVORITES 	: Result:=TKeyCodes.BrowserFavorites;
+    VK_BROWSER_HOME 	: Result:=TKeyCodes.BrowserHome;
+    VK_VOLUME_MUTE 	: Result:=TKeyCodes.AudioVolumeMute;
+    VK_VOLUME_DOWN 	: Result:=TKeyCodes.AudioVolumeDown;
+    VK_VOLUME_UP 	: Result:=TKeyCodes.AudioVolumeUp;
+    VK_MEDIA_NEXT_TRACK 	: Result:=TKeyCodes.MediaTrackNext;
+    VK_MEDIA_PREV_TRACK 	: Result:=TKeyCodes.MediaTrackPrevious;
+    VK_MEDIA_STOP 	: Result:=TKeyCodes.MediaStop;
+    VK_MEDIA_PLAY_PAUSE 	: Result:=TKeyCodes.MediaPlayPause;
+    VK_LAUNCH_MAIL 	: Result:=TKeyCodes.LaunchMail ;
+    VK_LAUNCH_MEDIA_SELECT 	: Result:=TKeyCodes.LaunchMediaPlayer ;
+    VK_LAUNCH_APP1 	: Result:=TKeyCodes.LaunchApplication1 ;
+    VK_LAUNCH_APP2 	: Result:=TKeyCodes.LaunchApplication2 ;
+    VK_PROCESSKEY 	: Result:=TKeyCodes.Process ;
+    VK_ATTN 	: Result:=TKeyCodes.KanaMode ;
+    VK_CRSEL 	: Result:=TKeyCodes.CrSel ;
+    VK_EXSEL 	: Result:=TKeyCodes.ExSel ;
+    VK_EREOF 	: Result:=TKeyCodes.EraseEof ;
+    VK_PLAY 	: Result:=TKeyCodes.Play ;
+    VK_ZOOM 	: Result:=TKeyCodes.ZoomToggle ;
+    VK_OEM_CLEAR 	: Result:=TKeyCodes.Clear ;
+  else
+    Result:=0;
+  { // Not supported
+    // VK_CANCEL :      : Result:=TKeyCodes. ;
+    // VK_IME_ON 	: Result:=TKeyCodes. ;
+    // VK_IME_OFF 	: Result:=TKeyCodes. ;
+    // VK_PRINT 	: Result:=TKeyCodes. ;
+    // VK_PACKET 	: Result:=TKeyCodes. ;
+    // VK_NONAME 	: Result:=TKeyCodes. ;
+    // VK_PA1 	        : Result:=TKeyCodes. ;
+    // VK_OEM_1 	: Result:=TKeyCodes. ;
+    // VK_OEM_PLUS 	: Result:=TKeyCodes. ;
+    // VK_OEM_COMMA 	: Result:=TKeyCodes. ;
+    // VK_OEM_MINUS 	: Result:=TKeyCodes. ;
+    // VK_OEM_PERIOD 	: Result:=TKeyCodes. ;
+    // VK_OEM_2 	: Result:=TKeyCodes. ;
+    // VK_OEM_3 	: Result:=TKeyCodes. ;
+    // VK_OEM_4 	: Result:=TKeyCodes. ;
+    // VK_OEM_5 	: Result:=TKeyCodes. ;
+    // VK_OEM_6 	: Result:=TKeyCodes. ;
+    // VK_OEM_7 	: Result:=TKeyCodes. ;
+    // VK_OEM_8 	: Result:=TKeyCodes. ;
+    // VK_OEM_102 	: Result:=TKeyCodes. ;
+
+  }
+  end;
+end;
+
+

+ 60 - 1
src/lcl/fresnel.lcl.pas

@@ -8,7 +8,7 @@ unit Fresnel.LCL;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, Types, Math, FPImage,
+  Classes, SysUtils, Types, Math, FPImage, lclType, fresnel.keys,
   AvgLvlTree, LazLoggerBase, GraphType,
   AvgLvlTree, LazLoggerBase, GraphType,
   Graphics, Controls, LCLIntf, Forms, LResources, IntfGraphics,
   Graphics, Controls, LCLIntf, Forms, LResources, IntfGraphics,
   Fresnel.Classes, Fresnel.Events, Fresnel.DOM,
   Fresnel.Classes, Fresnel.Events, Fresnel.DOM,
@@ -119,12 +119,15 @@ type
     FIntfImg: TLazIntfImage;
     FIntfImg: TLazIntfImage;
     {$ENDIF}
     {$ENDIF}
     procedure LCLChangeBounds(Sender: TObject);
     procedure LCLChangeBounds(Sender: TObject);
+    procedure LCLKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
     procedure LCLMouseDown(Sender: TObject; Button: Controls.TMouseButton;
     procedure LCLMouseDown(Sender: TObject; Button: Controls.TMouseButton;
       Shift: TShiftState; X, Y: Integer);
       Shift: TShiftState; X, Y: Integer);
     procedure LCLMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
     procedure LCLMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
     procedure LCLMouseUp(Sender: TObject; Button: Controls.TMouseButton;
     procedure LCLMouseUp(Sender: TObject; Button: Controls.TMouseButton;
       Shift: TShiftState; X, Y: Integer);
       Shift: TShiftState; X, Y: Integer);
     procedure LCLPaint(Sender: TObject);
     procedure LCLPaint(Sender: TObject);
+    procedure LCLKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
+    procedure LCLUTF8Key(Sender: TObject; var UTF8Key: TUTF8Char);
     procedure SetFresnelForm(const AValue: TFresnelCustomForm);
     procedure SetFresnelForm(const AValue: TFresnelCustomForm);
   protected
   protected
     function GetCaption: TFresnelCaption; override;
     function GetCaption: TFresnelCaption; override;
@@ -175,6 +178,8 @@ procedure FresnelPointsToPoints(const Src: TFresnelPointArray; out Dest: TPointA
 
 
 implementation
 implementation
 
 
+uses utf8utils;
+
 function CompareFresnelLCLFont(Item1, Item2: Pointer): integer;
 function CompareFresnelLCLFont(Item1, Item2: Pointer): integer;
 var
 var
   Font1: TFresnelLCLFont absolute Item1;
   Font1: TFresnelLCLFont absolute Item1;
@@ -347,6 +352,7 @@ var
   ts: TTextStyle;
   ts: TTextStyle;
   aFresnelFont: TFresnelLCLFont;
   aFresnelFont: TFresnelLCLFont;
 begin
 begin
+  Canvas.Brush.Style:=bsClear;
   aFresnelFont:=aFont.GetTool as TFresnelLCLFont;
   aFresnelFont:=aFont.GetTool as TFresnelLCLFont;
   Canvas.Font:=aFresnelFont.LCLFont;
   Canvas.Font:=aFresnelFont.LCLFont;
   Canvas.Font.Color:=FPColorToTColor(aColor);
   Canvas.Font.Color:=FPColorToTColor(aColor);
@@ -427,6 +433,7 @@ begin
   FresnelForm.WSResize(GetFormBounds,LCLForm.ClientWidth,LCLForm.ClientHeight);
   FresnelForm.WSResize(GetFormBounds,LCLForm.ClientWidth,LCLForm.ClientHeight);
 end;
 end;
 
 
+
 procedure TLCLWSForm.LCLMouseMove(Sender: TObject; Shift: TShiftState; X,
 procedure TLCLWSForm.LCLMouseMove(Sender: TObject; Shift: TShiftState; X,
   Y: Integer);
   Y: Integer);
 var
 var
@@ -504,6 +511,55 @@ begin
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 
+{$i fresnel.wintofresnelkey.inc}
+
+procedure TLCLWSForm.LCLKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+
+var
+  lInfo : TFresnelKeyEventInit;
+  lInput : TFresnelInputEventInit;
+
+begin
+  lInfo:=Default(TFresnelKeyEventInit);
+  lInfo.ShiftState:=Shift;
+  lInfo.NumKey:=WinKeyToFresnelKey(Key,0);
+  if not FresnelForm.WSKey(lInfo,evtKeyDown) then
+    if (lInfo.NumKey<0) then
+      begin
+      lInput:=Default(TFresnelInputEventInit);
+      if NumKeyToInputType(lInfo.NumKey,lInfo.ShiftState,lInput.InputType) then
+        FresnelForm.WSInput(lInput);
+      end;
+end;
+
+procedure TLCLWSForm.LCLKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
+
+var
+  lInfo : TFresnelKeyEventInit;
+
+begin
+  lInfo:=Default(TFresnelKeyEventInit);
+  lInfo.ShiftState:=Shift;
+  lInfo.NumKey:=WinKeyToFresnelKey(Key,0);
+  FresnelForm.WSKey(lInfo,evtKeyUp);
+end;
+
+procedure TLCLWSForm.LCLUTF8Key(Sender: TObject; var UTF8Key: TUTF8Char);
+
+var
+  lInfo : TFresnelInputEventInit;
+
+begin
+  lInfo:=Default(TFresnelInputEventInit);
+  case UTF8Key of
+    #8: Exit; // Already handled in keydown.
+  else
+    lInfo.Data:=UTF8Key;
+    lInfo.InputType:=fitInsertText;
+  end;
+  FresnelForm.WSInput(lInfo);
+end;
+
 function TLCLWSForm.GetFormBounds: TFresnelRect;
 function TLCLWSForm.GetFormBounds: TFresnelRect;
 begin
 begin
   Result.SetRect(LCLForm.BoundsRect);
   Result.SetRect(LCLForm.BoundsRect);
@@ -616,6 +672,9 @@ begin
   FLCLForm.OnMouseUp:=@LCLMouseUp;
   FLCLForm.OnMouseUp:=@LCLMouseUp;
   FLCLForm.OnPaint:=@LCLPaint;
   FLCLForm.OnPaint:=@LCLPaint;
   FLCLForm.OnChangeBounds:=@LCLChangeBounds;
   FLCLForm.OnChangeBounds:=@LCLChangeBounds;
+  FLCLForm.OnKeyDown:=@LCLKeyDown;
+  FLCLForm.OnKeyUp:=@LCLKeyUp;
+  FLCLForm.OnUTF8KeyPress:=@LCLUTF8Key;
 end;
 end;
 
 
 { TFresnelLCLWidgetSet }
 { TFresnelLCLWidgetSet }

+ 8 - 2
src/lcl/fresnellcl.lpk

@@ -6,6 +6,7 @@
     <CompilerOptions>
     <CompilerOptions>
       <Version Value="11"/>
       <Version Value="11"/>
       <SearchPaths>
       <SearchPaths>
+        <IncludeFiles Value="../include"/>
         <OtherUnitFiles Value="../skia;../skia/skia4delphi"/>
         <OtherUnitFiles Value="../skia;../skia/skia4delphi"/>
         <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
         <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
       </SearchPaths>
       </SearchPaths>
@@ -33,6 +34,7 @@ end;"/>
             <Item1 Value="skia"/>
             <Item1 Value="skia"/>
             <Item2 Value="lcl"/>
             <Item2 Value="lcl"/>
           </Values>
           </Values>
+          <ValueDescriptions Count="2"/>
         </Item1>
         </Item1>
       </BuildMacros>
       </BuildMacros>
     </CompilerOptions>
     </CompilerOptions>
@@ -40,7 +42,7 @@ end;"/>
     <License Value="Same as LCL.
     <License Value="Same as LCL.
 Modified LGPL-2."/>
 Modified LGPL-2."/>
     <Version Minor="3"/>
     <Version Minor="3"/>
-    <Files Count="8">
+    <Files Count="9">
       <Item1>
       <Item1>
         <Filename Value="fresnel.lcl.pas"/>
         <Filename Value="fresnel.lcl.pas"/>
         <UnitName Value="Fresnel.LCL"/>
         <UnitName Value="Fresnel.LCL"/>
@@ -59,7 +61,7 @@ Modified LGPL-2."/>
       </Item4>
       </Item4>
       <Item5>
       <Item5>
         <Filename Value="fresnel.lclevents.pas"/>
         <Filename Value="fresnel.lclevents.pas"/>
-        <UnitName Value="fresnel.lclevents"/>
+        <UnitName Value="Fresnel.LCLEvents"/>
       </Item5>
       </Item5>
       <Item6>
       <Item6>
         <Filename Value="../skia/fresnel.skiarenderer.pas"/>
         <Filename Value="../skia/fresnel.skiarenderer.pas"/>
@@ -76,6 +78,10 @@ Modified LGPL-2."/>
         <AddToUsesPkgSection Value="False"/>
         <AddToUsesPkgSection Value="False"/>
         <UnitName Value="System.Skia"/>
         <UnitName Value="System.Skia"/>
       </Item8>
       </Item8>
+      <Item9>
+        <Filename Value="../include/fresnel.wintofresnelkey.inc"/>
+        <Type Value="Binary"/>
+      </Item9>
     </Files>
     </Files>
     <CompatibilityMode Value="True"/>
     <CompatibilityMode Value="True"/>
     <RequiredPkgs Count="2">
     <RequiredPkgs Count="2">