unit Shell.MainForm; // Main form of the Atlas VCL application shell: a workspace with a navigation // pane, a command bar and a content pane, themed by TAtlasThemer. interface uses System.Classes, Vcl.Forms, Vcl.Controls, Vcl.ComCtrls, Vcl.ActnList, Atlas.Layout, Atlas.Charts, Atlas.Gauges, Atlas.Themes; type TShellForm = class(TForm) private FWorkspace: TAtlasWorkspace; FCommands: TAtlasCommandBar; FNavigation: TAtlasNavigation; FTiles: TAtlasTileBoard; FPages: TPageControl; FChart: TAtlasChart; FEast, FWest: TAtlasChartSeries; FQueue: TAtlasGauge; FThemer: TAtlasThemer; FAddMonth: TAction; FMonths: Integer; function NewAction(const Caption: string; Handler: TNotifyEvent; Tag: Integer = 0): TAction; function NewPage(const Caption: string): TTabSheet; function AddGauge(Parent: TWinControl; const Caption: string; Top: Integer; Value: Double): TAtlasGauge; procedure ShowPage(Sender: TObject); procedure AddMonth(Sender: TObject); procedure ToggleBars(Sender: TObject); procedure NextTheme(Sender: TObject); procedure FloatNavigation(Sender: TObject); procedure QueueChanged(Sender: TObject); procedure FirstActivate(Sender: TObject); procedure SaveLayout(Sender: TObject; var Action: TCloseAction); public constructor Create(AOwner: TComponent); override; procedure SelectPage(Index: Integer); end; implementation uses System.SysUtils, System.Types, System.IOUtils, Vcl.Graphics, Vcl.StdCtrls; const MonthNames: array[0..11] of string = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); EastSales: array[0..11] of Double = (18, 22, 21, 29, 35, 38, 41, 39, 44, 47, 52, 58); WestSales: array[0..11] of Double = (24, 29, 27, 32, 38, 42, 40, 45, 49, 51, 55, 61); function LayoutFile: string; begin Result := TPath.Combine(ExtractFilePath(ParamStr(0)), 'shell-layout.json'); end; constructor TShellForm.Create(AOwner: TComponent); var Page: TTabSheet; Slider: TTrackBar; Action: TAction; I: Integer; begin inherited CreateNew(AOwner); Caption := 'Atlas VCL application shell'; Position := poScreenCenter; Width := 1000; Height := 680; Font.Name := 'Segoe UI'; Font.Size := 10; OnActivate := FirstActivate; OnClose := SaveLayout; // 1. A workspace with three panes. The pane caption becomes the title of its floating window. FWorkspace := TAtlasWorkspace.Create(Self); FWorkspace.Parent := Self; FWorkspace.Align := alClient; FWorkspace.AddPane('navigation', 'Navigation', alLeft, 200); FWorkspace.AddPane('commands', '', alTop, 150); FWorkspace.AddPane('content', '', alClient, 400); // AddPane leaves the splitter of a left or top pane on the outer edge, where it // cannot be dragged; MovePane to the same edge puts it between pane and content. FWorkspace.MovePane('navigation', alLeft, 200); FWorkspace.MovePane('commands', alTop, 150); // 2. The command bar, the navigation list and a page control inside the panes. FCommands := TAtlasCommandBar.Create(Self); FCommands.Parent := FWorkspace.Pane('commands'); FCommands.Align := alClient; FNavigation := TAtlasNavigation.Create(Self); FNavigation.Parent := FWorkspace.Pane('navigation'); FNavigation.Align := alClient; FPages := TPageControl.Create(Self); FPages.Parent := FWorkspace.Pane('content'); FPages.Align := alClient; // 3. Content pages: a tile board, a chart and three gauges. Page := NewPage('Home'); FTiles := TAtlasTileBoard.Create(Self); FTiles.Parent := Page; FTiles.Align := alClient; FTiles.BevelOuter := bvNone; Page := NewPage('Sales'); FChart := TAtlasChart.Create(Self); FChart.Parent := Page; FChart.Align := alClient; FChart.Title := 'Regional sales (thousands)'; FEast := FChart.AddSeries('East', $00876524); FWest := FChart.AddSeries('West', $005986B4); for I := 1 to 6 do AddMonth(nil); Page := NewPage('Gauges'); FQueue := AddGauge(Page, 'Queue depth (0-500, warning 300, critical 450)', 16, 0); FQueue.SetRange(0, 500); FQueue.SetThresholds(300, 450); FQueue.Value := 120; AddGauge(Page, 'CPU % (warning 70, critical 90)', 132, 62); AddGauge(Page, 'Disk % (warning 70, critical 90)', 248, 91); Slider := TTrackBar.Create(Self); Slider.Parent := Page; Slider.SetBounds(16, 364, 560, 40); Slider.Anchors := [akLeft, akTop, akRight]; Slider.Max := 500; Slider.Frequency := 50; Slider.Position := 120; Slider.OnChange := QueueChanged; FPages.ActivePageIndex := 0; // 4. Actions shared by the navigation list, the tile board and the command bar. for I := 0 to FPages.PageCount - 1 do begin Action := NewAction(FPages.Pages[I].Caption, ShowPage, I); FNavigation.AddAction(Action); FTiles.AddTile(Action); FCommands.AddCommand('Home', 'Pages', Action); end; FAddMonth := NewAction('Add month', AddMonth); FCommands.AddCommand('Home', 'Sales', FAddMonth); FCommands.AddCommand('Home', 'Sales', NewAction('Bars / lines', ToggleBars)); FCommands.AddCommand('View', 'Appearance', NewAction('Next theme', NextTheme)); FCommands.AddCommand('View', 'Layout', NewAction('Float navigation', FloatNavigation)); // 5. The themer colours every control below its root; assign it after the controls exist. FThemer := TAtlasThemer.Create(Self); FThemer.Root := FWorkspace; end; function TShellForm.NewAction(const Caption: string; Handler: TNotifyEvent; Tag: Integer): TAction; begin Result := TAction.Create(Self); Result.Caption := Caption; Result.Tag := Tag; Result.OnExecute := Handler; end; function TShellForm.NewPage(const Caption: string): TTabSheet; begin Result := TTabSheet.Create(Self); Result.PageControl := FPages; Result.Caption := Caption; Result.TabVisible := False; end; function TShellForm.AddGauge(Parent: TWinControl; const Caption: string; Top: Integer; Value: Double): TAtlasGauge; begin Result := TAtlasGauge.Create(Self); Result.Parent := Parent; Result.SetBounds(16, Top, 560, 100); Result.Anchors := [akLeft, akTop, akRight]; Result.Caption := Caption; Result.Value := Value; end; procedure TShellForm.SelectPage(Index: Integer); begin FPages.ActivePageIndex := Index; FNavigation.ItemIndex := Index; end; procedure TShellForm.ShowPage(Sender: TObject); begin SelectPage(TAction(Sender).Tag); end; procedure TShellForm.AddMonth(Sender: TObject); begin if FMonths >= Length(MonthNames) then Exit; // X is the month number. Captions label the bars; line charts use the numeric axis. FEast.Add(FMonths + 1, EastSales[FMonths], MonthNames[FMonths]); if FMonths = 3 then FWest.AddMissing(FMonths + 1) // a gap: the line breaks and no bar is drawn else FWest.Add(FMonths + 1, WestSales[FMonths], MonthNames[FMonths]); Inc(FMonths); if Assigned(FAddMonth) then FAddMonth.Enabled := FMonths < Length(MonthNames); end; procedure TShellForm.ToggleBars(Sender: TObject); begin if FChart.Kind = acLine then FChart.Kind := acBar else FChart.Kind := acLine; end; procedure TShellForm.NextTheme(Sender: TObject); begin FThemer.ThemeIndex := (FThemer.ThemeIndex + 1) mod AtlasThemeCount; Caption := 'Atlas VCL application shell - ' + AtlasTheme(FThemer.ThemeIndex).Name; end; procedure TShellForm.FloatNavigation(Sender: TObject); var Origin: TPoint; begin if FWorkspace.IsFloating('navigation') then FWorkspace.DockPane('navigation') else begin Origin := ClientToScreen(Point(40, 120)); FWorkspace.FloatPane('navigation', Rect(Origin.X, Origin.Y, Origin.X + 240, Origin.Y + 400)); end; end; procedure TShellForm.QueueChanged(Sender: TObject); begin FQueue.Value := TTrackBar(Sender).Position; end; procedure TShellForm.FirstActivate(Sender: TObject); begin // Restore the saved layout once the workspace is showing, so a floating pane can open its window. OnActivate := nil; if not FileExists(LayoutFile) then Exit; try FWorkspace.LoadLayout(TFile.ReadAllText(LayoutFile, TEncoding.UTF8)); except on EAtlasLayoutError do ; // a stale or edited file: keep the default layout end; end; procedure TShellForm.SaveLayout(Sender: TObject; var Action: TCloseAction); begin TFile.WriteAllText(LayoutFile, FWorkspace.SaveLayout, TEncoding.UTF8); end; end.