なんだか、結構需要があるっぽい FireMonkey 用の TWebBrowser を作りました!というお話です。
FireMonkey で複数 Platform に対応するコントロールを作るためには「共通の要素を Interface 化する」という作業が必要になります。
そして、それぞれの Platform で、その Interface を実装してやります。
今回は、iOS / Android 用の TWebBrowser が継承しているのと同じ ICustomBrowser を継承して、Win/Mac 用の TWebBrowser を作りました。
また、FireMonkey のファイル名は、FMX.コントロール名.Platform.pas とする慣習があります。
ということで、今回は
- FMX.WebBrowser.Win.pas
- FMX.WebBrowser.Mac.pas
また、元々の TWebBrowser は iOS / Android 用なので Parent を設定するという処理がありません。
Parent が設定されたら ICustomBrowser を継承した WebBrowser を表示する必要があるので、それらを処理する TWebBrowserEx というコンポーネントも作りました。
また、もう一つ FireMonkey の慣習があって、各プラットフォーム用の Interface は、implementation 部に書き、interface 部には、それを登録する関数だけ宣言する、というモノがあります。
あまり好きでは無いのですが、今回は、それに習って Win/Mac 用のユニットには RegisterWebBrowserService と UnregisterWebBrowserService という関数だけ用意しました。
unit FMX.WebBrowser.Win;interfaceprocedure RegisterWebBrowserService;procedure UnRegisterWebBrowserService;implementation
unit FMX.WebBrowser.Mac;interfaceprocedure RegisterWebBrowserService;procedure UnRegisterWebBrowserService;implementation
この関数を呼ぶと、TPlatformServices に Win/Mac 用の WebBrowser コントロールを「生成するクラスのインスタンス」が登録される仕組みです。
ここで、WebBrowser コントロールのインスタンスを登録してはいけません!そうすると1個しかインスタンスが作れませんからね!
WebBrowser コントロールを生成するクラスのインスタンスを登録しておけば、そのインスタンスを取り出して、いくらでも WebBrowser を生成できます。
登録したインスタンスは TPlatformServices.Current.SupportsPlatformService メソッドで取り出せます。
RegisterWebBrowserService は initialization で呼ぶようにします。
initializationRegisterWebBrowserService;end.
これで、それぞれの Platform 用のユニットを読み込むと、Platform に適したコンポーネントが使えるようになります。
もちろん、それぞれの Platform 用のユニットは IFDEF を使って、どれを読み込むか制御します。
今回は、Win/Mac 用なので、下記のように制御しました。
usesSystem.Rtti{$IFDEF MSWINDOWS}, FMX.WebBrowser.Win{$ENDIF}{$IFDEF MACOS}, FMX.WebBrowser.Mac{$ENDIF};
これで、Win/Mac 用の WebBrowser を使えるようになりました。
しかし、WebBrowserEx には、WebBrowser を生成しているコードはありません!
生成するコードは、親の親である TCustomWebBrowser の Create で生成されています。
TPlatformServices に登録してあるので、こちらでは何もせずに、正しいインスタンスが生成されます。すばらしい!
ちなみに、軽く各 Platform の実装を説明すると、
Windows 用は、VCL の TWebBrowser を使っています。
IWebBrowserX を取り出したりとか、めんどくさいから!!
FireMonkey でも別に VCL のコントロールも使えてしまうのです。
FireMonkey は TForm だけが Window Handle を持っています。
Windows 用 WebBrowser は、VCL なので、独自に WindowHandle を持っています。
そのため、VCL の TWebBrowser より 上に FireMonkey のコントロールを載せることはできません!
今回は、TForm の指定された(Parent で)場所にコントロールが載っているように見せています。
単純に、Parent の Left,Top,Right,Bottom に合致するように TWebBrowser を作っているだけです。
OS X 用も基本的には同じ仕組みです。
ただ、こちらは VCL の TWebBrowser などないので、自分で WebView を実装しました…超大変だった…Obj-C から Delphi 用のファイルを作るのが。
これができたら、あとは実直に WebView を作るだけです。
で、結構はまってる人が居るみたいですが、StackOverflow に提示してあるコードは間違っています。
なぜか setHostWindow を使っていますが、これはレシーバ用の Host を決めるためのもので、Parent を設定するモノではありません!
実際には、下記のように addSubView を使って親を設定します。
procedure TMacWebBrowserService.UpdateContentFromControl;varView: NSView;Bounds: TRectF;beginif (FWebView <> nil) then beginif(FWebControl <> nil)and not (csDesigning in FWebControl.ComponentState)and (FForm <> nil)then beginBounds := GetBounds;View := WindowHandleToPlatform(FForm.Handle).View;View.addSubview(FWebView);if (SameValue(Bounds.Width, 0)) or (SameValue(Bounds.Height, 0)) thenFWebView.setHidden(True)else beginFWebView.setFrame(GetNSBounds);FWebView.setHidden(not FWebControl.ParentedVisible);end;endelseFWebView.setHidden(True);end;end;
これで、表示されるようになります。
実際に使うと、こんな感じ!
このようなものを公開していただき、大変重宝し、また感謝しています。
返信削除ですが一点、問題に遭遇しています。
FWebBrowser.LoadFromStrings(content, '');
のように使用すると、例外が発生し、何も表示されません。
何かわかりますでしょうか ?
0 CoreFoundation 0x980cb471 __raiseError + 193\
1 libobjc.A.dylib 0x99c37091 objc_exception_throw + 162\
2 CoreFoundation 0x980cfcb3 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275\
3 CoreFoundation 0x9801b5c2 ___forwarding___ + 1010\
4 CoreFoundation 0x9801b1ae _CF_forwarding_prep_0 + 14\
5 Project1 0x002cc033 TMethodImplementationIntercept + 1879535\
6 Project1 0x0051885f TMethodImplementationIntercept + 4290075\
7 Project1 0x00512826 TMethodImplementationIntercept + 4265442\
8 Project1 0x003512c6 TMethodImplementationIntercept + 2424962\
9 Project1 0x00232ca9 TMethodImplementationIntercept + 1251941\
10 Project1 0x004d3667 TMethodImplementationIntercept + 4006947\
11 Project1 0x0042fc59 TMethodImplementationIntercept + 3336725\
12 Project1 0x0042fe5b TMethodImplementationIntercept + 3337239\
13 Project1 0x00435fa4 TMethodImplementationIntercept + 3362144\
14 Project1 0x002cbcea TMethodImplementationIntercept + 1878694\
15 AppKit 0x90931ebb -[NSWindow sendEvent:] + 719\
16 AppKit 0x908d08fd -[NSApplication sendEvent:] + 4034\
17 AppKit 0x907151fc -[NSApplication run] + 823\
18 Project1 0x002cc033 TMethodImplementationIntercept + 1879535\
19 Project1 0x00430952 TMethodImplementationIntercept + 3340046\
20 Project1 0x0051a987 TMethodImplementationIntercept + 4298563\
21 ??? 0xbe00ef00 0x0 + 3187732224\
すみません、LoadFromStrings は XE6 から新たに追加された Interface Method なので、検証が不十分でした。
返信削除FMX.WebBrowser.Mac.pas の 208 行目 loadHTMLStrings の次の行に
FWebView.setFrame(CGRectFromRect(RectF(0, 0, 1, 1)));
を追加したらどうなるでしょうか?
変化ありませんね。
返信削除すみません。最初の投稿で、最も手がかりになる例外メッセージの先頭行を書き漏らしていました。
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[WebFrame loadHTMLString:URL:]: unrecognized selector sent to instance 0x5863830'
ありがとうございます。なるほど。loadHTMLString の引数がおかしいということですね。今すぐには直せませんが、近いうちに修正します。
削除この問題、修正しました。
削除お試し下さい。
直りました。ありがとうございます。
削除お礼が遅れたことをお詫び申し上げます。
hi.. i need to make a program with webbowser inside and this program need to go in win and mac.... i see this topic but i dont understand if i can use this.. and i dont understand how i can use this..
返信削除hi. I can't speak English.
削除but It is very easy to use TWebBrowserEx.
And Souce on GitHub.
Please, see below URL.
https://github.com/freeonterminate/delphi/tree/master/TWebBrowser
Ex.
procedure TForm1.FormCreate(Sender: TObject);
begin
FWebBrowserEx := TWebBrowserEx.Create(Self);
FWebBrowserEx.SetBounds(0, 0, 400, 400);
FWebBrowserEx.Parent := Self;
FWebBrowserEx.URL := 'http://google.com';
end;
If good, please introduce by a blog etc.
Since I cannot speak English.
If you are hurrying, please contact me twitter.
削除my twitter account @pik
日本語をよくず、Google翻訳で上げます。ご理解願います。
返信削除まず、あなたの労苦に感謝します。
XE7でエラーがナドラグヨ。
XE7も可能にお願い求めることができる。
TWebBrowserEx をご使用頂きありがとうございます。
削除XE7 対応は、まだしていなかったので、これから修正します。
ご連絡ありがとうございます!
XE7 に対応しました。
削除お試し下さい。
Thank you very much.
削除Please focus solution....
削除The Applicaiton made with TWebBrowserEx, The Application has 2-Focus System.
削除1.FireMonkey Focus.
2.NSWindow Focus.
But FireMonkey handling NSWindow Focus.
So, TWebBrowserEx can't handling Focus.
I'm thinking for solving to this Problem
Please wait...
Fixed !
削除Please try !
Hi, thank you for this OS X compatible firemonkey WebView!
返信削除There is a problem, mouse hover does not work.
If I go over a link with mouse, it does not underline the link.
What can be the problem? Do you know a solution?
The Applicaiton made with TWebBrowserEx, The Application has 2-Focus System.
削除1.FireMonkey Focus.
2.NSWindow Focus.
But FireMonkey handling NSWindow Focus.
So, TWebBrowserEx can't handling Focus.
I'm thinking for solving to this Problem
Please wait...
Fixed !
削除Please try !
not yet. The focus problem are still unclear in Windows. but after excuting alert function of Javascript, the problem seems to be disappeared just once.
返信削除Try it! Focus between FMX.TEdit and TWebbrowserEx
返信削除OK...
削除I confirmed the phenomenon...
Please Wait.
Fixed ! Please try !
削除こんにちは TWebbrowserExを提供されて とても感謝しています ありがとう
削除but the focus problem are still occured between FMX.TEdit and TWebbrowserEx in Windows.....
First I used mouse to focus on TWebbrowserEx then focus on TEdit but keyboard is not working on TEdit, please check it ありがとうございます
はじめまして。C++Builderで動かそうとして苦労してます。リンカに「idoc.objが見つからない」と怒られるところまでたどり着きました。今まで動作している例はあるのでしょうか?
返信削除idoc.pas は source\internet\idoc.pas にあるので、そこをインクルードパスに含めてください。
削除Can you tell me how to get the html code of the current page in your component?
返信削除Added New property HTMLSource !
削除Please try it.
Hi... OnDidFinishLoad and OnCanNavigate and OnShouldStartLoadWithRequest, and so on... are not work in OSX. (Rad studio 10.2.1 / sierra(xcode8) / el capitan(xcode7))
返信削除Could you fix it ?
Hi Kim
削除Since the desktop version of TWebBrowser comes out, TWebBrowserEx version upgrade will not be done.