Delphi 10.1 Berlinのスタイルを使い方の備忘録

Delphi 10.1 Berlinのスタイルを使い方の備忘録

登録されているスタイルを取得する

uses
  FMX.Styles;

  // 登録されているスタイルを列挙します。
  TStyleManager.EnumStyleResources(
    procedure(const AResourceName: string; const APlatform: TOSPlatform)
    begin
      ListBoxStyleNames.Items.Add(AResourceName)
    end);

設定されているスタイルオブジェクトを取得する

var
  StyleResource: TFmxObject;

StyleResource := TStyleManager.ActiveStyle(Self);

スタイル名からスタイルオブジェクトを取得する

var
  StyleResource: TFmxObject;

StyleResource := TStyleManager.GetStyleResource(ResourceName);

スタイルオブジェクトからスタイルの説明を取得する

  // スタイル説明を取得します
  StyleDescription := TStyleManager.FindStyleDescriptor(StyleResource);
  Memo1.Lines.Add('スタイルの作者=' + StyleDescription.Author);
  Memo1.Lines.Add('スタイルの作成者のeメールアドレス=' + StyleDescription.AuthorEMail);
  Memo1.Lines.Add('スタイルの作成者のWebサイトのURL =' + StyleDescription.AuthorURL);
  Memo1.Lines.Add('スタイルがモバイルプラットフォーム上で使用できるか=' +
    BoolToStr(StyleDescription.MobilePlatform, True));
  Memo1.Lines.Add('スタイルのタイトル=' + StyleDescription.Title);
  Memo1.Lines.Add('スタイルのバージョン=' + StyleDescription.Version);

デフォルトのスタイルを設定する

TStyleManager.SetStyle(nil);

スタイルをファイルから読み込んで適用する

AFileName := 'C:\~\Air.Style'
TStyleManager.SetStyleFromFile(AFileName);

サンプルアプリケーション

登録されているスタイルをリストボックスに一覧表示します。
リストボックスのスタイルを選択すると、スタイルの情報をメモに表示します。

ソースコードはこちら→StyleSample

ES2015 (ES6)の関数の引数にデフォルト値を指定する

ES2015 (ES6)では、関数の引数にデフォルト値を指定できます。

function func(a=100, b=200) {
    console.log(a);
    console.log(b);
}

func(30);

実行結果は次のようになります。

30
200

デフォルト値には、関数の返値も使用できます。

function func(a=new Date()) {
    console.log(a);
}

ES2015 (ES6)の分割代入を使う

分割代入は、変数の要素やオブジェクトの値を展開して、個別の変数に代入する構文です。

配列を分割代入

let [a, b] = [1, 2];
console.log(a); //=> 1
console.log(b); //=> 2

不要な値を無視する。
2番目の値は不要なので、変数に代入しません。

let [a, , c] = [1, 2, 3]; 
console.log(a); //=> 1
console.log(c); //=> 3

オブジェクトを分割代入

let {a, b} = {a:1, b:2};
console.log(a); //=> 1
console.log(b); //=> 2

オブジェクトのプロパティ名と異なる変数名に代入する場合の書き方です。

let {a:x, b} = {a:1, b:2};
console.log(x); //=> 1
console.log(b); //=> 2

デフォルト値

変数名の後ろにデフォルト値を指定できます。

let {a=100, b=200} = {a:1};
console.log(a); //=> 1
console.log(b); //=> 200

関数の引数にデフォルト値を指定する

function func({a=100, b=200} = {}) {
    console.log(a);
    console.log(b);
}

func({a:30});

実行結果は次のようになります。

30
200

関数が複数の返値を返す

関数から複数の値を返したいとき、関数は配列で値を返し、受け取り側は分割代入を使うと簡潔に記述できます。

function func() {
    return [100, 200];
}

let [a, b] = func();
console.log(a); //=> 100
console.log(b); //=> 200

Delphi 10.1 BerlinのFireMonkeyアプリケーションでTStringGridでカスタムエディタを使用する

TStringGridのセルに入力するエディタをカスタマイズする方法を紹介します。

TStringGridでカスタマイズしたエディタを使用するには

エディタをカスタマイズするには、TStringGridのOnCreateCustomEditorイベントを使用します。
OnCreateCustomEditorイベントで、カスタマイズしたエディタを返します。

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    StringColumn1: TStringColumn;
    procedure StringGrid1CreateCustomEditor(Sender: TObject;
      const Column: TColumn; var Control: TStyledControl);
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;

procedure TForm1.StringGrid1CreateCustomEditor(Sender: TObject;
  const Column: TColumn; var Control: TStyledControl);
begin
  // ここでカスタマイズしたエディタを返す
  Control := TMyEdit.Create(Sender as TComponent);
end;

カスタマイズしたエディタを作成する

入力した文字を大文字に変換するカスタムエディタのサンプルです。
TMyEditは入力された値を大文字に変換します。

TEditクラスを継承したTMyEditクラスを作成します

type
  TMyEdit = class(TEdit)
  public
    constructor Create(AOwner: TComponent); override;
  end;

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited;
end;

ICellControlインターフェースも継承します。

セルの編集が終わった後、列オブジェクト(TColumn)に対してセルの値の変更を通知する必要があります。
ICellControlインターフェースを継承すると、編集する列のオブジェクトを取得できるようになります。

type
  TMyEdit = class(TEdit, ICellControl)
  public
    constructor Create(AOwner: TComponent); override;
  end;

インターフェースが要求するメソッドを追加します。

type
  TMyEdit = class(TEdit, ICellControl)
  private
    [Weak]
    FColumn: TColumn;
    { ICellControl }
    function GetColumn: TColumn;
    procedure SetColumn(const Value: TColumn);
  end;

function TMyEdit.GetColumn: TColumn;
begin
  // 編集対象の列
  Result := FColumn;
end;

procedure TMyEdit.SetColumn(const Value: TColumn);
begin
  // 編集対象の列
  FColumn := Value;
end;

セルの値が変更されたら、列オブジェクトに変更を通知します。

type
  TMyEdit = class(TEdit, ICellControl)
  private
    procedure ChangeProc(Sender: TObject);
    ...
  end;

procedure TMyEdit.ChangeProc(Sender: TObject);
begin
  // 列に対して変更を通知する。これがないと編集した値が登録されない
  if FColumn <> nil then
    FColumn.EditorModified;
end;

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited;
  // 入力されたらChangeProc処理を行う
  OnChangeTracking := ChangeProc;
end;

入力された値を大文字に変換する処理を追加します。

type
  TMyEdit = class(TEdit, ICellControl)
  private
    procedure ValidateText(Sender: TObject; var Text: string);
    ...
  end;

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited;
  // 入力されたらChangeProc処理を行う
  OnChangeTracking := ChangeProc;
  // 入力が終わったときにValidateText処理を行う
  OnValidate := ValidateText;
end;

procedure TMyEdit.ValidateText(Sender: TObject; var Text: string);
begin
  // 入力が終わったら大文字に変換する
  Text := UpperCase(Text);
end;

以上で完成です。

全体のコードは次のようになります。

type
  TMyEdit = class(TEdit, ICellControl)
  private
    [Weak]
    FColumn: TColumn;
    procedure ChangeProc(Sender: TObject);
    { ICellControl }
    function GetColumn: TColumn;
    procedure SetColumn(const Value: TColumn);
    procedure ValidateText(Sender: TObject; var Text: string);
  public
    constructor Create(AOwner: TComponent); override;
  end;

{ TMyEdit }

procedure TMyEdit.ChangeProc(Sender: TObject);
begin
  // 列に対して変更を通知する。これがないと編集した値が登録されない
  if FColumn <> nil then
    FColumn.EditorModified;
end;

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited;
  // 入力されたらChangeProc処理を行う
  OnChangeTracking := ChangeProc;
  // 入力が終わったときにValidateText処理を行う
  OnValidate := ValidateText;
end;

function TMyEdit.GetColumn: TColumn;
begin
  // 編集対象の列
  Result := FColumn;
end;

procedure TMyEdit.SetColumn(const Value: TColumn);
begin
  // 編集対象の列
  FColumn := Value;
end;

procedure TMyEdit.ValidateText(Sender: TObject; var Text: string);
begin
  // 入力が終わったら大文字に変換する
  Text := UpperCase(Text);
end;

サンプルアプリケーション

フォームにTStringGridを配置し、TStringColumnを追加します。

グリッドのOnCreateCustomEditorイベントを追加します。

procedure TForm1.StringGrid1CreateCustomEditor(Sender: TObject;
  const Column: TColumn; var Control: TStyledControl);
begin
  Control := TMyEdit.Create(Sender as TComponent);
end;

アプリケーションを実行します。

セルに入力します。

001

入力を完了すると、セルに入力した値が大文字に変換されます。

002

ソースコードのダウンロードはこちら→GridCustomEditor

ES2015 (ES6)のSymbolを使う

シンボルを作る

シンボルはSymbolコンストラクタで生成します。

const FOO = Symbol();
const BAR = Symbol();

Symbolコンストラクタは、引数に文字列を受け取ることができます。

const FOO = Symbol('FOO');
const BAR = Symbol('BAR');

シンボルを比較する

シンボルは自分自身以外とは一致しません。

const FOO = Symbol();
const BAR = Symbol();

console.log(FOO == FOO); //=> true
console.log(FOO == BAR); //=> false

コンストラクタの引数が同じ文字列でも、シンボルは一致しません。

const FOO = Symbol('TEST');
const BAR = Symbol('TEST');

console.log(FOO == BAR); //=> false

シンボルを取得する

Symbol.for(文字列)を使うと、同じシンボルを取得することができます。

const FOO = Symbol.for('TEST');
const BAR = Symbol.for('TEST');

console.log(FOO == BAR); //=> true

Delphi iOSアプリケーションでネイティブコントロールの上に画像を表示する

FireMonkeyのコントロールはネイティブコントロールの上に表示することができません。

Delphi iOSアプリケーションでネイティブコントロールの上に画像を表示するために ネイティブコントロールのUIImageViewをラップしたクラスを作成しました。

UIImageViewもネイティブコントロールのため、ネイティブコントロールの上に画像を表示できます。

使用例

FUIImageView := FMX.iOS.UIImageView.TUIImageView.Create(Layout1);
FUIImageView.ImageWrapMode := TImageWrapMode.Fit;
FUIImageView.SetBitmap(Bitmap);

ソースコード

unit FMX.iOS.UIImageView;

interface

uses
  iOSapi.UIKit, iOSapi.CoreGraphics,
  System.Classes, System.Types, FMX.Graphics,
  FMX.Layouts, FMX.Forms, FMX.Objects;

type
  TUIImageView = class
  private
    FUIImageView: iOSapi.UIKit.UIImageView;
    FLayout: TLayout;
    FImageWrapMode: TImageWrapMode;
    procedure SetImageWrapMode(const Value: TImageWrapMode);
    function GetForm: TCustomForm;
    function GetLayoutRect: TRect;
    function getFrame: CGRect;
  public
    constructor Create(const ALayout: TLayout);
    destructor Destroy; override;
    procedure SetBitmap(const ABitmap: TBitmap);
    procedure Resize;
    property ImageWrapMode: TImageWrapMode read FImageWrapMode
      write SetImageWrapMode;
    property frame: CGRect read getFrame;
    property ImageView: iOSapi.UIKit.UIImageView read FUIImageView;

  end;

implementation

uses

  Macapi.Helpers, FMX.Helpers.iOS, iOSapi.Foundation, FMX.Platform.iOS,

  System.SysUtils, FMX.Types;

{ TUIImageView }

constructor TUIImageView.Create(const ALayout: TLayout);
var
  AForm: TCustomForm;
begin
  FLayout := ALayout;

  FUIImageView := iOSapi.UIKit.TUIImageView.Wrap
    (iOSapi.UIKit.TUIImageView.Alloc.init);
  FUIImageView.retain;
  Resize;

  AForm := GetForm;
  if AForm <> nil then
    WindowHandleToPlatform(AForm.Handle).View.addSubview(FUIImageView);

  Self.ImageWrapMode := TImageWrapMode.Stretch;
end;

destructor TUIImageView.Destroy;
begin
  if FUIImageView <> nil then
  begin
    FUIImageView.removeFromSuperview;
    FUIImageView.release;
    FUIImageView := nil;
  end;

  inherited;
end;

function TUIImageView.GetForm: TCustomForm;
var
  Parent: TFmxObject;
begin
  Parent := FLayout.Parent;

  while True do
  begin
    if Parent = nil then
      Exit(nil);
    if Parent is TForm then
      Exit(Parent as TForm);
    Parent := Parent.Parent;
  end;
end;

function TUIImageView.getFrame: CGRect;
begin
  Result := FUIImageView.frame;
end;

function TUIImageView.GetLayoutRect: TRect;
var
  ATopLeft, ABottomRight: TPoint;
begin
  if FLayout = nil then
  begin
    Log.d('Layout = nil');
    Exit(Rect(0, 0, 0, 0));
  end;

  ATopLeft := FLayout.LocalToAbsolute(PointF(0, 0)).Truncate;
  ABottomRight := ATopLeft.Add(PointF(FLayout.width, FLayout.height).Truncate);
  Result := System.Classes.Rect(ATopLeft, ABottomRight);
end;

procedure TUIImageView.Resize;
begin
  FUIImageView.setFrame(Macapi.Helpers.RectToNSRect(GetLayoutRect));
end;

procedure TUIImageView.SetBitmap(const ABitmap: TBitmap);
var
  Image: UIImage;
  AutoreleasePool: NSAutoreleasePool;
begin
  AutoreleasePool := TNSAutoreleasePool.Create;
  try
    Image := FMX.Helpers.iOS.BitmapToUIImage(ABitmap);
    FUIImageView.setImage(Image);
  finally
    AutoreleasePool.release
  end;
end;

procedure TUIImageView.SetImageWrapMode(const Value: TImageWrapMode);
begin
  FImageWrapMode := Value;

  case Value of
    TImageWrapMode.Fit: // コントロールの四角形にちょうど合うように調整します(画像の比率は保たれます)。
      FUIImageView.setContentMode(UIViewContentModeScaleAspectFit);
    TImageWrapMode.Stretch: // 画像を引き伸ばして、コントロールの四角形全体を埋めます。
      FUIImageView.setContentMode(UIViewContentModeScaleToFill);
    TImageWrapMode.Original: // 画像を元のサイズで表示します。
      FUIImageView.setContentMode(UIViewContentModeTopLeft);
    TImageWrapMode.Center: // 画像をコントロールの四角形の中央に表示します。
      FUIImageView.setContentMode(UIViewContentModeCenter);
    TImageWrapMode.Tile: // 画像を敷き詰めて(画像の数を増やして)、コントロールの四角形全体を覆います。
      raise System.SysUtils.ENotImplemented.Create('TImageWrapMode.Tile');
  end;
end;

end.

サンプルプログラム

TWebBrowserコントロールはネイティブコントロールのため、その上にコンポーネントを配置できません。

このサンプルプログラムでは、TWebBrowserコントロールの上にTUIImageViewを配置して、画像を表示します。

実行例は次のようになります。

Simulator Screen Shot 2016.06.20 0.30.50

フォームにTWebBrowserコントロールとTLayoutコントロールを配置します。
TLayoutコントロールはTUIImageViewの配置場所になります。

TUIImageView01

TUIImageView02

メニューの「プロジェクト」→「リソースと画像」を選択し、表示する画像を追加します。

TUIImageView03

今回使用した画像は次の画像です。

sample

画像を読み込む関数を作成します。

function GetImage: TBitmap;
var
  RS: TResourceStream;
begin
  Result := TBitmap.Create;
  RS := TResourceStream.Create(HInstance, 'PngImage_1', RT_RCDATA);
  try
    Result.LoadFromStream(RS);
  finally
    RS.Free;
  end;
end;

フォームにprivate変数にTUIImageViewを追加します。

type
  TForm1 = class(TForm)
    WebBrowser1: TWebBrowser;
    Layout1: TLayout;
    procedure FormCreate(Sender: TObject);
  private
    { private 宣言 }
    FUIImageView: TUIImageView;
  public
    { public 宣言 }
  end;

フォームのOnCreateイベントで、TWebBrowserコントロールにURLを指定してアクセスします。

procedure TForm1.FormCreate(Sender: TObject);
begin
  WebBrowser1.Navigate('http://www.gesource.jp/weblog/');
end;

フォームのOnCreateイベントにTUIImageViewを作成する処理を追加します。

uses
  FMX.Objects;

procedure TForm1.FormCreate(Sender: TObject);
var
  Bitmap: TBitmap;
begin
  WebBrowser1.Navigate('http://www.gesource.jp/weblog/');

  FUIImageView := FMX.iOS.UIImageView.TUIImageView.Create(Layout1);
  FUIImageView.ImageWrapMode := TImageWrapMode.Fit;

  Bitmap := GetImage;
  try
    FUIImageView.SetBitmap(Bitmap);
  finally
    Bitmap.Free;
  end;
end;

アプリケーションを実行すると、ブラウザの上に画像が表示されます。

Simulator Screen Shot 2016.06.20 0.30.50

ES2015 (ES6)のテンプレート文字列(Template Strings)を使う

Template Stringsはバッククォートでくくる

普通の文字列はシングルクォート(‘)やダブルクォート(“)で文字列をくくりますが、テンプレート文字列はバッククォート(`)で文字列をくくります。

let str1 = `Template Strings`;
console.log(str1); //=> Template Strings

Template Stringsは改行を含めることができる

テンプレート文字列には、改行を含めることができます。
改行を含む複数行の文字列を簡潔に記述できます。

let str1 = `Template
Strings`;
console.log(str1); //=> Template(改行)Strings

Template Stringsは変数を埋め込むことができる

テンプレート文字列は、変数を埋め込むことができます。
文字列や変数をを「+」を使って結合するよりも、わかりやすく記述できます。

let name = 'World';
let str1 = `Hello, ${name}`;
console.log(str1); //=> Hello, World

Template Stringsを関数に引数として渡すには

テンプレート文字列を関数に渡すときは、「関数名テンプレート文字列」の書式で渡します。

let name = 'World';
console.log(func`Hello ${name}.`);

このとき、「funcHello ${name}.」は次のように書いたのと同じになる。

func(['Hello ', '.'], name)

関数funcを次のように定義すると、

function func(strings, name) {
    console.log(strings);
    console.log(name);
    return strings[0] + name + strings[1];
}

実行結果はこのようになる。

[ 'Hello ', '.' ]
World
Hello World.

DelphiのiOSアプリで、URLスキームでアプリを起動する、アプリがなければウェブサイトを開く

sharedApplicationのcanOpenURLメソッドで、アプリをURLスキームで起動できるか調べます。
起動できるときはsharedApplicationのopenURLメソッドでアプリを起動します。
アプリを起動できないときは、ウェブサイトのURLをopenURLメソッドで開きます。

uses iOSapi.Helpers, Macapi.Helpers;

/// <summary>
/// URLスキームを呼び出す、なければURLを開く
/// </summary>
procedure OpenURLSchemaOrURL(const URLSchema, URL: string);
begin
  if TiOSHelper.SharedApplication.canOpenURL(StrToNSUrl(URLSchema)) then
    TiOSHelper.SharedApplication.openURL(StrToNSUrl(URLSchema))
  else
    TiOSHelper.SharedApplication.openURL(StrToNSUrl(URL));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenURLSchemaOrURL(
    // Google Maps for iOS アプリのURLスキーム
    'comgooglemapsurl://',
    // Google Mapのウェブサイト
    'https://www.google.co.jp/maps');
end;

関連

ECMAScript 2015(ES6)のletとconstについて

ECMAScript 2015 (ES6)のletとconstについて

varとlet,constのスコープについて

varを使って宣言した変数のスコープは、関数またはグローバルになります。

{
    var x = 'Hello';
}
console.log(x); // Hello

letやconstで宣言した変数のスコープは、ブロックスコープです。

{
    let x = 'Hello';
}
console.log(x); // エラー

letで宣言した変数xのスコープはブロックスコープのため、ブロックの外から変数xにアクセスできません。

letの重複チェック

varは同じ変数を何回でも宣言できます。

var i = 123;
var i = 456;
console.log(i); // 456

letやconstは同じ変数を再宣言するとエラーになります。

let i = 123;
let i = 456; // エラー
console.log(i);

constの変数の変更

constで宣言した変数は再代入できませんが、値を変更することはできます。

const i = 1;
i = 2; // 再代入できない
console.log(i);

次のように配列オブジェクトの要素を変更することはできます。

const i = [1, 2, 3];
i[1] = 4; // 値の変更はできる
console.log(i); // [ 1, 4, 3 ]

LinuxMintで画面をロックしないように設定をする

パソコンを一定時間使用しないと、画面がロックされ、ロックを解除するためにパスワードを入力することになります。

Linux Mint 17.3で画面をロックしないようにするには、次の手順で設定します。

  1. 「メニュー」→「設定」→「スクリーンセーバー」を選択します。

  2. 「スクリーンセーバーを起動したら画面をロックする」のチェックを外します。

linuxmint_screenlock01

linuxmint_screenlock02