#navi_header|JavaScript| ExtJS4.2 + Sencha Cmdの開発環境で、アイコン画像のカスタマイズについて練習してみます。 引き続き [[1291]], [[1292]] と同じ環境で練習します。 今回の実験環境: #pre||> Ubuntu 12.04 LTS (64bit) ext-4.2.1.883 (GPL版) Sencha Cmd v4.0.4.84 Ruby (rbenvにて ruby 2.0.0p481 をインストール済み) /work/www 以下をDocumentRootとしてApacheHTTPDで公開 /work/www/ext-4.2.1.883 にExtJSを展開済み /work/devtools 以下にSencha Cmdをインストール Webブラウザによる動作確認:Firefox 29 on Win7 Pro 日本語版 ||< 引き続き、"/work/www/extjs-tests/t1" を "${app.dir}" として参照します。 サンプルコード : https://github.com/msakamoto-sf/extjs42-senchacmdv4-exercise #more|| #outline|| ---- * ExtJSコンポーネント用のアイコンを変更する (= テーマのカスタマイズ) ExtJSのテーマには、ExtJSコンポーネントが使うアイコンが含まれています。新しくテーマを作成する際に、継承元のテーマからそれらのアイコンを引き継ぎます。 今回はサンプルとして、ExtJSの公式ドキュメントでもサンプルとして例示されている、Ext.window.MessageBox の Ext.MessageBox.INFO アイコンを試してみます。 [[1292]] で作った時点での MyApp.view.sub.FormDemo に、以下のように Ext.MessageBox.INFO アイコンを使ったMessageBoxの表示用ボタンを追加します。 "${app.dir}/app/view/sub/FormDemo.js": #code|javascript|> Ext.define("MyApp.view.sub.FormDemo", { extend: 'Ext.form.Panel', xtype: 'sub-formdemo', frame: true, title: 'Form Demo', bodyPadding: 10, autoScroll:true, defaultType: 'textfield', defaults : { anchor: '100%' }, initComponent: function() { this.items = [ { /* ... */ }]; this.buttons = [ { text: 'Show MessageBox', handler: function() { Ext.Msg.show({ title: 'Info', msg: 'Message Box with custom icon', buttons: Ext.MessageBox.OK, icon: Ext.MessageBox.INFO }); } } ]; this.callParent(); } }); ||< 今回の勉強シリーズでは "/work/www/" がDocumentRootとして公開されてますので、そのまま "/extjs-tests/t1/" のURLパスでアクセスしてみました。"Show MessageBox"のボタンが追加され、クリックすると以下のようにINFOアイコンでメッセージボックスが表示されます。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_01_neptune.png) 前回までの練習と、今回使っている "my-custom-theme" はNeptuneテーマを継承したテーマです。よって、上の図で表示されているINFOアイコンはNeptuneテーマが提供しているアイコン画像となり、CSSでは以下のURLが指定されています。 url("images/shared/icon-info.png") 実際にブラウザでアクセスすると、以下のURLで画像がロードされていました。直近でビルドしたのが "testing" 環境でしたので、そちらのURLとなっています。 http://192.168.250.103/extjs-tests/t1/build/testing/MyApp/resources/images/shared/icon-info.png このアイコン画像を変更してみます。ExtJS 4.2 の公式ドキュメントで "Components" の解説に "Theming" というページがあり、そこでサンプルとして載っている以下のアイコン画像を使ってみます。 &image(yb://images/20140607_extjs42_icon_customize/icon-info.png) これを "my-custome-theme" のディレクトリ中に、以下のファイル名となるよう配置します。"images/shared/" ディレクトリはテーマを作成した直後には存在しないので、自分で追加しておきます。 ${app.dir}/packages/my-custom-theme/resources/images/shared/icon-info.png" "sencha app build" でビルドしてみます。 msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ sencha app build msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ sencha app build testing 念のためブラウザのキャッシュをクリアし、それぞれの環境用のビルド結果を確認してみると、以下のようにINFOアイコン画像が変化しました。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_02_icon-info.png) URLは以下のようになり、カスタマイズ前と同じです。 http://192.168.250.103/extjs-tests/t1/build/testing/MyApp/resources/images/shared/icon-info.png gitで変更されたファイルを確認してみると、確かにicon-info.pngがmodifyされ、開発者が用意した新しいアイコン画像に入れ替わったことが分かります。 #pre||> msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ git status # On branch master # Changes not staged for commit: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: app/view/sub/FormDemo.js # modified: build/production/MyApp/app.js # modified: build/production/MyApp/resources/images/shared/icon-info.png # modified: build/testing/MyApp/app.js # modified: build/testing/MyApp/resources/images/shared/icon-info.png # # Untracked files: # (use "git add ..." to include in what will be committed) # # packages/my-custom-theme/resources/images/ ||< * アプリ独自のアイコン画像を使う (iconプロパティ版) テーマの提供するアイコンは、ExtJSコンポーネント用のものに限定されます。アプリ独自のアイコンは、開発者側で準備する必要があります。 今回は、Buttonのiconプロパティを使って、URLでアプリ独自のアイコン画像を指定してみます。 アプリ用のアイコン画像など静的ファイルは、以下のディレクトリに配置すると、ビルド時に "resources/" の下に展開されます。 ${app.dir}/resources/ 練習用のアイコン画像は、FAMFAMFAMの "Silk" アイコンから "star.png" : &image(yb://images/20140607_extjs42_icon_customize/star.png) を拝借します。 http://www.famfamfam.com/lab/icons/silk/ このアイコン画像を、以下のディレクトリに配置します。"images/app/" というディレクトリ名は、著者が自分で考えたディレクトリ名で、特にExtJS側でのルールは無いようです。ただし、テーマ側の画像と一緒になりますので、衝突しないようなディレクトリに調整する必要はありそうです。 ${app.dir}/resources/images/app/star.png 続いて、FormDemoで以下のようにボタンの "icon" プロパティに、 "${app.dir}" からの相対パスでアイコン画像のURLを指定します。 "${app.dir}/app/view/sub/FormDemo.js": #code|javascript|> Ext.define("MyApp.view.sub.FormDemo", { extend: 'Ext.form.Panel', xtype: 'sub-formdemo', frame: true, title: 'Form Demo', bodyPadding: 10, autoScroll:true, defaultType: 'textfield', defaults : { anchor: '100%' }, initComponent: function() { this.items = [ { /* ... */ }]; this.buttons = [ { text: 'Show MessageBox', // 追加 icon: 'resources/images/app/star.png', handler: function() { /* ... */ ||< この段階で "${app.dir}/index.html" で確認してみると、以下のように "star.png" が "Show MessageBox" のアイコン画像として表示されます。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_03_icon-prop1.png) ビルドしてみます。 msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ sencha app build msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ sencha app build testing ビルド結果をブラウザで確認すると、testing環境の例では以下のように "star.png" が表示されるようになりました。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_03_icon-prop2.png) DOM要素を見てみると、以下のように "x-btn-icon-el" というクラスのspan要素について、style属性の "background-image" によりアイコン画像をロードしていることが分かります。(iconプロパティを指定する前は、"x-btn-icon-el" クラスのspan要素があるにはありましたが、style属性が空文字列になっていました。) &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_03_icon-prop3.png) "star.png" のURLを確認すると以下のようになっていて、"resources/"の下にまとめられたことが分かります。 http://192.168.250.103/extjs-tests/t1/build/testing/MyApp/resources/images/app/star.png "git add ."で追加した後に変更状態を確認すると、それぞれのビルド出力先の "resources/" 以下に "star.png" が追加されています。 #pre||> msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ git status # On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: app/view/sub/FormDemo.js # modified: build/production/MyApp/app.js # new file: build/production/MyApp/resources/images/app/star.png # modified: build/testing/MyApp/app.js # new file: build/testing/MyApp/resources/images/app/star.png # new file: resources/images/app/star.png ||< * アプリ独自のアイコン画像を使う (iconClsプロパティ版) 引き続きアプリ独自のアイコン画像を追加してみますが、今度はButtonのiconClsプロパティを使ってみます。 iconプロパティ版と同様に、FAMFAMFAMの "Silk" アイコンから "accept.png" : &image(yb://images/20140607_extjs42_icon_customize/accept.png) を拝借し、以下の場所に配置します。 ${app.dir}/resources/images/app/accept.png 続いて、FormDemoで以下のようにボタンの "iconCls" プロパティを使うボタンを追加します。クラス名の "myapp-icon-accept" というのは、名前空間がアプリごとに分離されるよう意識して自分で決めています。ExtJS側でアプリ固有のCSSクラスについて命名規約をルール化しているかどうかは、分かりませんでした。 "${app.dir}/app/view/sub/FormDemo.js": #code|javascript|> Ext.define("MyApp.view.sub.FormDemo", { extend: 'Ext.form.Panel', xtype: 'sub-formdemo', frame: true, title: 'Form Demo', bodyPadding: 10, autoScroll:true, defaultType: 'textfield', defaults : { anchor: '100%' }, initComponent: function() { this.items = [ { /* ... */ }]; this.buttons = [ // 追加 { text: 'iconCls demo', iconCls: 'myapp-icon-accept' }, { text: 'Show MessageBox', /* ... */ ||< この段階で "${app.dir}/index.html" を表示しても、以下のようにアイコン画像が表示される場所が空いただけになっています。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_04_iconCls-prop1.png) DOM要素を確認してみると、以下のように "x-btn-icon-el" というクラスのspan要素で "myapp-icon-accept" クラスが追加されていました。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_04_iconCls-prop2.png) この後CSSのルールを追加しますが、 [[1292]] でのアプリ独自CSS変数を定義したように、 "${app.dir}/sass" 以下のディレクトリで作業します。CSSのクラス指定のルールは "${app.dir}/sass/src/" 以下にファイルを作成して定義します。 前回にならって、Application.scss ファイルを以下の内容で作成します。 "${app.dir}/sass/src/Application.scss": #pre||> .myapp-icon-accept { background-image: url(images/app/accept.png); } ||< ビルドしてみます。 msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ sencha app build msakamoto@dev1-u1204lts-x64:/work/www/extjs-tests/t1$ sencha app build testing ビルド結果をブラウザで確認すると、testing環境の例では以下のように "accept.png" が表示されるようになりました。 &image(yb://images/20140607_extjs42_icon_customize/extjs42-icon-customize_04_iconCls-prop3.png) ロードしているURLを確認すると、以下のようにビルド先の "resources/images/app/accept.png" になっていました。 http://192.168.250.103/extjs-tests/t1/build/testing/MyApp/resources/images/app/accept.png ここでscssファイルで指定したurl()について、どの相対パスで指定したら良いか、実はいくつか試行錯誤しています。 NG例1: background-image: url(resources/images/app/accept.png); → /t1/build/testing/MyApp/resources/resources/images/app/accept.png NG例2: background-image: url(../images/app/accept.png); → /t1/build/testing/MyApp/images/app/accept.png どこからの相対パスで指定すれば良いかですが、Sencha Cmdのビルドシステムの場合は、最終的にはCSSファイルは以下の場所に一ファイルに結合されて出力されます。 ${app.dir}/build/(環境)/(アプリ名)/resources/(アプリ名)-all.css そして、CSS中のurl()で相対パスを扱う場合、そのスタイルシートの位置からの相対パスになります。 - html - Using relative URL in CSS file, what location is it relative to? - Stack Overflow -- http://stackoverflow.com/questions/940451/using-relative-url-in-css-file-what-location-is-it-relative-to - Quick Reminder About File Paths | CSS-Tricks -- http://css-tricks.com/quick-reminder-about-file-paths/ 以上より、 ${app.dir}/resources/images/app/accept.png → ${app.dir}/build/(環境)/(アプリ名)/resources/images/app/accept.png になり、CSSは ${app.dir}/build/(環境)/(アプリ名)/resources/(アプリ名)-all.css に配置されるので、url()で相対パスを指定するには(アプリ名)-all.cssからの相対パスとなるため、 url(images/app/accept.png) が正解となります。 なお、"!important"については、今回試しに付けずにやってみたら上手く行ったのでそのままにしてます。CSS関連は詳しくないのですが、他の人のblog記事では "!important" をつけてる記事もありましたので、場合によっては必要なケースもあるかもしれません。 #navi_footer|JavaScript|