
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2684 8e941d3f-bd1b-0410-a28a-d453659cc2b4
496 lines
16 KiB
ObjectPascal
496 lines
16 KiB
ObjectPascal
{ iOS NOB-designer for the Lazarus IDE
|
|
|
|
Copyright (C) 2012 Joost van der Sluis joost@cnoc.nl
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version with the following modification:
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent modules,and
|
|
to copy and distribute the resulting executable under terms of your choice,
|
|
provided that you also meet, for each linked independent module, the terms
|
|
and conditions of the license of that module. An independent module is a
|
|
module which is not derived from or based on this library. If you modify
|
|
this library, you may extend this exception to your version of the library,
|
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
}
|
|
|
|
unit iOSNIBDesigner;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$typeinfo off}
|
|
|
|
interface
|
|
|
|
uses
|
|
LCLProc, LCLType, Classes, SysUtils, FormEditingIntf, LCLIntf, Graphics, propedits, CodeToolManager,
|
|
ProjectIntf,
|
|
iOS_Views,
|
|
dom,
|
|
IDEIntf,
|
|
IDEWindowIntf,
|
|
LazIDEIntf,
|
|
Dialogs,
|
|
Controls,
|
|
ComponentReg,
|
|
typinfo,
|
|
forms;
|
|
|
|
type
|
|
|
|
{ TNSObjectDesignerMediator }
|
|
|
|
TNSObjectDesignerMediator = class(TDesignerMediator,IMyWidgetDesigner)
|
|
private
|
|
FMyForm: NSObject;
|
|
public
|
|
function UseRTTIForMethods(aComponent: TComponent): boolean; override;
|
|
// needed by the lazarus form editor
|
|
class function CreateMediator(TheOwner, aForm: TComponent): TDesignerMediator; override;
|
|
class function FormClass: TComponentClass; override;
|
|
procedure GetBounds(AComponent: TComponent; out CurBounds: TRect); override;
|
|
procedure SetBounds(AComponent: TComponent; NewBounds: TRect); override;
|
|
procedure GetClientArea(AComponent: TComponent; out CurClientArea: TRect; out ScrollOffset: TPoint); override;
|
|
procedure Paint; override;
|
|
function ComponentIsIcon(AComponent: TComponent): boolean; override;
|
|
function ParentAcceptsChild(Parent: TComponent; Child: TComponentClass): boolean; override;
|
|
procedure InitComponent(AComponent, NewParent: TComponent; NewBounds: TRect); override;
|
|
function CreateComponent(ParentComp: TComponent;
|
|
TypeClass: TComponentClass;
|
|
const AUnitName: shortstring;
|
|
X,Y,W,H: Integer;
|
|
DisableAutoSize: boolean): TComponent;
|
|
public
|
|
// needed by UIView
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure InvalidateRect(Sender: TObject; ARect: TRect; Erase: boolean);
|
|
property MyForm: NSObject read FMyForm;
|
|
end;
|
|
|
|
{ TUIResponderDesignerMediator }
|
|
|
|
TUIResponderDesignerMediator = class(TNSObjectDesignerMediator)
|
|
public
|
|
class function FormClass: TComponentClass; override;
|
|
end;
|
|
|
|
|
|
{ TiOSMethodPropertyEditor }
|
|
|
|
TiOSMethodPropertyEditor = class(TMethodPropertyEditor)
|
|
public
|
|
procedure GetValues(Proc: TGetStrProc); override;
|
|
procedure SetValue(const NewValue: ansistring); override;
|
|
end;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
uses
|
|
ObjInspStrConsts;
|
|
|
|
type
|
|
|
|
{ TiOSEventHandlers }
|
|
|
|
TiOSEventHandlers = class
|
|
private
|
|
FUpdateVisibleHandlerSet: boolean;
|
|
FUpdateVisibleHandlerDesignerSet: boolean;
|
|
public
|
|
constructor create;
|
|
destructor destroy;
|
|
procedure ChangeLookupRoot;
|
|
procedure HandlerUpdateVisible(AComponent: TRegisteredComponent; var VoteVisible: integer);
|
|
procedure HandlerUpdateVisibleDesigner(AComponent: TRegisteredComponent; var VoteVisible: integer);
|
|
end;
|
|
|
|
var
|
|
GiOSEventHandlers: TiOSEventHandlers = nil;
|
|
|
|
procedure Register;
|
|
|
|
procedure SetFakeUnitname(AClass: TClass);
|
|
var
|
|
ATypInfo: PTypeInfo;
|
|
ATypData: PTypeData;
|
|
begin
|
|
ATypInfo:=PTypeInfo(AClass.ClassInfo);
|
|
ATypData:=GetTypeData(ATypInfo);
|
|
|
|
ATypData^.UnitName[1]:='i';
|
|
ATypData^.UnitName[2]:='P';
|
|
ATypData^.UnitName[3]:='h';
|
|
ATypData^.UnitName[4]:='o';
|
|
ATypData^.UnitName[5]:='n';
|
|
ATypData^.UnitName[6]:='e';
|
|
ATypData^.UnitName[7]:='A';
|
|
ATypData^.UnitName[8]:='l';
|
|
ATypData^.UnitName[9]:='l';
|
|
end;
|
|
|
|
begin
|
|
FormEditingHook.RegisterDesignerMediator(TNSObjectDesignerMediator);
|
|
FormEditingHook.RegisterDesignerMediator(TUIResponderDesignerMediator);
|
|
RegisterComponents('iOS',[UIWindow,UINavigationController,UIButton,UILabel,UITextField,UITableView,UISearchBar,UIView,UIViewController, UIProgressView]);
|
|
|
|
GiOSEventHandlers := TiOSEventHandlers.Create;
|
|
|
|
RegisterClass(UINavigationItem);
|
|
RegisterClass(UIViewController);
|
|
RegisterClass(UINavigationBar);
|
|
|
|
RegisterPropertyEditor(FindPropInfo(UIButton, 'onTouchDown')^.PropType , tiOSFakeComponent,'onTouchDown',TiOSMethodPropertyEditor);
|
|
|
|
// This is a hack to overwrite the unitname RTTI-information of these objects.
|
|
// This is to make sure that the Codetools add the right unit-name to the
|
|
// source when an object is added to the NIB-file.
|
|
SetFakeUnitname(UIButton);
|
|
SetFakeUnitname(UILabel);
|
|
SetFakeUnitname(UITextField);
|
|
SetFakeUnitname(UITableView);
|
|
SetFakeUnitname(UISearchBar);
|
|
SetFakeUnitname(UIWindow);
|
|
SetFakeUnitname(UIView);
|
|
SetFakeUnitname(UINavigationController);
|
|
SetFakeUnitname(UIViewController);
|
|
SetFakeUnitname(UIProgressView);
|
|
end;
|
|
|
|
{ TiOSEventHandlers }
|
|
|
|
constructor TiOSEventHandlers.create;
|
|
begin
|
|
GlobalDesignHook.AddHandlerChangeLookupRoot(@ChangeLookupRoot);
|
|
end;
|
|
|
|
destructor TiOSEventHandlers.destroy;
|
|
begin
|
|
GlobalDesignHook.RemoveAllHandlersForObject(self);
|
|
end;
|
|
|
|
procedure TiOSEventHandlers.ChangeLookupRoot;
|
|
begin
|
|
if GlobalDesignHook.LookupRoot is tiOSFakeComponent then
|
|
begin
|
|
if not FUpdateVisibleHandlerDesignerSet then
|
|
begin
|
|
IDEComponentPalette.AddHandlerUpdateVisible(@GiOSEventHandlers.HandlerUpdateVisibleDesigner);
|
|
FUpdateVisibleHandlerDesignerSet := true;
|
|
end;
|
|
if FUpdateVisibleHandlerSet then
|
|
begin
|
|
IDEComponentPalette.RemoveHandlerUpdateVisible(@GiOSEventHandlers.HandlerUpdateVisible);
|
|
FUpdateVisibleHandlerSet := false;
|
|
end;
|
|
end
|
|
else if assigned(GlobalDesignHook.LookupRoot) then
|
|
begin
|
|
if FUpdateVisibleHandlerDesignerSet then
|
|
begin
|
|
IDEComponentPalette.RemoveHandlerUpdateVisible(@GiOSEventHandlers.HandlerUpdateVisibleDesigner);
|
|
FUpdateVisibleHandlerDesignerSet := False;
|
|
end;
|
|
if not FUpdateVisibleHandlerSet then
|
|
begin
|
|
IDEComponentPalette.AddHandlerUpdateVisible(@GiOSEventHandlers.HandlerUpdateVisible);
|
|
FUpdateVisibleHandlerSet := true;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if FUpdateVisibleHandlerDesignerSet then
|
|
begin
|
|
IDEComponentPalette.RemoveHandlerUpdateVisible(@GiOSEventHandlers.HandlerUpdateVisibleDesigner);
|
|
FUpdateVisibleHandlerDesignerSet := False;
|
|
end;
|
|
if FUpdateVisibleHandlerSet then
|
|
begin
|
|
IDEComponentPalette.RemoveHandlerUpdateVisible(@GiOSEventHandlers.HandlerUpdateVisible);
|
|
FUpdateVisibleHandlerSet := false;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TiOSEventHandlers.HandlerUpdateVisible(
|
|
AComponent: TRegisteredComponent; var VoteVisible: integer);
|
|
begin
|
|
if assigned(AComponent) and assigned(AComponent.ComponentClass) and AComponent.ComponentClass.InheritsFrom(tiOSFakeComponent) then
|
|
dec(VoteVisible);
|
|
end;
|
|
|
|
procedure TiOSEventHandlers.HandlerUpdateVisibleDesigner(
|
|
AComponent: TRegisteredComponent; var VoteVisible: integer);
|
|
begin
|
|
if not AComponent.ComponentClass.InheritsFrom(tiOSFakeComponent) then
|
|
dec(VoteVisible);
|
|
end;
|
|
|
|
{ TiOSMethodPropertyEditor }
|
|
|
|
procedure TiOSMethodPropertyEditor.GetValues(Proc: TGetStrProc);
|
|
begin
|
|
proc(oisNone);
|
|
end;
|
|
|
|
procedure TiOSMethodPropertyEditor.SetValue(const NewValue: ansistring);
|
|
var
|
|
CreateNewMethod: Boolean;
|
|
CurValue: string;
|
|
NewMethodExists, NewMethodIsCompatible, NewMethodIsPublished,
|
|
NewIdentIsMethod: boolean;
|
|
IsNil: Boolean;
|
|
NewMethod: TMethod;
|
|
begin
|
|
CurValue := GetValue;
|
|
if CurValue = NewValue then exit;
|
|
//DebugLn('### TMethodPropertyEditor.SetValue A OldValue="',CurValue,'" NewValue=',NewValue);
|
|
IsNil := (NewValue='') or (NewValue=oisNone);
|
|
|
|
if (not IsNil) and (not IsValidIdent(NewValue)) then
|
|
begin
|
|
MessageDlg(oisIncompatibleIdentifier,
|
|
Format(oisIsNotAValidMethodName,['"',NewValue,'"']), mtError,
|
|
[mbCancel, mbIgnore], 0);
|
|
exit;
|
|
end;
|
|
|
|
NewMethodExists := (not IsNil); {and
|
|
PropertyHook.CompatibleMethodExists(NewValue, GetInstProp,
|
|
NewMethodIsCompatible, NewMethodIsPublished, NewIdentIsMethod);}
|
|
//DebugLn('### TMethodPropertyEditor.SetValue B NewMethodExists=',NewMethodExists,' NewMethodIsCompatible=',NewMethodIsCompatible,' ',NewMethodIsPublished,' ',NewIdentIsMethod);
|
|
{ if NewMethodExists then
|
|
begin
|
|
if not NewIdentIsMethod then
|
|
begin
|
|
if MessageDlg(oisIncompatibleIdentifier,
|
|
Format(oisTheIdentifierIsNotAMethodPressCancelToUndoPressIgn,
|
|
['"', NewValue, '"', LineEnding, LineEnding]),
|
|
mtWarning, [mbCancel, mbIgnore], 0)<>mrIgnore
|
|
then
|
|
exit;
|
|
end;
|
|
if not NewMethodIsPublished then
|
|
begin
|
|
if MessageDlg(oisIncompatibleMethod,
|
|
Format(oisTheMethodIsNotPublishedPressCancelToUndoPressIgnor,
|
|
['"', NewValue, '"', LineEnding, LineEnding]),
|
|
mtWarning, [mbCancel, mbIgnore], 0)<>mrIgnore
|
|
then
|
|
exit;
|
|
end;
|
|
if not NewMethodIsCompatible then
|
|
begin
|
|
if MessageDlg(oisIncompatibleMethod,
|
|
Format(oisTheMethodIsIncompatibleToThisEventPressCancelToUnd,
|
|
['"', NewValue, '"', GetName, LineEnding, LineEnding]),
|
|
mtWarning, [mbCancel, mbIgnore], 0)<>mrIgnore
|
|
then
|
|
exit;
|
|
end;
|
|
end; }
|
|
//DebugLn('### TMethodPropertyEditor.SetValue C');
|
|
if IsNil then
|
|
begin
|
|
NewMethod.Data := nil;
|
|
NewMethod.Code := nil;
|
|
SetMethodValue(NewMethod);
|
|
end
|
|
else
|
|
if IsValidIdent(CurValue) and
|
|
not NewMethodExists and
|
|
not PropertyHook.MethodFromAncestor(GetMethodValue) then
|
|
begin
|
|
// rename the method
|
|
// Note:
|
|
// All other not selected properties that use this method, contain just
|
|
// the TMethod record. So, changing the name in the jitform will change
|
|
// all other event names in all other components automatically.
|
|
PropertyHook.RenameMethod(CurValue, NewValue)
|
|
end else
|
|
begin
|
|
//DebugLn('### TMethodPropertyEditor.SetValue E');
|
|
CreateNewMethod := not NewMethodExists;
|
|
SetMethodValue(
|
|
PropertyHook.CreateMethod(NewValue, GetPropType,
|
|
GetComponent(0), GetPropertyPath(0)));
|
|
//DebugLn('### TMethodPropertyEditor.SetValue F NewValue=',GetValue);
|
|
if CreateNewMethod then
|
|
begin
|
|
//DebugLn('### TMethodPropertyEditor.SetValue G');
|
|
PropertyHook.ShowMethod(NewValue);
|
|
end;
|
|
end;
|
|
//DebugLn('### TMethodPropertyEditor.SetValue END NewValue=',GetValue);
|
|
end;
|
|
|
|
{ TNSObjectDesignerMediator }
|
|
|
|
constructor TNSObjectDesignerMediator.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
end;
|
|
|
|
destructor TNSObjectDesignerMediator.Destroy;
|
|
begin
|
|
if FMyForm<>nil then FMyForm.Designer:=nil;
|
|
FMyForm:=nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TNSObjectDesignerMediator.UseRTTIForMethods(aComponent: TComponent): boolean;
|
|
begin
|
|
if aComponent is tiOSFakeComponent then
|
|
result := true
|
|
else
|
|
Result:=inherited UseRTTIForMethods(aComponent);
|
|
end;
|
|
|
|
class function TNSObjectDesignerMediator.CreateMediator(TheOwner, aForm: TComponent): TDesignerMediator;
|
|
var
|
|
Mediator: TNSObjectDesignerMediator;
|
|
begin
|
|
Result:=inherited CreateMediator(TheOwner,aForm);
|
|
Mediator:=TNSObjectDesignerMediator(Result);
|
|
Mediator.FMyForm:=aForm as NSObject;
|
|
Mediator.FMyForm.Designer:=Mediator;
|
|
end;
|
|
|
|
class function TNSObjectDesignerMediator.FormClass: TComponentClass;
|
|
begin
|
|
Result:=NSObject;
|
|
end;
|
|
|
|
procedure TNSObjectDesignerMediator.GetBounds(AComponent: TComponent; out CurBounds: TRect);
|
|
var
|
|
w: tiOSFakeComponent;
|
|
begin
|
|
if AComponent is tiOSFakeComponent then begin
|
|
w:=tiOSFakeComponent(AComponent);
|
|
CurBounds:=Bounds(w.Left,w.Top,w.Width,w.Height);
|
|
end else
|
|
inherited GetBounds(AComponent,CurBounds);
|
|
end;
|
|
|
|
procedure TNSObjectDesignerMediator.InvalidateRect(Sender: TObject; ARect: TRect; Erase: boolean);
|
|
begin
|
|
if (LCLForm=nil) or (not LCLForm.HandleAllocated) then exit;
|
|
LCLIntf.InvalidateRect(LCLForm.Handle,@ARect,Erase);
|
|
end;
|
|
|
|
procedure TNSObjectDesignerMediator.SetBounds(AComponent: TComponent; NewBounds: TRect);
|
|
begin
|
|
if AComponent is tiOSFakeComponent then begin
|
|
tiOSFakeComponent(AComponent).SetBounds(NewBounds.Left,NewBounds.Top,
|
|
NewBounds.Right-NewBounds.Left,NewBounds.Bottom-NewBounds.Top);
|
|
end else
|
|
inherited SetBounds(AComponent,NewBounds);
|
|
end;
|
|
|
|
procedure TNSObjectDesignerMediator.GetClientArea(AComponent: TComponent; out CurClientArea: TRect; out ScrollOffset: TPoint);
|
|
var
|
|
Widget: tiOSFakeComponent;
|
|
begin
|
|
if AComponent is tiOSFakeComponent then begin
|
|
Widget:=tiOSFakeComponent(AComponent);
|
|
CurClientArea:=Rect(0,0,
|
|
Widget.Width,
|
|
Widget.Height);
|
|
ScrollOffset:=Point(0,0);
|
|
end else
|
|
inherited GetClientArea(AComponent, CurClientArea, ScrollOffset);
|
|
end;
|
|
|
|
procedure TNSObjectDesignerMediator.Paint;
|
|
|
|
procedure PaintWidget(AWidget: tiOSFakeComponent);
|
|
var
|
|
i: Integer;
|
|
Child: tiOSFakeComponent;
|
|
begin
|
|
with LCLForm.Canvas do
|
|
begin
|
|
SaveHandleState;
|
|
if AWidget is NSObject then
|
|
begin
|
|
Brush.Style:=bsClear;
|
|
Brush.Color:=clLtGray;
|
|
Pen.Color:=clMaroon;
|
|
Rectangle(0,0,AWidget.Width,AWidget.Height);
|
|
end
|
|
else
|
|
begin
|
|
AWidget.Paint(LCLForm.Canvas);
|
|
end;
|
|
RestoreHandleState;
|
|
// children
|
|
if AWidget.ChildCount>0 then
|
|
begin
|
|
for i:=0 to AWidget.ChildCount-1 do begin
|
|
SaveHandleState;
|
|
Child:=AWidget.Children[i];
|
|
// clip child area
|
|
MoveWindowOrgEx(Handle,Child.Left,Child.Top);
|
|
if IntersectClipRect(Handle,0,0,Child.Width,Child.Height)<>NullRegion then
|
|
PaintWidget(Child);
|
|
RestoreHandleState;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
PaintWidget(MyForm);
|
|
inherited Paint;
|
|
end;
|
|
|
|
function TNSObjectDesignerMediator.ComponentIsIcon(AComponent: TComponent): boolean;
|
|
begin
|
|
Result:=not (AComponent is tiOSFakeComponent);
|
|
end;
|
|
|
|
function TNSObjectDesignerMediator.ParentAcceptsChild(Parent: TComponent; Child: TComponentClass): boolean;
|
|
begin
|
|
Result:=(Parent is tiOSFakeComponent) and (Child.InheritsFrom(tiOSFakeComponent))
|
|
and (tiOSFakeComponent(Parent).AcceptChildsAtDesignTime);
|
|
end;
|
|
|
|
procedure TNSObjectDesignerMediator.InitComponent(AComponent, NewParent: TComponent; NewBounds: TRect);
|
|
begin
|
|
inherited InitComponent(AComponent, NewParent, NewBounds);
|
|
if AComponent is tiOSFakeComponent then
|
|
tiOSFakeComponent(AComponent).InitializeDefaults;
|
|
end;
|
|
|
|
function TNSObjectDesignerMediator.CreateComponent(ParentComp: TComponent;
|
|
TypeClass: TComponentClass; const AUnitName: shortstring; X, Y, W,
|
|
H: Integer; DisableAutoSize: boolean): TComponent;
|
|
begin
|
|
result := FormEditingHook.CreateComponent(ParentComp,TypeClass,AUnitName,x,y,w,h,DisableAutoSize);
|
|
end;
|
|
|
|
{ TUIResponderDesignerMediator }
|
|
|
|
class function TUIResponderDesignerMediator.FormClass: TComponentClass;
|
|
begin
|
|
Result:=UIResponder;
|
|
end;
|
|
|
|
end.
|
|
|