unit dgui3;

//
//  How to add gui in an application:
//  1. Initialize a theme:
//     Theme.Init;
//     Theme.SetFont('font.json');
//  2. Create main gui object:
//     Gui.Init(@Theme);
//  3. Create widgets:
//     Button := GuiCreateButton(Gui);
//     Button.SetRect(10, 10, 100, 100);
//     Button.Caption := 'Hello!';
//  4. Dont forget to free all:
//     Gui.Done;
//     Theme.Done;
//
//  How to write new widget:
//  1. The widget needs unique string identifier name, e.g.
//     WIDGET_MYWIDGET = 'mywidget';
//  2. Inherite widget class from TWidget. The class should implement control
//     logic. Its WidgetName field should be equel to the WIDGET_MYWIDGET defined
//     on previous step.
//  3. Inherite style class from TGuiStyle. It should implement view logic.
//     Its WidgetName should be equel to the WIDGET_MYWIDGET as well.
//  4. Register the defined TGuiStyle in all used themes.
//
//  For all defulat widget types see WIDGET_* constants.
//
//  A widget can render using skin or by itself.
//
//  Widget.Skin := Skin; // set skin for the widget and ALL of its subwidgets
//
//  Not all widgets implemented yet...
//

{$MODE OBJFPC}
{$MODESWITCH AutoDeref}
{$MODESWITCH POINTERTOPROCVAR+}

interface

uses
  dtypes,
  dobjects,
  dimbeddedtree,
  dvector2i,
  dvector2f,
  drect2i,
  dcolors,
  DEngBase,
  deng,
  DEngLog,
  DEngStreamAssets,
  DDrawTex,
  DDrawFont,
  DAudio,
  dgui_place;

const
  STYLE_DEFAULT = '';
  WIDGET_GUI = 'gui'; // the main (global) object for gui
  WIDGET_RECT = 'rect'; // just a rectangle
  WIDGET_LABEL = 'label'; // just a text label
  WIDGET_IMAGE = 'image'; // just an image
  WIDGET_BUTTON = 'button'; // a button
  WIDGET_EDIT = 'edit'; // a text edit
  WIDGET_MEMO = 'memo'; // multiline text edit
  WIDGET_WINDOW = 'window'; // window
  WIDGET_CHECKBOX = 'checkbox'; // checkbox
  WIDGET_RADIOBOX = 'radiobox'; // list with several choices
  WIDGET_TABPANEL = 'tabpanel'; // panel with tabs
  WIDGET_TABPAGE = 'tabpage'; // a page in panel with tabs
  WIDGET_CONSOLE = 'console'; // console for programmers 8]
  WIDGET_HORSLIDER = 'horslider'; // horizontal slider
  WIDGET_HORSLIDERTHUMB = 'horsliderthumb';
  WIDGET_VERSLIDER = 'verslider'; // vertical slider
  WIDGET_VERSLIDERTHUMB = 'versliderthumb';
  WIDGET_HORSCROLLBAR = 'horscrollbar';
  WIDGET_HORSCROLLBARTHUMB = 'horscrollbarthumb';
  WIDGET_HORSCROLLBARLEFTBUTTON = 'horscrollbarleftbutton';
  WIDGET_HORSCROLLBARRIGHTBUTTON = 'horscrollbarrightbutton';
  WIDGET_VERSCROLLBAR = 'verscrollbar';
  WIDGET_VERSCROLLBARTHUMB = 'verscrollbarthumb';
  WIDGET_VERSCROLLBARUPBUTTON = 'verscrollbarupbutton';
  WIDGET_VERSCROLLBARDOWNBUTTON = 'verscrollbardownbutton';
  //  List of default controls:
  //    gui -- top level gui control
  //    rect -- just filled rect
  //    label -- just a text label
  //    image -- just a texture
  //    button
  //    edit
  //    memo
  //    window
  //    checkbox
  //    radiobox
  //    tabpanel
  //    tabpage
  //    console
  //    hslider
  //    hsliderthumb?
  //    vslider
  //    vsliderthumb?
  //    hscrollbar
  //    vscrollbar

type
PGuiViewState = ^TGuiViewState;
PGuiStyle = ^TGuiStyle;
PGuiTheme = ^TGuiTheme;
PWidget = ^TWidget;
PGui = ^TGui;

TGuiViewState = object(TObject)
end;

PGUISliderState = ^TGUISliderState;
TGUISliderState = object
  Value: Single; // 0 .. 1
  Hover: Boolean;
  Dragging: Boolean;
end;

PGUIButtonState = ^TGUIButtonState;
TGUIButtonState = object
  Hover: Boolean;
  Pressed: Boolean;
  Disabled: Boolean;
end;

TGuiStyle = object(TObject)
private
  FWidgetName: AnsiString;
protected
  // View State
  function CreateViewState(Widget: PWidget): PGuiViewState; virtual;
  procedure FreeViewState(Widget: PWidget; ViewState: PGuiViewState); virtual;
public
  constructor Init(const aWidgetName: AnsiString);

  // Name -> Resource map
  function GetColor(const ColorId: AnsiString; Def: TColor): TColor;
  function GetTexture(const TextureId: AnsiString): PDrawTex;
  function GetFont(Theme: PGuiTheme; const FontId: AnsiString): PFont;
  function GetSound(const SoundId: AnsiString): PSound;

  // Basic Logic
  function IsPointIn(Widget: PWidget; const P: TVec2i): Boolean; virtual;
  function GetMinSize(Widget: PWidget): TVec2i; virtual;

  procedure RectRender(Widget: PWidget); virtual;

  procedure ButtonRender(Widget: PWidget; ButtonState: PGUIButtonState); virtual;
  function ButtonIsPointIn(Widget: PWidget;
                           State: PGUISliderState;
                           const GlobalP: TVec2i): Boolean; virtual;

  procedure HSliderRender(Widget: PWidget; SliderState: PGUISliderState); virtual;
  function HSliderIsPointInThumb(Widget: PWidget;
                                 SliderState: PGUISliderState;
                                 const V: TVec2i): Boolean; virtual;
  function HSliderPointToValue(Widget: PWidget;
                               SliderState: PGUISliderState;
                               const V: TVec2i): Single; virtual;

  property WidgetName: AnsiString read FWidgetName;
end;

TGuiTheme = object(TObject)
private
  FStyles: array of PGuiStyle;
  FFonts: array of record
    Id: AnsiString;
    Font: PFont;
  end;
public
  constructor Init;
  destructor Done; virtual;

  // First style must be default
  procedure AddStyle(const WidgetName: AnsiString; Style: PGuiStyle);
  function GetStyle(const WidgetName: AnsiString): PGuiStyle;

  procedure AddFont(const FontId: AnsiString; Font: PFont);
  function GetFont(const FontId: AnsiString): PFont;
end;

TGuiProcUserdata = procedure (Userdata: Pointer);
TGuiProcOfObject = procedure of object;

PGuiCallback = ^TGuiCallback;
TGuiCallback = object
private
  Proc: TGuiProcUserdata;
  Userdata: Pointer;
public
  Call: TGuiProcOfObject;
  procedure SetProc; overload; inline;
  procedure SetProc(aProc: TGuiProcOfObject); overload;
  procedure SetProc(aProc: TGuiProcUserdata; aUserdata: Pointer); overload;
  procedure DoCall;
  procedure DoNothing;
end;

TOnFree = procedure (Widget: PWidget);
      //  OnFree callback is called after destructor, typically by parent
      //  widget.
      //
      //  By default widget's OnFree callback is GuiNotFree, so if will be
      //  leaked if if was allocated with GetMem.

TWidget = object(TObject)
private
  // Hierarchy
  //FGui: PGui;
  FParent: PWidget;
  FSubFirst, FSubLast: PWidget;
  FPrev, FNext: PWidget;
  // Style
  FWidgetName: AnsiString;
  FTheme: PGuiTheme;
  FStyle: PGuiStyle;
  FViewState: PGuiViewState;
  // Common data
  FPlace: TGUIPlace;
  // FRelativePos: TVec2i;
  // FGlobalPos: TVec2i;
  // FSize: TVec2i;
  FCaption: AnsiString;
  FMouseHover: Boolean;
  FCallback: TGuiCallback;
  FEnabled: Boolean;
  FOnFree: TOnFree;
  function GetPos: TVec2i; inline;
  function GetAbsPos: TVec2i; inline;
  function GetSize: TVec2i; inline;
  function GetAutoSize: Boolean; inline;
  procedure SetAutoSize(NewAutoSize: Boolean); inline;
  procedure OnNewParent;
  procedure AssignStyle(Style: PGuiStyle);
  procedure SetCaption(const aCaption: AnsiString);
protected
  procedure DoRender; virtual;
  procedure DoUpdate; virtual;
  function DoOnWindowEvent(Ev: PWindowEvent): Boolean; virtual;
  property Theme: PGuiTheme read FTheme;
  property Style: PGuiStyle read FStyle;
public
  constructor Init(Parent: PWidget); overload;
  constructor Init(const WidgetName: AnsiString; Parent: PWidget); overload;
  destructor Done; virtual;
  procedure SetOnFree(Callback: TOnFree);
      //  Changes OnFree callback to Callback.
      //  Callback=nil is legal and just sets default callback GuiNotFree.
  procedure Render;
  procedure Update;
  function OnWindowEvent(Ev: PWindowEvent): Boolean;
  procedure OnResize(NewParentWidth, NewParentHeight: LongInt); virtual;
  property WidgetName: AnsiString read FWidgetName;
  property Enabled: Boolean read FEnabled write FEnabled;
public
  function IsMouserHover: Boolean;
  procedure AlignTopLeft;
  procedure SetLocalRect(X, Y, W, H: PtrInt); overload;
  procedure SetLocalRect(const P, S: TVec2i); overload;
  property RelativePos: TVec2i read GetPos;
  property GlobalPos: TVec2i read GetAbsPos;
  property Size: TVec2i read GetSize;
  property AutoSize: Boolean read GetAutoSize write SetAutoSize;
  property Caption: AnsiString read FCaption write SetCaption;
  property MouseHover: Boolean read IsMouserHover;
public // hierarchy
  procedure SetParent(NewParent: PWidget);
  procedure BringToTop;
  property Parent: PWidget read FParent;
  property SubFirst: PWidget read FSubFirst;
  property SubLast: PWidget read FSubLast;
  property Prev: PWidget read FPrev;
  property Next: PWidget read FNext;
public // functional
  function Callback: PGuiCallback; inline;
end;

TGui = object(TWidget)
public
  constructor Init(aTheme: PGuiTheme);
  procedure OnResize(NewParentWidth, NewParentHeight: LongInt); virtual;
end;

TGuiRect = object(TWidget)
protected
  procedure DoRender; virtual;
public
  constructor Init(aParent: PWidget; P, S: TVec2i);
end;

PGuiImage = ^TGuiImage;
TGuiImage = object(TWidget)
private
  FTex: TDrawTex;
  // FTexRect: TRect2f;
protected
  procedure DoRender; virtual;
public
  constructor Init(aParent: PWidget);
  procedure SetTexture(const FileName: AnsiString);
end;

TLabel = object(TWidget)
public
  constructor Init(aParent: PWidget; P, S: TVec2i; const aCaption: AnsiString);
end;

PButton = ^TButton;
TButton = object(TWidget)
private
  FState: TGUIButtonState;
protected
  procedure DoRender; virtual;
  function DoOnWindowEvent(Ev: PWindowEvent): Boolean; virtual;
public
  constructor Init(aParent: PWidget; P, S: TVec2i; const aCaption: AnsiString);
end;

TWindow = object(TWidget)
public
  constructor Init(aParent: PWidget; P, S: TVec2i; const aCaption: AnsiString);
end;

procedure GuiNotFree(Widget: PWidget);
procedure GuiFreeMem(Widget: PWidget);
procedure GuiDispose(Widget: PWidget);

implementation

var
  DummyStyle: TGuiStyle;

constructor TGuiStyle.Init(const aWidgetName: AnsiString);
begin
  FWidgetName := aWidgetName;
end;

function TGuiStyle.GetColor(const ColorId: AnsiString; Def: TColor): TColor;
begin
  Exit(Def);
end;

function TGuiStyle.GetTexture(const TextureId: AnsiString): PDrawTex;
begin
  Exit(nil);
end;

function TGuiStyle.GetFont(Theme: PGuiTheme; const FontId: AnsiString): PFont;
begin
  Exit(Theme^.GetFont(FontId));
end;

function TGuiStyle.GetSound(const SoundId: AnsiString): PSound;
begin
  Exit(nil);
end;

function TGuiStyle.IsPointIn(Widget: PWidget; const P: TVec2i): Boolean;
begin
  Exit(Widget^.FPlace.IsPointInAbs(P));
end;

function TGuiStyle.GetMinSize(Widget: PWidget): TVec2i;
begin
  Exit(Vec2i(0, 0));
end;

procedure TGuiStyle.RectRender(Widget: PWidget);
begin
end;

procedure TGuiStyle.ButtonRender(Widget: PWidget; ButtonState: PGUIButtonState);
begin
end;

function TGuiStyle.ButtonIsPointIn(Widget: PWidget;
                         State: PGUISliderState;
                         const GlobalP: TVec2i): Boolean;
begin
  Exit(Widget^.FPlace.IsPointInAbs(GlobalP));
end;

procedure TGuiStyle.HSliderRender(Widget: PWidget; SliderState: PGUISliderState);
begin
end;

function TGuiStyle.HSliderIsPointInThumb(Widget: PWidget;
                                             SliderState: PGUISliderState;
                                             const V: TVec2i): Boolean;
begin
  Exit(False);
end;

function TGuiStyle.HSliderPointToValue(Widget: PWidget;
                             SliderState: PGUISliderState;
                             const V: TVec2i): Single;
begin
  Result := (V.X - Widget^.GlobalPos.X) / (Widget^.Size.X + 1);
  if Result < 0 then begin
    Result := 0;
  end else if Result > 1 then
    Result := 1;
end;

function TGuiStyle.CreateViewState(Widget: PWidget): PGuiViewState;
begin
  Result := nil;
end;

procedure TGuiStyle.FreeViewState(Widget: PWidget; ViewState: PGuiViewState);
begin
  if ViewState <> nil then
    Dispose(ViewState, Done);
end;

constructor TGuiTheme.Init;
begin
  inherited Init;
end;

destructor TGuiTheme.Done;
begin
  inherited Done;
end;

procedure TGuiTheme.AddStyle(const WidgetName: AnsiString; Style: PGuiStyle);
begin
  SetLength(FStyles, Length(FStyles) + 1);
  FStyles[High(FStyles)] := Style;
end;

function TGuiTheme.GetStyle(const WidgetName: AnsiString): PGuiStyle;
var
  I: LongInt;
begin
  for I := 0 to High(FStyles) do begin
    if FStyles[I].WidgetName = WidgetName then
      Exit(FStyles[I]);
  end;
  Exit(FStyles[0]);
end;

procedure TGuiTheme.AddFont(const FontId: AnsiString; Font: PFont);
begin
  SetLength(FFonts, Length(FFonts) + 1);
  FFonts[High(FFonts)].Id := FontId;
  FFonts[High(FFonts)].Font := Font;
end;

function TGuiTheme.GetFont(const FontId: AnsiString): PFont;
var
  I: LongInt;
begin
  for I := 0 to High(FFonts) do begin
    if FFonts[I].Id = FontId then
      Exit(FFonts[I].Font);
  end;
  Exit(FFonts[0].Font);
end;

procedure TGuiCallback.SetProc;
begin
  Call := @DoNothing;
end;

procedure TGuiCallback.SetProc(aProc: TGuiProcOfObject);
begin
  if aProc <> nil then begin
    Call := aProc;
  end else
    SetProc;
end;

procedure TGuiCallback.SetProc(aProc: TGuiProcUserdata; aUserdata: Pointer);
begin
  if aProc <> nil then begin
    Proc := aProc;
    Userdata := aUserdata;
    Call := @DoCall;
  end else
    SetProc;
end;

procedure TGuiCallback.DoCall;
begin
  Proc(Userdata);
end;

procedure TGuiCallback.DoNothing;
begin
end;

constructor TWidget.Init(Parent: PWidget);
begin
  inherited Init;
  FPlace.Init;
  FStyle := @DummyStyle;
  FWidgetName := '';
  SetParent(Parent);
  FCallback.SetProc;
  FOnFree := @GuiNotFree;
  FEnabled := True;
end;

constructor TWidget.Init(const WidgetName: AnsiString; Parent: PWidget);
begin
  inherited Init;
  FPlace.Init;
  FStyle := @DummyStyle;
  FWidgetName := WidgetName;
  SetParent(Parent);
  FCallback.SetProc;
  FOnFree := @GuiNotFree;
  FEnabled := True;
end;

destructor TWidget.Done;
var
  It: PWidget;
begin
  It := FSubFirst;
  while It <> nil do begin
    It^.FOnFree(It);
    It := It^.FNext;
  end;
  FPlace.Done;
  inherited Done;
end;

procedure TWidget.SetOnFree(Callback: TOnFree);
begin
  if Callback = nil then begin
    FOnFree := @GuiNotFree;
  end else
    FOnFree := Callback;
end;

procedure TWidget.SetParent(NewParent: PWidget);
begin
  if FNext <> nil then
    FNext^.FPrev := FPrev;
  if FPrev <> nil then
    FPrev^.FNext := FNext;
  if FParent <> nil then begin
    if FParent^.FSubFirst = @Self then
      FParent^.FSubFirst := FNext;
    if FParent^.FSubLast = @Self then
      FParent^.FSubLast := FPrev;
    FParent := nil;
  end;
  if NewParent = nil then begin
    FPrev := nil;
    FNext := nil;
    Exit;
  end;
  FParent := NewParent;
  FNext := nil;
  FPrev := FParent^.FSubLast;
  if FPrev <> nil then
    FPrev^.FNext := @Self;
  FParent^.FSubLast := @Self;
  if FParent^.FSubFirst = nil then
    FParent^.FSubFirst := @Self;
  OnNewParent;
end;

procedure TWidget.BringToTop;
begin
  if FParent = nil then
    Exit;
  if FParent.FSubLast = @Self then
    Exit;
  if FNext <> nil then
    FNext^.FPrev := FPrev;
  if FPrev <> nil then
    FPrev^.FNext := FNext;
  FPrev := FParent.FSubLast;
  if FPrev <> nil then
    FPrev.FNext := @Self;
  FParent.FSubLast := @Self;
  FNext := nil;
end;

function TWidget.Callback: PGuiCallback;
begin
  Result := @FCallback;
end;

function TWidget.GetPos: TVec2i; inline;
begin
  Exit(FPlace.Pos);
end;

function TWidget.GetAbsPos: TVec2i; inline;
begin
  Exit(FPlace.AbsPos);
end;

function TWidget.GetSize: TVec2i; inline;
begin
  Exit(FPlace.Size);
end;

function TWidget.GetAutoSize: Boolean;
begin
  Exit(FPlace.AutoSize);
end;

procedure TWidget.SetAutoSize(NewAutoSize: Boolean);
begin
  FPlace.AutoSize := NewAutoSize;
end;

procedure TWidget.OnNewParent;
begin
  if FParent <> nil then begin
    FTheme := FParent.FTheme;
    FStyle := FTheme.GetStyle(FWidgetName);
  end;
end;

procedure TWidget.AssignStyle(Style: PGuiStyle);
begin
  if (FStyle <> nil) and (FStyle <> Style) then begin
    if FViewState <> nil then
      FStyle.FreeViewState(@Self, FViewState);
    FStyle := Style;
    FViewState := FStyle.CreateViewState(@Self);
  end;
end;

procedure TWidget.SetCaption(const aCaption: AnsiString);
begin
  FCaption := aCaption;
end;

procedure TWidget.DoRender;
begin
end;

procedure TWidget.DoUpdate;
begin
end;

function TWidget.DoOnWindowEvent(Ev: PWindowEvent): Boolean;
begin
  Result := False;
end;

procedure TWidget.Render;
var
  It: PWidget;
begin
  DoRender;
  It := FSubFirst;
  while It <> nil do begin
    if It^.Enabled then
      It^.Render;
    It := It^.FNext;
  end;
end;

procedure TWidget.Update;
var
  It: PWidget;
begin
  DoUpdate;
  It := FSubFirst;
  while It <> nil do begin
    if It^.Enabled then
      It^.Update;
    It := It^.FNext;
  end;
end;

function TWidget.IsMouserHover: Boolean;
begin
  Result := FStyle.IsPointIn(@Self, Eng.GetMouse);
end;

procedure TWidget.AlignTopLeft;
begin
  FPlace.AlignTopLeft;
end;

procedure TWidget.SetLocalRect(X, Y, W, H: PtrInt);
begin
  SetLocalRect(Vec2i(X, Y), Vec2i(W, H));
end;

procedure TWidget.SetLocalRect(const P, S: TVec2i);
begin
  FPlace.SetPos(P, Parent^.Size);
  FPlace.SetSize(S, Parent^.Size);
end;

function TWidget.OnWindowEvent(Ev: PWindowEvent): Boolean;
var
  It: PWidget;
begin
  It := FSubLast;
  while It <> nil do begin
    if It^.Enabled and It^.OnWindowEvent(Ev) then
      Exit(True);
    It := It^.Prev;
  end;
  Result := DoOnWindowEvent(Ev);
end;

procedure TWidget.OnResize(NewParentWidth, NewParentHeight: LongInt);
var
  It: PWidget;
begin
  FPlace.OnParentResized(Vec2i(NewParentWidth, NewParentHeight));
  It := FSubLast;
  while It <> nil do begin
    It^.OnResize(NewParentWidth, NewParentHeight);
    It := It^.Prev;
  end;
end;

constructor TGui.Init(aTheme: PGuiTheme);
begin
  inherited Init(WIDGET_GUI, nil);
  FPlace.Pos := Vec2i(0, 0);
  FPlace.SetSize(Vec2i(Eng.Width, Eng.Height), Vec2i(1, 1));
  FTheme := aTheme;
  FStyle := @DummyStyle;
end;

procedure TGui.OnResize(NewParentWidth, NewParentHeight: LongInt);
begin
  FPlace.SetSize(Vec2i(NewParentWidth, NewParentHeight), Vec2i(1, 1));
  inherited OnResize(NewParentWidth, NewParentHeight);
end;

procedure TGuiRect.DoRender;
begin
  FStyle^.RectRender(@Self);
end;

constructor TGuiRect.Init(aParent: PWidget; P, S: TVec2i);
begin
  inherited Init(WIDGET_RECT, aParent);
  FPlace.SetPos(P, aParent^.Size);
  FPlace.SetSize(S, aParent^.Size);
end;

procedure TGuiImage.DoRender;
var
  ActualCenter: TVec2f;
  ActualSize: TVec2f;
begin
  ActualSize := Vec2f(FPlace.Size);
  ActualCenter := Vec2f(FPlace.AbsPos) + Mul2f(ActualSize, 0.5);
  Eng.Draw.RGBA(255, 255, 255, 255);
  FTex.Draw(@Eng, ActualCenter, ActualSize);
end;

constructor TGuiImage.Init(aParent: PWidget);
begin
  inherited Init(WIDGET_IMAGE, aParent);
end;

procedure TGuiImage.SetTexture(const FileName: AnsiString);
begin
  // FIXME very unsafe code here...
  FTex.Init(nil, @Eng, Asset(FileName));
end;

constructor TLabel.Init(aParent: PWidget; P, S: TVec2i; const aCaption: AnsiString);
begin
  inherited Init(WIDGET_LABEL, aParent);
  FPlace.SetPos(P, aParent^.Size);
  FPlace.SetSize(S, aParent^.Size);
  FCaption := aCaption;
end;

procedure TButton.DoRender;
begin
  FStyle^.ButtonRender(@Self, @FState);
end;

function TButton.DoOnWindowEvent(Ev: PWindowEvent): Boolean;
begin
  if Ev.IsLeftClickDown then begin
    if FStyle.IsPointIn(@Self, Round2f(Ev.GetCursor)) then begin
      FCallback.Call;
      Exit(True);
    end;
  end else if Ev.IsCursorMove then begin
    FMouseHover := FStyle.IsPointIn(@Self, Round2f(Ev.GetCursor))
  end;
  Result := False;
end;

constructor TButton.Init(aParent: PWidget; P, S: TVec2i; const aCaption: AnsiString);
begin
  inherited Init(WIDGET_BUTTON, aParent);
  FPlace.SetPos(P, aParent^.Size);
  FPlace.SetSize(S, aParent^.Size);
  FCaption := aCaption;
  FPlace.MinSize := FStyle.GetMinSize(@Self);
end;

constructor TWindow.Init(aParent: PWidget; P, S: TVec2i; const aCaption: AnsiString);
begin
  inherited Init(WIDGET_WINDOW, aParent);
  FPlace.SetPos(P, aParent^.Size);
  FPlace.SetSize(S, aParent^.Size);
  FCaption := aCaption;
end;

procedure GuiNotFree(Widget: PWidget);
begin
  Widget^.Done;
end;

procedure GuiFreeMem(Widget: PWidget);
begin
  FreeMem(Pointer(Widget));
end;

procedure GuiDispose(Widget: PWidget);
begin
  Dispose(Widget, Done);
end;

initialization
  DummyStyle.Init('');
finalization
  DummyStyle.Done;
end.
