Merge branch 'master' of github.com:buggins/dlangui

This commit is contained in:
Vadim Lopatin 2015-01-14 20:47:47 +03:00
commit 2c855c4a97
85 changed files with 1391 additions and 970 deletions

View File

@ -89,7 +89,6 @@
<resfile />
<exefile>$(OutDir)\$(ProjectName).lib</exefile>
<useStdLibPath>1</useStdLibPath>
<cRuntime>2</cRuntime>
<additionalOptions />
<preBuildCommand />
<postBuildCommand />
@ -184,7 +183,6 @@
<resfile />
<exefile>$(OutDir)\$(ProjectName).lib</exefile>
<useStdLibPath>1</useStdLibPath>
<cRuntime>1</cRuntime>
<additionalOptions />
<preBuildCommand />
<postBuildCommand />
@ -357,6 +355,7 @@
<File path="src\dlangui\dialogs\msgbox.d" />
</Folder>
<Folder name="graphics">
<File path="src\dlangui\graphics\colors.d" />
<File path="src\dlangui\graphics\drawbuf.d" />
<File path="src\dlangui\graphics\fonts.d" />
<File path="src\dlangui\graphics\ftfonts.d" />

View File

@ -89,7 +89,6 @@
<resfile />
<exefile>$(OutDir)\$(ProjectName).exe</exefile>
<useStdLibPath>1</useStdLibPath>
<cRuntime>2</cRuntime>
<additionalOptions>-profile</additionalOptions>
<preBuildCommand />
<postBuildCommand />
@ -184,7 +183,6 @@
<resfile />
<exefile>$(OutDir)\$(ProjectName).exe</exefile>
<useStdLibPath>1</useStdLibPath>
<cRuntime>1</cRuntime>
<additionalOptions />
<preBuildCommand />
<postBuildCommand />

View File

@ -424,15 +424,34 @@ extern (C) int UIAppMain(string[] args) {
}
{
LinearLayout layout3 = new LinearLayout("tab3");
LinearLayout layout3 = new VerticalLayout("tab3");
// 3 types of buttons: Button, ImageButton, ImageTextButton
layout3.addChild(new TextWidget(null, "Buttons in HorizontalLayout"d));
WidgetGroup buttons1 = new HorizontalLayout();
buttons1.addChild(new Button("btn1", "Button 1"d));
buttons1.addChild(new Button("btn2", "Button 2"d));
buttons1.addChild(new Button("btn3", "Button 3"d));
buttons1.addChild(new ResizerWidget());
buttons1.addChild(new Button("btn4", "Button 4"d));
buttons1.addChild(new TextWidget(null, "Button widgets: "d));
buttons1.addChild(new Button("btn1", "Button"d));
buttons1.addChild((new Button("btn2", "Disabled Button"d)).enabled(false));
buttons1.addChild(new TextWidget(null, "ImageButton widgets: "d));
buttons1.addChild(new ImageButton("btn3", "text-plain"));
buttons1.addChild(new TextWidget(null, "disabled: "d));
buttons1.addChild((new ImageButton("btn4", "folder")).enabled(false));
buttons1.addChild(new TextWidget(null, "ImageTextButton widgets: "d));
buttons1.addChild(new ImageTextButton("btn5", "text-plain", "Enabled"d));
buttons1.addChild((new ImageTextButton("btn6", "folder", "Disabled"d)).enabled(false));
layout3.addChild(buttons1);
WidgetGroup buttons11 = new HorizontalLayout();
buttons11.addChild(new TextWidget(null, "Construct buttons by action (Button, ImageButton, ImageTextButton): "d));
Action FILE_OPEN_ACTION = new Action(ACTION_FILE_OPEN, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control);
buttons11.addChild(new Button(FILE_OPEN_ACTION));
buttons11.addChild(new ImageButton(FILE_OPEN_ACTION));
buttons11.addChild(new ImageTextButton(FILE_OPEN_ACTION));
buttons11.addChild(new TextWidget(null, "The same in disabled state: "d));
buttons11.addChild((new Button(FILE_OPEN_ACTION)).enabled(false));
buttons11.addChild((new ImageButton(FILE_OPEN_ACTION)).enabled(false));
buttons11.addChild((new ImageTextButton(FILE_OPEN_ACTION)).enabled(false));
layout3.addChild(buttons11);
layout3.addChild(new VSpacer());
layout3.addChild(new TextWidget(null, "CheckBoxes in HorizontalLayout"d));
WidgetGroup buttons2 = new HorizontalLayout();
@ -463,7 +482,7 @@ extern (C) int UIAppMain(string[] args) {
layout3.addChild(new VSpacer());
layout3.addChild(new TextWidget(null, "In vertical layouts:"d));
HorizontalLayout hlayout2 = new HorizontalLayout();
hlayout2.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
hlayout2.layoutHeight(FILL_PARENT); //layoutWidth(FILL_PARENT).
buttons1 = new VerticalLayout();
buttons1.addChild(new TextWidget(null, "Buttons"d));
@ -528,13 +547,19 @@ extern (C) int UIAppMain(string[] args) {
table.addChild((new TextWidget(null, "Parameter 2 name bla bla"d)).alignment(Align.Right | Align.VCenter));
table.addChild((new EditLine("edit2", "Some text for parameter 2"d)).layoutWidth(FILL_PARENT));
// row 3
table.addChild((new TextWidget(null, "Param 3"d)).alignment(Align.Right | Align.VCenter));
table.addChild((new EditLine("edit3", "Parameter 3 value"d)).layoutWidth(FILL_PARENT));
table.addChild((new TextWidget(null, "Param 3 is disabled"d)).alignment(Align.Right | Align.VCenter).enabled(false));
table.addChild((new EditLine("edit3", "Parameter 3 value"d)).layoutWidth(FILL_PARENT).enabled(false));
// normal readonly combo box
ComboBox combo1 = new ComboBox("combo1", ["item value 1"d, "item value 2"d, "item value 3"d, "item value 4"d, "item value 5"d, "item value 6"d]);
table.addChild((new TextWidget(null, "Combo box param"d)).alignment(Align.Right | Align.VCenter));
combo1.selectedItemIndex(3);
combo1.selectedItemIndex = 3;
table.addChild(combo1).layoutWidth(FILL_PARENT);
// disabled readonly combo box
ComboBox combo2 = new ComboBox("combo2", ["item value 1"d, "item value 2"d, "item value 3"d]);
table.addChild((new TextWidget(null, "Disabled combo box"d)).alignment(Align.Right | Align.VCenter));
combo2.enabled = false;
combo2.selectedItemIndex = 0;
table.addChild(combo2).layoutWidth(FILL_PARENT);
table.margins(Rect(10,10,10,10)).layoutWidth(FILL_PARENT);
tabs.addTab(table, "TAB_TABLE_LAYOUT"c);
@ -639,11 +664,11 @@ extern (C) int UIAppMain(string[] args) {
table2.addChild((new TextWidget(null, "Param 5 - edit text here - blah blah blah"d)).alignment(Align.Right | Align.VCenter));
table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
// row 6
table2.addChild((new TextWidget(null, "Param 6 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
table2.addChild((new TextWidget(null, "Param 6 - just to fill content widget (DISABLED)"d)).alignment(Align.Right | Align.VCenter).enabled(false));
table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT).enabled(false));
// row 7
table2.addChild((new TextWidget(null, "Param 7 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));
table2.addChild((new TextWidget(null, "Param 7 - just to fill content widget (DISABLED)"d)).alignment(Align.Right | Align.VCenter).enabled(false));
table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT).enabled(false));
// row 8
table2.addChild((new TextWidget(null, "Param 8 - just to fill content widget"d)).alignment(Align.Right | Align.VCenter));
table2.addChild((new EditLine("edit3", "Parameter 5 value"d)).layoutWidth(FILL_PARENT));

18
res/btn_background.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/btn_pressed" />
<item android:state_focused="true" android:state_default="true" android:state_enabled="true"
android:drawable="@drawable/btn_default" />
<item android:state_focused="true" android:state_enabled="true"
android:drawable="@drawable/btn_normal" />
<item android:state_enabled="true" android:state_default="true"
android:drawable="@drawable/btn_default" />
<item android:state_enabled="true" android:state_hovered="true"
android:drawable="@drawable/btn_hover" />
<item android:state_enabled="true"
android:drawable="@drawable/btn_normal" />
<item
android:drawable="@drawable/btn_disabled" />
</selector>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@null"
android:state_enabled="false"/>
<item
android:drawable="@null"
android:state_focused="true" />
<item
android:drawable="btn_pressed"
android:state_pressed="true" />
<item
android:drawable="btn_hover"
android:state_hovered="true" android:state_enabled="true" />
<item
android:drawable="@null" />
</selector>

View File

@ -1,65 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Enabled states -->
<item android:state_checked="true" android:state_window_focused="false"
android:state_enabled="true"
android:drawable="@drawable/btn_check_on_holo_light" />
<item android:state_checked="false" android:state_window_focused="false"
android:state_enabled="true"
android:drawable="@drawable/btn_check_off_holo_light" />
<item android:state_checked="true" android:state_pressed="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_on_pressed_holo_light" />
android:drawable="@drawable/btn_check_on_pressed" />
<item android:state_checked="false" android:state_pressed="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_off_pressed_holo_light" />
android:drawable="@drawable/btn_check_off_pressed" />
<item android:state_checked="true" android:state_focused="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_on_focused_holo_light" />
android:drawable="@drawable/btn_check_on_focused" />
<item android:state_checked="false" android:state_focused="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_off_focused_holo_light" />
android:drawable="@drawable/btn_check_off_focused" />
<item android:state_checked="true" android:state_hovered="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_on_focused" />
<item android:state_checked="false" android:state_hovered="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_off_focused" />
<item android:state_checked="false"
android:state_enabled="true"
android:drawable="@drawable/btn_check_off_holo_light" />
android:drawable="@drawable/btn_check_off" />
<item android:state_checked="true"
android:state_enabled="true"
android:drawable="@drawable/btn_check_on_holo_light" />
android:drawable="@drawable/btn_check_on" />
<!-- Disabled states -->
<item android:state_checked="true" android:state_window_focused="false"
android:drawable="@drawable/btn_check_on_disabled_holo_light" />
<item android:state_checked="false" android:state_window_focused="false"
android:drawable="@drawable/btn_check_off_disabled_holo_light" />
<item android:state_checked="true"
android:drawable="@drawable/btn_check_on_disabled" />
<item android:state_checked="false"
android:drawable="@drawable/btn_check_off_disabled" />
<item android:state_checked="true" android:state_focused="true"
android:drawable="@drawable/btn_check_on_disabled_focused_holo_light" />
<item android:state_checked="false" android:state_focused="true"
android:drawable="@drawable/btn_check_off_disabled_focused_holo_light" />
<item android:state_checked="false" android:drawable="@drawable/btn_check_off_disabled_holo_light" />
<item android:state_checked="true" android:drawable="@drawable/btn_check_on_disabled_holo_light" />
<item android:state_checked="false" android:drawable="@drawable/btn_check_off" />
<item android:state_checked="true" android:drawable="@drawable/btn_check_on" />
</selector>

View File

@ -1,59 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:state_window_focused="false"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_on_holo_light" />
<item android:state_checked="false" android:state_window_focused="false"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_off_holo_light" />
<item android:state_checked="true" android:state_pressed="true"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_on_pressed_holo_light" />
android:drawable="@drawable/btn_radio_on_pressed" />
<item android:state_checked="false" android:state_pressed="true"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_off_pressed_holo_light" />
android:drawable="@drawable/btn_radio_off_pressed" />
<item android:state_checked="true" android:state_focused="true"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_on_focused_holo_light" />
android:drawable="@drawable/btn_radio_on_focused" />
<item android:state_checked="false" android:state_focused="true"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_off_focused_holo_light" />
android:drawable="@drawable/btn_radio_off_focused" />
<item android:state_checked="true" android:state_hovered="true"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_on_focused" />
<item android:state_checked="false" android:state_hovered="true"
android:state_enabled="true"
android:drawable="@drawable/btn_radio_off_focused" />
<item android:state_checked="false" android:state_enabled="true"
android:drawable="@drawable/btn_radio_off_holo_light" />
android:drawable="@drawable/btn_radio_off" />
<item android:state_checked="true" android:state_enabled="true"
android:drawable="@drawable/btn_radio_on_holo_light" />
android:drawable="@drawable/btn_radio_on" />
<!-- Disabled states -->
<item android:state_checked="true" android:state_window_focused="false"
android:drawable="@drawable/btn_radio_on_disabled_holo_light" />
<item android:state_checked="false" android:state_window_focused="false"
android:drawable="@drawable/btn_radio_off_disabled_holo_light" />
<item android:state_checked="true" android:state_focused="true"
android:drawable="@drawable/btn_radio_on_disabled_focused_holo_light" />
<item android:state_checked="false" android:state_focused="true"
android:drawable="@drawable/btn_radio_off_disabled_focused_holo_light" />
<item android:state_checked="false" android:drawable="@drawable/btn_radio_off_disabled_holo_light" />
<item android:state_checked="true" android:drawable="@drawable/btn_radio_on_disabled_holo_light" />
<item android:state_checked="false" android:drawable="@drawable/btn_radio_off_disabled" />
<item android:state_checked="true" android:drawable="@drawable/btn_radio_on_disabled" />
</selector>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/btn_pressed" />
<item android:state_focused="true" android:state_default="true" android:state_enabled="true"
android:drawable="@drawable/btn_default" />
<item android:state_focused="true" android:state_enabled="true"
android:drawable="@drawable/btn_default" />
<item android:state_enabled="true" android:state_default="true"
android:drawable="@drawable/btn_default" />
<item android:state_enabled="true" android:state_hovered="true"
android:drawable="@drawable/btn_hover" />
<item android:state_enabled="true"
android:drawable="@drawable/btn_normal" />
<item
android:drawable="@drawable/btn_disabled" />
</selector>

View File

@ -1,26 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/assets/res/any/drawable/editbox_background.xml
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/editbox_background_disabled_focus_yellow" />
<item android:state_focused="true" android:drawable="@drawable/editbox_background_focus_yellow" />
<item android:state_enabled="false" android:drawable="@drawable/editbox_background_disabled" />
<item android:drawable="@drawable/editbox_background_normal" />
<item android:state_focused="true" android:state_enabled="false" android:drawable="editbox_background_disabled_focus" />
<item android:state_focused="true" android:drawable="editbox_background_focus" />
<item android:state_enabled="false" android:drawable="editbox_background_disabled" />
<item android:drawable="editbox_background_normal" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 B

BIN
res/mdpi/btn_check_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/mdpi/btn_check_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
res/mdpi/btn_default.9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
res/mdpi/btn_disabled.9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

BIN
res/mdpi/btn_hover.9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

BIN
res/mdpi/btn_normal.9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

BIN
res/mdpi/btn_pressed.9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

BIN
res/mdpi/btn_radio_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

BIN
res/mdpi/btn_radio_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 216 B

View File

@ -1,25 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<theme id="theme_default" fontSize="15" >
<style id="BUTTON"
backgroundImageId="btn_default_small"
backgroundImageId="btn_background"
align="Center"
margins="5,5,5,5"
/>
focusRectColors="#000"
textFlags="UnderlineHotKeys"
>
<state state_enabled="true" state_hovered="true" textColor="#006080"/>
<state state_enabled="false" textColor="#80000000"/>
</style>
<style id="BUTTON_TRANSPARENT"
backgroundImageId="btn_default_small_transparent"
backgroundImageId="btn_background_transparent"
align="Center"
/>
<style id="BUTTON_LABEL"
layoutWidth="FILL_PARENT"
margins="2,2,2,2"
align="Left|VCenter"
/>
<style id="BUTTON_ICON"
textFlags="UnderlineHotKeys"
>
<state state_enabled="false" textColor="#80000000"/>
<state state_enabled="true" state_hovered="true" textColor="#006080"/>
</style>
<style id="BUTTON_IMAGE"
margins="2,2,2,2"
align="Center"
/>
<style id="CHECKBOX"
backgroundImageId="@null"
margins="2,2,2,2"
padding="2,2,2,2"
focusRectColors="@null"
/>
<style id="CHECKBOX_IMAGE" parent="BUTTON_IMAGE"
align="Center"
margins="2,2,2,2"
/>
<style id="CHECKBOX_LABEL" parent="BUTTON_LABEL"
align="Left|VCenter"
focusRectColors="#000"
>
<state state_enabled="true" state_hovered="true" textColor="#006080"/>
</style>
<style id="RADIOBUTTON" parent="CHECKBOX"
margins="2,2,2,2"
padding="2,2,2,2"
/>
<style id="RADIOBUTTON_IMAGE" parent="CHECKBOX_IMAGE"
align="Center"
margins="2,2,2,2"
/>
<style id="RADIOBUTTON_LABEL" parent="CHECKBOX_LABEL"
align="Left|VCenter"
>
<state state_enabled="true" state_hovered="true" textColor="#006080"/>
</style>
<style id="TEXT"
margins="2,2,2,2"
padding="1,1,1,1"
/>
align="Left|VCenter"
>
<state state_enabled="false" textColor="#A0000000"/>
</style>
<style id="HSPACER"
layoutWidth="FILL_PARENT"
layoutWeight="100"
@ -31,9 +74,13 @@
minHeight="5"
/>
<style id="BUTTON_NOMARGINS"
backgroundImageId="btn_default_small"
backgroundImageId="btn_background"
align="Center"
/>
<color id="sample_color_rgb" value="#8CF"/>
<color id="sample_color_argb" value="#48CF"/>
<color id="sample_color_rrggbb" value="#80C0F0"/>
<color id="sample_color_aarrggbb" value="#4080C0F0"/>
<drawable id="scrollbar_button_up" value="scrollbar_btn_up"/>
<drawable id="scrollbar_button_down" value="scrollbar_btn_down"/>
<drawable id="scrollbar_button_left" value="scrollbar_btn_left"/>
@ -134,43 +181,39 @@
<style id="LIST_ITEM"
backgroundImageId="list_item_background"
/>
<style id="EDIT_LINE"
backgroundImageId="editbox_background"
padding="5,6,5,6"
margins="2,2,2,2"
minWidth="40"
fontFace="Arial"
fontFamily="SansSerif"
fontSize="16"
/>
<style id="COMBO_BOX"
backgroundImageId="editbox_background"
backgroundImageId="combobox_background"
padding="2,2,2,2"
margins="2,2,2,2"
minWidth="40"
fontFace="Arial"
fontFamily="SansSerif"
fontSize="16"
/>
<style id="COMBO_BOX_BODY"
padding="2,2,2,2"
minWidth="40"
fontFace="Arial"
fontFamily="SansSerif"
fontSize="16"
align="Left|VCenter"
focusRectColors="#000"
/>
<style id="COMBO_BOX_BUTTON"
padding="2,2,2,2"
backgroundImageId="btn_background_transparent"
align="Center"
/>
<style id="EDIT_LINE"
backgroundImageId="editbox_background"
padding="4,4,4,4"
margins="2,2,2,2"
minWidth="40"
/>
<style id="EDIT_BOX"
backgroundImageId="editbox_background"
padding="5,6,5,6"
padding="2,2,2,2"
margins="2,2,2,2"
minWidth="100"
minHeight="60"
layoutWidth="FILL_PARENT"
layoutHeight="FILL_PARENT"
fontFace="Courier New"
fontFamily="MonoSpace"
fontSize="16"
/>
<style id="TREE_ITEM"
padding="2,2,2,2"

View File

@ -64,6 +64,7 @@ public import dlangui.widgets.grid;
public import dlangui.widgets.tree;
public import dlangui.widgets.combobox;
public import dlangui.widgets.popup;
public import dlangui.graphics.colors;
public import dlangui.graphics.fonts;
public import dlangui.graphics.drawbuf;
public import dlangui.platforms.common.platform;

View File

@ -221,3 +221,82 @@ struct Collection(T, bool ownItems = false) {
}
}
/** object list holder, owning its objects - on destroy of holder, all own objects will be destroyed */
struct ObjectList(T) {
protected T[] _list;
protected int _count;
/** returns count of items */
@property int count() const { return _count; }
/** get item by index */
T get(int index) {
assert(index >= 0 && index < _count, "child index out of range");
return _list[index];
}
/// get item by index
T opIndex(int index) {
return get(index);
}
/** add item to list */
T add(T item) {
if (_list.length <= _count) // resize
_list.length = _list.length < 4 ? 4 : _list.length * 2;
_list[_count++] = item;
return item;
}
/** add item to list */
T insert(T item, int index = -1) {
if (index > _count || index < 0)
index = _count;
if (_list.length <= _count) // resize
_list.length = _list.length < 4 ? 4 : _list.length * 2;
for (int i = _count; i > index; i--)
_list[i] = _list[i - 1];
_list[index] = item;
_count++;
return item;
}
/** find child index for item, return -1 if not found */
int indexOf(T item) {
for (int i = 0; i < _count; i++)
if (_list[i] == item)
return i;
return -1;
}
/** find child index for item by id, return -1 if not found */
static if (__traits(hasMember, T, "compareId")) {
int indexOf(string id) {
for (int i = 0; i < _count; i++)
if (_list[i].compareId(id))
return i;
return -1;
}
}
/** remove item from list, return removed item */
T remove(int index) {
assert(index >= 0 && index < _count, "child index out of range");
T item = _list[index];
for (int i = index; i < _count - 1; i++)
_list[i] = _list[i + 1];
_count--;
return item;
}
/** Replace item with another value, destroy old value. */
void replace(T item, int index) {
T old = _list[index];
_list[index] = item;
destroy(old);
}
/** remove and destroy all items */
void clear() {
for (int i = 0; i < _count; i++) {
destroy(_list[i]);
_list[i] = null;
}
_count = 0;
}
~this() {
clear();
}
}

View File

@ -52,7 +52,7 @@ class Dialog : VerticalLayout {
Signal!DialogResultHandler onDialogResult;
this(UIString caption, Window parentWindow = null, uint flags = DialogFlag.Modal) {
super("dlg");
super("dialog-main-widget");
_caption = caption;
_parentWindow = parentWindow;
_flags = flags;
@ -155,8 +155,11 @@ class Dialog : VerticalLayout {
uint wflags = 0;
if (_flags & DialogFlag.Modal)
wflags |= WindowFlag.Modal;
if (_flags & DialogFlag.Resizable)
if (_flags & DialogFlag.Resizable) {
wflags |= WindowFlag.Resizable;
layoutWidth = FILL_PARENT;
layoutHeight = FILL_PARENT;
}
_window = Platform.instance.createWindow(_caption, _parentWindow, wflags);
if (_window && _icon)
_window.windowIcon = drawableCache.getImage(_icon);

View File

@ -133,13 +133,6 @@ class FileDialog : Dialog, CustomGridCellAdapter {
return null;
}
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) {
super.layout(rc);
_fileList.autoFitColumnWidths();
_fileList.fillColumnWidth(1);
}
protected bool upLevel() {
return openDirectory(parentDir(_path), _path);
}
@ -234,12 +227,12 @@ class FileDialog : Dialog, CustomGridCellAdapter {
protected ListWidget createRootsList() {
ListWidget res = new ListWidget("ROOTS_LIST");
res.styleId = "EDIT_BOX";
res.styleId = STYLE_EDIT_BOX;
WidgetListAdapter adapter = new WidgetListAdapter();
foreach(ref RootEntry root; _roots) {
ImageTextButton btn = new ImageTextButton(null, root.icon, root.label);
btn.orientation = Orientation.Vertical;
btn.styleId = "TRANSPARENT_BUTTON_BACKGROUND";
btn.styleId = STYLE_TRANSPARENT_BUTTON_BACKGROUND;
btn.focusable = false;
adapter.widgets.add(btn);
}
@ -310,11 +303,11 @@ class FileDialog : Dialog, CustomGridCellAdapter {
layoutWidth(FILL_PARENT);
layoutWidth(FILL_PARENT);
minWidth = 600;
minHeight = 400;
//minHeight = 400;
LinearLayout content = new HorizontalLayout("dlgcontent");
content.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT).minWidth(400).minHeight(300);
content.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); //.minWidth(400).minHeight(300);
leftPanel = new VerticalLayout("places");
leftPanel.addChild(createRootsList());
@ -387,6 +380,28 @@ class FileDialog : Dialog, CustomGridCellAdapter {
}
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) {
super.layout(rc);
_fileList.autoFitColumnWidths();
_fileList.fillColumnWidth(1);
}
///// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
//override void measure(int parentWidth, int parentHeight) {
// super.measure(parentWidth, parentHeight);
// for(int i = 0; i < childCount; i++) {
// Widget w = child(i);
// Log.d("id=", w.id, " measuredHeight=", w.measuredHeight );
// for (int j = 0; j < w.childCount; j++) {
// Widget w2 = w.child(j);
// Log.d(" id=", w2.id, " measuredHeight=", w.measuredHeight );
// }
// }
// Log.d("this id=", id, " measuredHeight=", measuredHeight);
//}
override void onShow() {
_fileList.setFocus();
}
@ -403,17 +418,17 @@ class FilePathPanelItem : HorizontalLayout {
Listener!OnPathSelectionHandler onPathSelectionListener;
this(string path) {
super(null);
styleId = "LIST_ITEM";
styleId = STYLE_LIST_ITEM;
_path = path;
string fname = isRoot(path) ? path : baseName(path);
_text = new TextWidget(null, toUTF32(fname));
_text.styleId = "BUTTON_TRANSPARENT";
_text.styleId = STYLE_BUTTON_TRANSPARENT;
_text.clickable = true;
_text.onClickListener = &onTextClick;
//_text.backgroundColor = 0xC0FFFF;
_text.state = State.Parent;
_button = new ImageButton(null, "scrollbar_btn_right");
_button.styleId = "BUTTON_TRANSPARENT";
_button.styleId = STYLE_BUTTON_TRANSPARENT;
_button.focusable = false;
_button.onClickListener = &onButtonClick;
//_button.backgroundColor = 0xC0FFC0;
@ -458,7 +473,7 @@ class FilePathPanelItem : HorizontalLayout {
}
/// Panel with buttons - path segments - for fast navigation to subdirs.
class FilePathPanelButtons : WidgetGroup {
class FilePathPanelButtons : WidgetGroupDefaultDrawing {
protected string _path;
Listener!OnPathSelectionHandler onPathSelectionListener;
protected bool onPathSelected(string path) {
@ -568,22 +583,6 @@ class FilePathPanelButtons : WidgetGroup {
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
auto saver = ClipRectSaver(buf, rc);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
}
}

View File

@ -0,0 +1,184 @@
// Written in the D programming language.
/**
This module contains declaration of useful color related operations.
In dlangui, colors are represented as 32 bit uint AARRGGBB values.
Synopsis:
----
import dlangui.graphics.colors;
----
Copyright: Vadim Lopatin, 2015
License: Boost License 1.0
Authors: Vadim Lopatin, coolreader.org@gmail.com
*/
module dlangui.graphics.colors;
private import std.string : strip;
/// special color constant to identify value as not a color (to use default/parent value instead)
immutable uint COLOR_UNSPECIFIED = 0xFFDEADFF;
/// transparent color constant
immutable uint COLOR_TRANSPARENT = 0xFFFFFFFF;
immutable uint COLOR_TRANSFORM_OFFSET_NONE = 0x80808080;
immutable uint COLOR_TRANSFORM_MULTIPLY_NONE = 0x40404040;
uint makeRGBA(T)(T r, T g, T b, T a) {
return (cast(uint)a << 24)|(cast(uint)r << 16)|(cast(uint)g << 8)|(cast(uint)b);
}
/// blend two RGB pixels using alpha
uint blendARGB(uint dst, uint src, uint alpha) {
uint dstalpha = dst >> 24;
if (dstalpha > 0x80)
return src;
uint srcr = (src >> 16) & 0xFF;
uint srcg = (src >> 8) & 0xFF;
uint srcb = (src >> 0) & 0xFF;
uint dstr = (dst >> 16) & 0xFF;
uint dstg = (dst >> 8) & 0xFF;
uint dstb = (dst >> 0) & 0xFF;
uint ialpha = 256 - alpha;
uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF;
uint g = ((srcg * ialpha + dstg * alpha) >> 8) & 0xFF;
uint b = ((srcb * ialpha + dstb * alpha) >> 8) & 0xFF;
return (r << 16) | (g << 8) | b;
}
/// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque)
uint blendAlpha(uint a1, uint a2) {
if (!a1)
return a2;
if (!a2)
return a1;
return (((a1 ^ 0xFF) * (a2 ^ 0xFF)) >> 8) ^ 0xFF;
}
/// applies additional alpha to color
uint addAlpha(uint color, uint alpha) {
alpha = blendAlpha(color >> 24, alpha);
return (color & 0xFFFFFF) | (alpha << 24);
}
ubyte rgbToGray(uint color) {
uint srcr = (color >> 16) & 0xFF;
uint srcg = (color >> 8) & 0xFF;
uint srcb = (color >> 0) & 0xFF;
return cast(uint)(((srcr + srcg + srcg + srcb) >> 2) & 0xFF);
}
// todo
struct ColorTransformHandler {
void init(ref ColorTransform transform) {
}
uint transform(uint color) {
return color;
}
}
uint transformComponent(int src, int addBefore, int multiply, int addAfter) {
int add1 = (cast(int)(addBefore << 1)) - 0x100;
int add2 = (cast(int)(addAfter << 1)) - 0x100;
int mul = cast(int)(multiply << 2);
int res = (((src + add1) * mul) >> 8) + add2;
if (res < 0)
res = 0;
else if (res > 255)
res = 255;
return cast(uint)res;
}
uint transformRGBA(uint src, uint addBefore, uint multiply, uint addAfter) {
uint a = transformComponent(src >> 24, addBefore >> 24, multiply >> 24, addAfter >> 24);
uint r = transformComponent((src >> 16) & 0xFF, (addBefore >> 16) & 0xFF, (multiply >> 16) & 0xFF, (addAfter >> 16) & 0xFF);
uint g = transformComponent((src >> 8) & 0xFF, (addBefore >> 8) & 0xFF, (multiply >> 8) & 0xFF, (addAfter >> 8) & 0xFF);
uint b = transformComponent(src & 0xFF, addBefore & 0xFF, multiply & 0xFF, addAfter & 0xFF);
return (a << 24) | (r << 16) | (g << 8) | b;
}
struct ColorTransform {
uint addBefore = COLOR_TRANSFORM_OFFSET_NONE;
uint multiply = COLOR_TRANSFORM_MULTIPLY_NONE;
uint addAfter = COLOR_TRANSFORM_OFFSET_NONE;
@property bool empty() const {
return addBefore == COLOR_TRANSFORM_OFFSET_NONE
&& multiply == COLOR_TRANSFORM_MULTIPLY_NONE
&& addAfter == COLOR_TRANSFORM_OFFSET_NONE;
}
uint transform(uint color) {
return transformRGBA(color, addBefore, multiply, addAfter);
}
}
/// blend two RGB pixels using alpha
ubyte blendGray(ubyte dst, ubyte src, uint alpha) {
uint ialpha = 256 - alpha;
return cast(ubyte)(((src * ialpha + dst * alpha) >> 8) & 0xFF);
}
/// returns true if color is #FFxxxxxx (color alpha is 255)
bool isFullyTransparentColor(uint color) pure nothrow {
return (color >> 24) == 0xFF;
}
/// decodes hex digit (0..9, a..f, A..F), returns uint.max if invalid
uint decodeHexDigit(char ch) {
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
return uint.max;
}
/// decode color string supported formats: #RGB #ARGB #RRGGBB #AARRGGBB
uint decodeHexColor(string s, uint defValue = 0) {
s = strip(s);
switch (s) {
case "@null":
case "transparent":
return COLOR_TRANSPARENT;
case "black":
return 0x000000;
case "white":
return 0xFFFFFF;
case "red":
return 0xFF0000;
case "green":
return 0x00FF00;
case "blue":
return 0x0000FF;
case "gray":
return 0x808080;
case "lightgray":
case "silver":
return 0xC0C0C0;
default:
break;
}
if (s.length != 4 && s.length != 5 && s.length != 7 && s.length != 9)
return defValue;
if (s[0] != '#')
return defValue;
uint value = 0;
for (int i = 1; i < s.length; i++) {
uint digit = decodeHexDigit(s[i]);
if (digit == uint.max)
return defValue;
value = (value << 4) | digit;
if (s.length < 7) // double the same digit for short forms
value = (value << 4) | digit;
}
return value;
}

View File

@ -19,109 +19,8 @@ module dlangui.graphics.drawbuf;
public import dlangui.core.types;
import dlangui.core.logger;
import dlangui.graphics.colors;
immutable uint COLOR_TRANSFORM_OFFSET_NONE = 0x80808080;
immutable uint COLOR_TRANSFORM_MULTIPLY_NONE = 0x40404040;
uint makeRGBA(T)(T r, T g, T b, T a) {
return (cast(uint)a << 24)|(cast(uint)r << 16)|(cast(uint)g << 8)|(cast(uint)b);
}
/// blend two RGB pixels using alpha
uint blendARGB(uint dst, uint src, uint alpha) {
uint dstalpha = dst >> 24;
if (dstalpha > 0x80)
return src;
uint srcr = (src >> 16) & 0xFF;
uint srcg = (src >> 8) & 0xFF;
uint srcb = (src >> 0) & 0xFF;
uint dstr = (dst >> 16) & 0xFF;
uint dstg = (dst >> 8) & 0xFF;
uint dstb = (dst >> 0) & 0xFF;
uint ialpha = 256 - alpha;
uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF;
uint g = ((srcg * ialpha + dstg * alpha) >> 8) & 0xFF;
uint b = ((srcb * ialpha + dstb * alpha) >> 8) & 0xFF;
return (r << 16) | (g << 8) | b;
}
/// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque)
uint blendAlpha(uint a1, uint a2) {
if (!a1)
return a2;
if (!a2)
return a1;
return (((a1 ^ 0xFF) * (a2 ^ 0xFF)) >> 8) ^ 0xFF;
}
/// applies additional alpha to color
uint addAlpha(uint color, uint alpha) {
alpha = blendAlpha(color >> 24, alpha);
return (color & 0xFFFFFF) | (alpha << 24);
}
ubyte rgbToGray(uint color) {
uint srcr = (color >> 16) & 0xFF;
uint srcg = (color >> 8) & 0xFF;
uint srcb = (color >> 0) & 0xFF;
return cast(uint)(((srcr + srcg + srcg + srcb) >> 2) & 0xFF);
}
// todo
struct ColorTransformHandler {
void init(ref ColorTransform transform) {
}
uint transform(uint color) {
return color;
}
}
uint transformComponent(int src, int addBefore, int multiply, int addAfter) {
int add1 = (cast(int)(addBefore << 1)) - 0x100;
int add2 = (cast(int)(addAfter << 1)) - 0x100;
int mul = cast(int)(multiply << 2);
int res = (((src + add1) * mul) >> 8) + add2;
if (res < 0)
res = 0;
else if (res > 255)
res = 255;
return cast(uint)res;
}
uint transformRGBA(uint src, uint addBefore, uint multiply, uint addAfter) {
uint a = transformComponent(src >> 24, addBefore >> 24, multiply >> 24, addAfter >> 24);
uint r = transformComponent((src >> 16) & 0xFF, (addBefore >> 16) & 0xFF, (multiply >> 16) & 0xFF, (addAfter >> 16) & 0xFF);
uint g = transformComponent((src >> 8) & 0xFF, (addBefore >> 8) & 0xFF, (multiply >> 8) & 0xFF, (addAfter >> 8) & 0xFF);
uint b = transformComponent(src & 0xFF, addBefore & 0xFF, multiply & 0xFF, addAfter & 0xFF);
return (a << 24) | (r << 16) | (g << 8) | b;
}
struct ColorTransform {
uint addBefore = COLOR_TRANSFORM_OFFSET_NONE;
uint multiply = COLOR_TRANSFORM_MULTIPLY_NONE;
uint addAfter = COLOR_TRANSFORM_OFFSET_NONE;
@property bool empty() const {
return addBefore == COLOR_TRANSFORM_OFFSET_NONE
&& multiply == COLOR_TRANSFORM_MULTIPLY_NONE
&& addAfter == COLOR_TRANSFORM_OFFSET_NONE;
}
uint transform(uint color) {
return transformRGBA(color, addBefore, multiply, addAfter);
}
}
/// blend two RGB pixels using alpha
ubyte blendGray(ubyte dst, ubyte src, uint alpha) {
uint ialpha = 256 - alpha;
return cast(ubyte)(((src * ialpha + dst * alpha) >> 8) & 0xFF);
}
/// returns true if color is #FFxxxxxx (color alpha is 255)
bool isFullyTransparentColor(uint color) pure nothrow {
return (color >> 24) == 0xFF;
}
/**
* 9-patch image scaling information (see Android documentation).
@ -411,6 +310,32 @@ class DrawBuf : RefCountedObject {
}
}
/// draw focus rectangle; vertical gradient supported - colors[0] is top color, colors[1] is bottom color
void drawFocusRect(Rect rc, const uint[] colors) {
// override for faster performance when using OpenGL
if (colors.length < 1)
return;
uint color1 = colors[0];
uint color2 = colors.length > 1 ? colors[1] : color1;
if (isFullyTransparentColor(color1) && isFullyTransparentColor(color2))
return;
// draw horizontal lines
for (int x = rc.left; x < rc.right; x++) {
if ((x ^ rc.top) & 1)
fillRect(Rect(x, rc.top, x + 1, rc.top + 1), color1);
if ((x ^ (rc.bottom - 1)) & 1)
fillRect(Rect(x, rc.bottom - 1, x + 1, rc.bottom), color2);
}
// draw vertical lines
for (int y = rc.top + 1; y < rc.bottom - 1; y++) {
uint color = color1 == color2 ? color1 : blendARGB(color2, color1, 255 / (rc.bottom - rc.top));
if ((y ^ rc.left) & 1)
fillRect(Rect(rc.left, y, rc.left + 1, y + 1), color);
if ((y ^ (rc.right - 1)) & 1)
fillRect(Rect(rc.right - 1, y, rc.right, y + 1), color);
}
}
/// create drawbuf with copy of current buffer with changed colors (returns this if not supported)
DrawBuf transformColors(ref ColorTransform transform) {
return this;

View File

@ -62,9 +62,9 @@ class GLDrawBuf : DrawBuf {
/// reserved for hardware-accelerated drawing - ends drawing batch
override void afterDrawing() {
setOrthoProjection(_dx, _dy);
glSupport.setOrthoProjection(_dx, _dy);
_scene.draw();
flushGL();
glSupport.flushGL();
destroy(_scene);
_scene = null;
}
@ -244,7 +244,7 @@ private class GLImageCache {
_drawbuf = null;
}
if (_textureId != 0) {
deleteTexture(_textureId);
glSupport.deleteTexture(_textureId);
_textureId = 0;
}
}
@ -254,14 +254,14 @@ private class GLImageCache {
return; // no draw buffer!!!
if (_textureId == 0) {
//CRLog::debug("updateTexture - new texture");
_textureId = genTexture();
_textureId = glSupport.genTexture();
if (!_textureId)
return;
}
//CRLog::debug("updateTexture - setting image %dx%d", _drawbuf.width, _drawbuf.height);
uint * pixels = _drawbuf.scanLine(0);
if (!setTextureImage(_textureId, _drawbuf.width, _drawbuf.height, cast(ubyte*)pixels)) {
deleteTexture(_textureId);
if (!glSupport.setTextureImage(_textureId, _drawbuf.width, _drawbuf.height, cast(ubyte*)pixels)) {
glSupport.deleteTexture(_textureId);
_textureId = 0;
return;
}
@ -339,7 +339,7 @@ private class GLImageCache {
if (_needUpdateTexture)
updateTexture();
if (_textureId != 0) {
if (!isTexture(_textureId)) {
if (!glSupport.isTexture(_textureId)) {
Log.e("Invalid texture ", _textureId);
return;
}
@ -371,12 +371,12 @@ private class GLImageCache {
dstrc.bottom -= clip.bottom;
}
if (!dstrc.empty)
drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height());
glSupport.drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height());
//drawColorAndTextureRect(vertices, texcoords, color, _textureId);
if (rotationAngle) {
// unset rotation
setRotation(rx, ry, 0);
glSupport.setRotation(rx, ry, 0);
// glMatrixMode(GL_PROJECTION);
// glPopMatrix();
// checkError("pop matrix");
@ -576,7 +576,7 @@ private class GLGlyphCache {
_drawbuf = null;
}
if (_textureId != 0) {
deleteTexture(_textureId);
glSupport.deleteTexture(_textureId);
_textureId = 0;
}
}
@ -589,7 +589,7 @@ private class GLGlyphCache {
return; // no draw buffer!!!
if (_textureId == 0) {
//CRLog::debug("updateTexture - new texture");
_textureId = genTexture();
_textureId = glSupport.genTexture();
if (!_textureId)
return;
}
@ -600,8 +600,8 @@ private class GLGlyphCache {
_rgbaBuffer.length = len;
for (int i = 0; i < len; i++)
_rgbaBuffer[i] = ((cast(uint)pixels[i]) << 24) | 0x00FFFFFF;
if (!setTextureImage(_textureId, _drawbuf.width, _drawbuf.height, cast(ubyte*)_rgbaBuffer.ptr)) {
deleteTexture(_textureId);
if (!glSupport.setTextureImage(_textureId, _drawbuf.width, _drawbuf.height, cast(ubyte*)_rgbaBuffer.ptr)) {
glSupport.deleteTexture(_textureId);
_textureId = 0;
return;
}
@ -667,7 +667,7 @@ private class GLGlyphCache {
if (_needUpdateTexture)
updateTexture();
if (_textureId != 0) {
if (!isTexture(_textureId)) {
if (!glSupport.isTexture(_textureId)) {
Log.e("Invalid texture ", _textureId);
return;
}
@ -693,7 +693,7 @@ private class GLGlyphCache {
}
if (!dstrc.empty) {
//Log.d("drawing glyph with color ", color);
drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, false);
glSupport.drawColorAndTextureRect(_textureId, _tdx, _tdy, srcrc, dstrc, color, false);
}
}
@ -818,7 +818,7 @@ class SolidRectSceneItem : SceneItem {
_color = color;
}
override void draw() {
drawSolidFillRect(_rc, _color, _color, _color, _color);
glSupport.drawSolidFillRect(_rc, _color, _color, _color, _color);
}
}

View File

@ -53,7 +53,8 @@ static this() {
0x0505: "GL_OUT_OF_MEMORY"
];
}
/* Convenient wrapper around glGetError()
/**
* Convenient wrapper around glGetError()
* TODO use one of the DEBUG extensions instead
*/
bool checkError(string context="", string file=__FILE__, int line=__LINE__)
@ -67,295 +68,6 @@ bool checkError(string context="", string file=__FILE__, int line=__LINE__)
return false;
}
immutable float Z_2D = -2.0f;
void drawSolidFillRect(Rect rc, uint color1, uint color2, uint color3, uint color4) {
float[6 * 4] colors;
LVGLFillColor(color1, colors.ptr + 4*0, 1);
LVGLFillColor(color4, colors.ptr + 4*1, 1);
LVGLFillColor(color3, colors.ptr + 4*2, 1);
LVGLFillColor(color1, colors.ptr + 4*3, 1);
LVGLFillColor(color3, colors.ptr + 4*4, 1);
LVGLFillColor(color2, colors.ptr + 4*5, 1);
float x0 = cast(float)(rc.left);
float y0 = cast(float)(bufferDy-rc.top);
float x1 = cast(float)(rc.right);
float y1 = cast(float)(bufferDy-rc.bottom);
// don't flip for framebuffer
if (currentFramebufferId) {
y0 = cast(float)(rc.top);
y1 = cast(float)(rc.bottom);
}
float[3 * 6] vertices = [
x0,y0,Z_2D,
x0,y1,Z_2D,
x1,y1,Z_2D,
x0,y0,Z_2D,
x1,y1,Z_2D,
x1,y0,Z_2D];
if (_solidFillProgram !is null) {
//Log.d("solid fill: vertices ", vertices, " colors ", colors);
_solidFillProgram.execute(vertices, colors);
} else
Log.e("No program");
}
void drawColorAndTextureRect(uint textureId, int tdx, int tdy, Rect srcrc, Rect dstrc, uint color, bool linear) {
//Log.v("drawColorAndTextureRect tx=", textureId, " src=", srcrc, " dst=", dstrc);
drawColorAndTextureRect(textureId, tdx, tdy, srcrc.left, srcrc.top, srcrc.width(), srcrc.height(), dstrc.left, dstrc.top, dstrc.width(), dstrc.height(), color, linear);
}
void drawColorAndTextureRect(uint textureId, int tdx, int tdy, int srcx, int srcy, int srcdx, int srcdy, int xx, int yy, int dx, int dy, uint color, bool linear) {
float[6*4] colors;
LVGLFillColor(color, colors.ptr, 6);
float dstx0 = cast(float)xx;
float dsty0 = cast(float)(bufferDy - (yy));
float dstx1 = cast(float)(xx + dx);
float dsty1 = cast(float)(bufferDy - (yy + dy));
// don't flip for framebuffer
if (currentFramebufferId) {
dsty0 = cast(float)((yy));
dsty1 = cast(float)((yy + dy));
}
float srcx0 = srcx / cast(float)tdx;
float srcy0 = srcy / cast(float)tdy;
float srcx1 = (srcx + srcdx) / cast(float)tdx;
float srcy1 = (srcy + srcdy) / cast(float)tdy;
float[3 * 6] vertices = [dstx0,dsty0,Z_2D,
dstx0,dsty1,Z_2D,
dstx1,dsty1,Z_2D,
dstx0,dsty0,Z_2D,
dstx1,dsty1,Z_2D,
dstx1,dsty0,Z_2D];
float[2 * 6] texcoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy1, srcx0,srcy0, srcx1,srcy1, srcx1,srcy0];
_textureProgram.execute(vertices, texcoords, colors, textureId, linear);
//drawColorAndTextureRect(vertices, texcoords, colors, textureId, linear);
}
/// generate new texture ID
uint genTexture() {
GLuint textureId = 0;
glGenTextures(1, &textureId);
return textureId;
}
/// delete OpenGL texture
void deleteTexture(ref uint textureId) {
if (!textureId)
return;
if (glIsTexture(textureId) != GL_TRUE) {
Log.e("Invalid texture ", textureId);
return;
}
GLuint id = textureId;
glDeleteTextures(1, &id);
checkError("glDeleteTextures");
textureId = 0;
}
/// call glFlush
void flushGL() {
glFlush();
checkError("glFlush");
}
bool setTextureImage(uint textureId, int dx, int dy, ubyte * pixels) {
//checkError("before setTextureImage");
glActiveTexture(GL_TEXTURE0);
checkError("updateTexture - glActiveTexture");
glBindTexture(GL_TEXTURE_2D, 0);
checkError("updateTexture - glBindTexture(0)");
glBindTexture(GL_TEXTURE_2D, textureId);
checkError("updateTexture - glBindTexture");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
checkError("updateTexture - glPixelStorei");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkError("updateTexture - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkError("updateTexture - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError("updateTexture - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
checkError("updateTexture - glTexParameteri");
if (!glIsTexture(textureId))
Log.e("second test - invalid texture passed to CRGLSupportImpl::setTextureImage");
// ORIGINAL: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
checkError("updateTexture - glTexImage2D");
if (glGetError() != GL_NO_ERROR) {
Log.e("Cannot set image for texture");
return false;
}
checkError("after setTextureImage");
return true;
}
bool setTextureImageAlpha(uint textureId, int dx, int dy, ubyte * pixels) {
checkError("before setTextureImageAlpha");
glActiveTexture(GL_TEXTURE0);
checkError("updateTexture - glActiveTexture");
glBindTexture(GL_TEXTURE_2D, 0);
checkError("updateTexture - glBindTexture(0)");
glBindTexture(GL_TEXTURE_2D, textureId);
checkError("setTextureImageAlpha - glBindTexture");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
checkError("setTextureImageAlpha - glPixelStorei");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkError("setTextureImageAlpha - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkError("setTextureImageAlpha - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError("setTextureImageAlpha - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
checkError("setTextureImageAlpha - glTexParameteri");
if (!glIsTexture(textureId))
Log.e("second test: invalid texture passed to CRGLSupportImpl::setTextureImageAlpha");
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, dx, dy, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
checkError("setTextureImageAlpha - glTexImage2D");
if (glGetError() != GL_NO_ERROR) {
Log.e("Cannot set image for texture");
return false;
}
glBindTexture(GL_TEXTURE_2D, 0);
checkError("updateTexture - glBindTexture(0)");
checkError("after setTextureImageAlpha");
return true;
}
private uint currentFramebufferId;
/// returns texture ID for buffer, 0 if failed
bool createFramebuffer(ref uint textureId, ref uint framebufferId, int dx, int dy) {
checkError("before createFramebuffer");
bool res = true;
textureId = framebufferId = 0;
textureId = genTexture();
if (!textureId)
return false;
GLuint fid = 0;
glGenFramebuffers(1, &fid);
if (checkError("createFramebuffer glGenFramebuffersOES")) return false;
framebufferId = fid;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferId);
if (checkError("createFramebuffer glBindFramebuffer")) return false;
glBindTexture(GL_TEXTURE_2D, textureId);
checkError("glBindTexture(GL_TEXTURE_2D, _textureId)");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dx, dy, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, null);
checkError("glTexImage2D");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkError("texParameter");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkError("texParameter");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError("texParameter");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
checkError("texParameter");
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
checkError("glFramebufferTexture2D");
// Always check that our framebuffer is ok
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
Log.e("glFramebufferTexture2D failed");
res = false;
}
checkError("glCheckFramebufferStatus");
//glClearColor(0.5f, 0, 0, 1);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
checkError("glClearColor");
glClear(GL_COLOR_BUFFER_BIT);
checkError("glClear");
checkError("after createFramebuffer");
//CRLog::trace("CRGLSupportImpl::createFramebuffer %d,%d texture=%d, buffer=%d", dx, dy, textureId, framebufferId);
currentFramebufferId = framebufferId;
glBindTexture(GL_TEXTURE_2D, 0);
checkError("createFramebuffer - glBindTexture(0)");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkError("createFramebuffer - glBindFramebuffer(0)");
return res;
}
void deleteFramebuffer(ref uint framebufferId) {
//CRLog::debug("GLDrawBuf::deleteFramebuffer");
if (framebufferId != 0) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkError("deleteFramebuffer - glBindFramebuffer");
GLuint fid = framebufferId;
glDeleteFramebuffers(1, &fid);
checkError("deleteFramebuffer - glDeleteFramebuffer");
}
//CRLog::trace("CRGLSupportImpl::deleteFramebuffer(%d)", framebufferId);
framebufferId = 0;
checkError("after deleteFramebuffer");
currentFramebufferId = 0;
}
bool bindFramebuffer(uint framebufferId) {
//CRLog::trace("CRGLSupportImpl::bindFramebuffer(%d)", framebufferId);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferId);
currentFramebufferId = framebufferId;
return !checkError("glBindFramebuffer");
}
/// projection matrix
//private mat4 m;
/// current gl buffer width
private int bufferDx;
/// current gl buffer height
private int bufferDy;
//private float[16] matrix;
private float[16] qtmatrix;
void QMatrix4x4_ortho(float left, float right, float bottom, float top, float nearPlane, float farPlane)
{
// Bail out if the projection volume is zero-sized.
if (left == right || bottom == top || nearPlane == farPlane)
return;
// Construct the projection.
float width = right - left;
float invheight = top - bottom;
float clip = farPlane - nearPlane;
float[4][4] m;
m[0][0] = 2.0f / width;
m[1][0] = 0.0f;
m[2][0] = 0.0f;
m[3][0] = -(left + right) / width;
m[0][1] = 0.0f;
m[1][1] = 2.0f / invheight;
m[2][1] = 0.0f;
m[3][1] = -(top + bottom) / invheight;
m[0][2] = 0.0f;
m[1][2] = 0.0f;
m[2][2] = -2.0f / clip;
m[3][2] = -(nearPlane + farPlane) / clip;
m[0][3] = 0.0f;
m[1][3] = 0.0f;
m[2][3] = 0.0f;
m[3][3] = 1.0f;
for (int y = 0; y < 4; y++)
for (int x = 0; x < 4; x++)
qtmatrix[y * 4 + x] = m[y][x];
}
void setOrthoProjection(int dx, int dy) {
bufferDx = dx;
bufferDy = dy;
QMatrix4x4_ortho(0, dx, 0, dy, 0.5f, 50.0f);
glViewport(0, 0, dx, dy);
checkError("glViewport");
}
class GLProgram {
@property abstract string vertexSource();
@ -503,7 +215,7 @@ class SolidFillProgram : GLProgram {
bind();
//glUniformMatrix4fv(matrixLocation, 1, false, m.value_ptr);
//glUniformMatrix4fv(matrixLocation, 1, false, matrix.ptr);
glUniformMatrix4fv(matrixLocation, 1, false, qtmatrix.ptr);
glUniformMatrix4fv(matrixLocation, 1, false, glSupport.qtmatrix.ptr);
checkError("glUniformMatrix4fv");
}
@ -704,57 +416,366 @@ class TextureProgram : SolidFillProgram {
}
}
__gshared TextureProgram _textureProgram;
__gshared SolidFillProgram _solidFillProgram;
__gshared GLSupport _glSupport;
@property GLSupport glSupport() {
if (!_glSupport) {
Log.f("GLSupport is not initialized");
assert(false, "GLSupport is not initialized");
}
if (!_glSupport.valid) {
Log.e("GLSupport programs are not initialized");
}
return _glSupport;
}
bool initShaders() {
if (_textureProgram is null) {
_textureProgram = new TextureProgram();
if (!_textureProgram.compile())
class GLSupport {
TextureProgram _textureProgram;
SolidFillProgram _solidFillProgram;
@property bool valid() {
return _textureProgram && _solidFillProgram;
}
bool initShaders() {
if (_textureProgram is null) {
_textureProgram = new TextureProgram();
if (!_textureProgram.compile())
return false;
}
if (_solidFillProgram is null) {
_solidFillProgram = new SolidFillProgram();
if (!_solidFillProgram.compile())
return false;
}
Log.d("Shaders compiled successfully");
return true;
}
bool uninitShaders() {
Log.d("Uniniting shaders");
if (_textureProgram !is null) {
destroy(_textureProgram);
_textureProgram = null;
}
if (_solidFillProgram !is null) {
destroy(_solidFillProgram);
_solidFillProgram = null;
}
return true;
}
bool isTexture(uint textureId) {
return glIsTexture(textureId) == GL_TRUE;
}
void setRotation(int x, int y, int rotationAngle) {
/*
this->rotationAngle = rotationAngle;
rotationX = x;
rotationY = y;
if (!currentFramebufferId) {
rotationY = bufferDy - rotationY;
}
QMatrix4x4 matrix2;
matrix2.ortho(0, bufferDx, 0, bufferDy, 0.5f, 5.0f);
if (rotationAngle) {
matrix2.translate(rotationX, rotationY, 0);
matrix2.rotate(rotationAngle, 0, 0, 1);
matrix2.translate(-rotationX, -rotationY, 0);
}
matrix2.copyDataTo(m);
*/
}
static immutable float Z_2D = -2.0f;
void drawSolidFillRect(Rect rc, uint color1, uint color2, uint color3, uint color4) {
float[6 * 4] colors;
LVGLFillColor(color1, colors.ptr + 4*0, 1);
LVGLFillColor(color4, colors.ptr + 4*1, 1);
LVGLFillColor(color3, colors.ptr + 4*2, 1);
LVGLFillColor(color1, colors.ptr + 4*3, 1);
LVGLFillColor(color3, colors.ptr + 4*4, 1);
LVGLFillColor(color2, colors.ptr + 4*5, 1);
float x0 = cast(float)(rc.left);
float y0 = cast(float)(bufferDy-rc.top);
float x1 = cast(float)(rc.right);
float y1 = cast(float)(bufferDy-rc.bottom);
// don't flip for framebuffer
if (currentFramebufferId) {
y0 = cast(float)(rc.top);
y1 = cast(float)(rc.bottom);
}
float[3 * 6] vertices = [
x0,y0,Z_2D,
x0,y1,Z_2D,
x1,y1,Z_2D,
x0,y0,Z_2D,
x1,y1,Z_2D,
x1,y0,Z_2D];
if (_solidFillProgram !is null) {
//Log.d("solid fill: vertices ", vertices, " colors ", colors);
_solidFillProgram.execute(vertices, colors);
} else
Log.e("No program");
}
void drawColorAndTextureRect(uint textureId, int tdx, int tdy, Rect srcrc, Rect dstrc, uint color, bool linear) {
//Log.v("drawColorAndTextureRect tx=", textureId, " src=", srcrc, " dst=", dstrc);
drawColorAndTextureRect(textureId, tdx, tdy, srcrc.left, srcrc.top, srcrc.width(), srcrc.height(), dstrc.left, dstrc.top, dstrc.width(), dstrc.height(), color, linear);
}
void drawColorAndTextureRect(uint textureId, int tdx, int tdy, int srcx, int srcy, int srcdx, int srcdy, int xx, int yy, int dx, int dy, uint color, bool linear) {
float[6*4] colors;
LVGLFillColor(color, colors.ptr, 6);
float dstx0 = cast(float)xx;
float dsty0 = cast(float)(bufferDy - (yy));
float dstx1 = cast(float)(xx + dx);
float dsty1 = cast(float)(bufferDy - (yy + dy));
// don't flip for framebuffer
if (currentFramebufferId) {
dsty0 = cast(float)((yy));
dsty1 = cast(float)((yy + dy));
}
float srcx0 = srcx / cast(float)tdx;
float srcy0 = srcy / cast(float)tdy;
float srcx1 = (srcx + srcdx) / cast(float)tdx;
float srcy1 = (srcy + srcdy) / cast(float)tdy;
float[3 * 6] vertices = [dstx0,dsty0,Z_2D,
dstx0,dsty1,Z_2D,
dstx1,dsty1,Z_2D,
dstx0,dsty0,Z_2D,
dstx1,dsty1,Z_2D,
dstx1,dsty0,Z_2D];
float[2 * 6] texcoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy1, srcx0,srcy0, srcx1,srcy1, srcx1,srcy0];
_textureProgram.execute(vertices, texcoords, colors, textureId, linear);
//drawColorAndTextureRect(vertices, texcoords, colors, textureId, linear);
}
/// generate new texture ID
uint genTexture() {
GLuint textureId = 0;
glGenTextures(1, &textureId);
return textureId;
}
/// delete OpenGL texture
void deleteTexture(ref uint textureId) {
if (!textureId)
return;
if (glIsTexture(textureId) != GL_TRUE) {
Log.e("Invalid texture ", textureId);
return;
}
GLuint id = textureId;
glDeleteTextures(1, &id);
checkError("glDeleteTextures");
textureId = 0;
}
/// call glFlush
void flushGL() {
glFlush();
checkError("glFlush");
}
bool setTextureImage(uint textureId, int dx, int dy, ubyte * pixels) {
//checkError("before setTextureImage");
glActiveTexture(GL_TEXTURE0);
checkError("updateTexture - glActiveTexture");
glBindTexture(GL_TEXTURE_2D, 0);
checkError("updateTexture - glBindTexture(0)");
glBindTexture(GL_TEXTURE_2D, textureId);
checkError("updateTexture - glBindTexture");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
checkError("updateTexture - glPixelStorei");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkError("updateTexture - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkError("updateTexture - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError("updateTexture - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
checkError("updateTexture - glTexParameteri");
if (!glIsTexture(textureId))
Log.e("second test - invalid texture passed to CRGLSupportImpl::setTextureImage");
// ORIGINAL: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
checkError("updateTexture - glTexImage2D");
if (glGetError() != GL_NO_ERROR) {
Log.e("Cannot set image for texture");
return false;
}
checkError("after setTextureImage");
return true;
}
if (_solidFillProgram is null) {
_solidFillProgram = new SolidFillProgram();
if (!_solidFillProgram.compile())
bool setTextureImageAlpha(uint textureId, int dx, int dy, ubyte * pixels) {
checkError("before setTextureImageAlpha");
glActiveTexture(GL_TEXTURE0);
checkError("updateTexture - glActiveTexture");
glBindTexture(GL_TEXTURE_2D, 0);
checkError("updateTexture - glBindTexture(0)");
glBindTexture(GL_TEXTURE_2D, textureId);
checkError("setTextureImageAlpha - glBindTexture");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
checkError("setTextureImageAlpha - glPixelStorei");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkError("setTextureImageAlpha - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkError("setTextureImageAlpha - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError("setTextureImageAlpha - glTexParameteri");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
checkError("setTextureImageAlpha - glTexParameteri");
if (!glIsTexture(textureId))
Log.e("second test: invalid texture passed to CRGLSupportImpl::setTextureImageAlpha");
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, dx, dy, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
checkError("setTextureImageAlpha - glTexImage2D");
if (glGetError() != GL_NO_ERROR) {
Log.e("Cannot set image for texture");
return false;
}
Log.d("Shaders compiled successfully");
return true;
}
bool uninitShaders() {
Log.d("Uniniting shaders");
if (_textureProgram !is null) {
destroy(_textureProgram);
_textureProgram = null;
}
if (_solidFillProgram !is null) {
destroy(_solidFillProgram);
_solidFillProgram = null;
}
return true;
}
bool isTexture(uint textureId) {
return glIsTexture(textureId) == GL_TRUE;
}
void setRotation(int x, int y, int rotationAngle) {
/*
this->rotationAngle = rotationAngle;
rotationX = x;
rotationY = y;
if (!currentFramebufferId) {
rotationY = bufferDy - rotationY;
}
glBindTexture(GL_TEXTURE_2D, 0);
checkError("updateTexture - glBindTexture(0)");
checkError("after setTextureImageAlpha");
return true;
}
QMatrix4x4 matrix2;
matrix2.ortho(0, bufferDx, 0, bufferDy, 0.5f, 5.0f);
if (rotationAngle) {
matrix2.translate(rotationX, rotationY, 0);
matrix2.rotate(rotationAngle, 0, 0, 1);
matrix2.translate(-rotationX, -rotationY, 0);
private uint currentFramebufferId;
/// returns texture ID for buffer, 0 if failed
bool createFramebuffer(ref uint textureId, ref uint framebufferId, int dx, int dy) {
checkError("before createFramebuffer");
bool res = true;
textureId = framebufferId = 0;
textureId = genTexture();
if (!textureId)
return false;
GLuint fid = 0;
glGenFramebuffers(1, &fid);
if (checkError("createFramebuffer glGenFramebuffersOES")) return false;
framebufferId = fid;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferId);
if (checkError("createFramebuffer glBindFramebuffer")) return false;
glBindTexture(GL_TEXTURE_2D, textureId);
checkError("glBindTexture(GL_TEXTURE_2D, _textureId)");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dx, dy, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, null);
checkError("glTexImage2D");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkError("texParameter");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkError("texParameter");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
checkError("texParameter");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
checkError("texParameter");
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
checkError("glFramebufferTexture2D");
// Always check that our framebuffer is ok
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
Log.e("glFramebufferTexture2D failed");
res = false;
}
checkError("glCheckFramebufferStatus");
//glClearColor(0.5f, 0, 0, 1);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
checkError("glClearColor");
glClear(GL_COLOR_BUFFER_BIT);
checkError("glClear");
checkError("after createFramebuffer");
//CRLog::trace("CRGLSupportImpl::createFramebuffer %d,%d texture=%d, buffer=%d", dx, dy, textureId, framebufferId);
currentFramebufferId = framebufferId;
glBindTexture(GL_TEXTURE_2D, 0);
checkError("createFramebuffer - glBindTexture(0)");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkError("createFramebuffer - glBindFramebuffer(0)");
return res;
}
matrix2.copyDataTo(m);
*/
void deleteFramebuffer(ref uint framebufferId) {
//CRLog::debug("GLDrawBuf::deleteFramebuffer");
if (framebufferId != 0) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkError("deleteFramebuffer - glBindFramebuffer");
GLuint fid = framebufferId;
glDeleteFramebuffers(1, &fid);
checkError("deleteFramebuffer - glDeleteFramebuffer");
}
//CRLog::trace("CRGLSupportImpl::deleteFramebuffer(%d)", framebufferId);
framebufferId = 0;
checkError("after deleteFramebuffer");
currentFramebufferId = 0;
}
bool bindFramebuffer(uint framebufferId) {
//CRLog::trace("CRGLSupportImpl::bindFramebuffer(%d)", framebufferId);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferId);
currentFramebufferId = framebufferId;
return !checkError("glBindFramebuffer");
}
/// projection matrix
//private mat4 m;
/// current gl buffer width
private int bufferDx;
/// current gl buffer height
private int bufferDy;
//private float[16] matrix;
private float[16] qtmatrix;
void QMatrix4x4_ortho(float left, float right, float bottom, float top, float nearPlane, float farPlane)
{
// Bail out if the projection volume is zero-sized.
if (left == right || bottom == top || nearPlane == farPlane)
return;
// Construct the projection.
float width = right - left;
float invheight = top - bottom;
float clip = farPlane - nearPlane;
float[4][4] m;
m[0][0] = 2.0f / width;
m[1][0] = 0.0f;
m[2][0] = 0.0f;
m[3][0] = -(left + right) / width;
m[0][1] = 0.0f;
m[1][1] = 2.0f / invheight;
m[2][1] = 0.0f;
m[3][1] = -(top + bottom) / invheight;
m[0][2] = 0.0f;
m[1][2] = 0.0f;
m[2][2] = -2.0f / clip;
m[3][2] = -(nearPlane + farPlane) / clip;
m[0][3] = 0.0f;
m[1][3] = 0.0f;
m[2][3] = 0.0f;
m[3][3] = 1.0f;
for (int y = 0; y < 4; y++)
for (int x = 0; x < 4; x++)
qtmatrix[y * 4 + x] = m[y][x];
}
void setOrthoProjection(int dx, int dy) {
bufferDx = dx;
bufferDy = dy;
QMatrix4x4_ortho(0, dx, 0, dy, 0.5f, 50.0f);
glViewport(0, 0, dx, dy);
checkError("glViewport");
}
}

View File

@ -39,6 +39,7 @@ version (USE_DLIBIMAGE) {
import dlangui.core.logger;
import dlangui.core.types;
import dlangui.graphics.colors;
import dlangui.graphics.drawbuf;
import std.stream;
import std.conv : to;

View File

@ -28,6 +28,7 @@ module dlangui.graphics.resources;
import dlangui.graphics.images;
import dlangui.graphics.drawbuf;
import dlangui.graphics.colors;
import dlangui.core.logger;
import std.file;
import std.algorithm;
@ -93,22 +94,7 @@ class FrameDrawable : Drawable {
@property override Rect padding() { return _frameWidths; }
}
/// decode color string #AARRGGBB, e.g. #5599AA
static uint decodeHexColor(string s) {
uint value = 0;
foreach(c; s) {
int digit = -1;
if (c >='0' && c <= '9')
digit = c - '0';
else if (c >='a' && c <= 'f')
digit = c - 'a' + 10;
else if (c >='A' && c <= 'F')
digit = c - 'A' + 10;
if (digit >= 0)
value = (value << 4) | digit;
}
return value;
}
/// decode size string, e.g. 1px or 2 or 3pt
static uint decodeDimension(string s) {
uint value = 0;
@ -348,6 +334,7 @@ void extractStateFlag(ref string[string] attr, string attrName, string attrName2
void extractStateFlags(ref string[string] attr, ref uint stateMask, ref uint stateValue) {
extractStateFlag(attr, "state_pressed", "android:state_pressed", State.Pressed, stateMask, stateValue);
extractStateFlag(attr, "state_focused", "android:state_focused", State.Focused, stateMask, stateValue);
extractStateFlag(attr, "state_default", "android:state_default", State.Default, stateMask, stateValue);
extractStateFlag(attr, "state_hovered", "android:state_hovered", State.Hovered, stateMask, stateValue);
extractStateFlag(attr, "state_selected", "android:state_selected", State.Selected, stateMask, stateValue);
extractStateFlag(attr, "state_checkable", "android:state_checkable", State.Checkable, stateMask, stateValue);

View File

@ -107,11 +107,13 @@ class Window {
Log.d("onResize ", _dx, "x", _dy);
long measureStart = currentTimeMillis;
measure();
//Log.d("measured size: ", _mainWidget.measuredWidth, "x", _mainWidget.measuredHeight);
long measureEnd = currentTimeMillis;
Log.d("measure took ", measureEnd - measureStart, " ms");
layout();
long layoutEnd = currentTimeMillis;
Log.d("layout took ", layoutEnd - measureEnd, " ms");
//Log.d("layout position: ", _mainWidget.pos);
}
}

View File

@ -97,6 +97,8 @@ class SDLWindow : Window {
version(USE_OPENGL) {
if (_enableOpengl)
windowFlags |= SDL_WINDOW_OPENGL;
if (!_glSupport)
_glSupport = new GLSupport();
}
_win = SDL_CreateWindow(toUTF8(_caption).toStringz, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
700, 500,
@ -127,7 +129,7 @@ class SDLWindow : Window {
} else if (!_gl3Reloaded) {
DerelictGL3.reload();
_gl3Reloaded = true;
if (!initShaders())
if (!glSupport.valid && !glSupport.initShaders())
_enableOpengl = false;
}
}

View File

@ -42,6 +42,7 @@ class Win32ColorDrawBuf : ColorDrawBufBase {
/// create resized copy of ColorDrawBuf
this(ColorDrawBuf v, int dx, int dy) {
this(dx, dy);
resetClipping();
fill(0xFFFFFFFF);
if (_dx == dx && _dy == dy)
drawImage(0, 0, v);
@ -87,7 +88,7 @@ class Win32ColorDrawBuf : ColorDrawBufBase {
//return CreateBitmap(_dx, _dy, 1, 1, buf.ptr);
}
/// destroy object, but leave bitmap as is
HBITMAP destoryLeavingBitmap() {
HBITMAP destroyLeavingBitmap() {
HBITMAP res = _drawbmp;
_drawbmp = null;
destroy(this);

View File

@ -154,10 +154,12 @@ version (USE_OPENGL) {
class Win32Window : Window {
Win32Platform _platform;
HWND _hwnd;
version (USE_OPENGL) {
HGLRC _hGLRC; // opengl context
HPALETTE _hPalette;
GLSupport _gl;
}
dstring _caption;
Win32ColorDrawBuf _drawbuf;
@ -167,6 +169,9 @@ class Win32Window : Window {
Win32Window w32parent = cast(Win32Window)parent;
HWND parenthwnd = w32parent ? w32parent._hwnd : null;
_platform = platform;
version (USE_OPENGL) {
_gl = new GLSupport();
}
_caption = windowCaption;
_flags = flags;
uint ws = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
@ -199,7 +204,9 @@ class Win32Window : Window {
_hPalette = setupPalette(hDC);
_hGLRC = wglCreateContext(hDC);
if (_hGLRC) {
wglMakeCurrent(hDC, _hGLRC);
_glSupport = _gl;
if (!DERELICT_GL3_RELOADED) {
// run this code only once
@ -208,7 +215,7 @@ class Win32Window : Window {
import derelict.opengl3.gl3;
DerelictGL3.reload();
// successful
if (initShaders()) {
if (glSupport.initShaders()) {
setOpenglEnabled();
useOpengl = true;
} else {
@ -218,7 +225,7 @@ class Win32Window : Window {
Log.e("Derelict exception", e);
}
} else {
if (initShaders()) {
if (glSupport.initShaders()) {
setOpenglEnabled();
useOpengl = true;
} else {
@ -252,6 +259,7 @@ class Win32Window : Window {
//scope(exit) EndPaint(_hwnd, &ps);
HDC hdc = GetDC(_hwnd);
wglMakeCurrent(hdc, _hGLRC);
_glSupport = _gl;
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, _dx, _dy);
float a = 1.0f;
@ -288,7 +296,10 @@ class Win32Window : Window {
version (USE_OPENGL) {
import derelict.opengl3.wgl;
if (_hGLRC) {
uninitShaders();
glSupport.uninitShaders();
destroy(_glSupport);
_glSupport = null;
_gl = null;
wglMakeCurrent (null, null) ;
wglDeleteContext(_hGLRC);
_hGLRC = null;
@ -440,7 +451,7 @@ class Win32Window : Window {
resizedicon.invertAlpha();
ICONINFO ii;
HBITMAP mask = resizedicon.createTransparencyBitmap();
HBITMAP color = resizedicon.destoryLeavingBitmap();
HBITMAP color = resizedicon.destroyLeavingBitmap();
ii.fIcon = TRUE;
ii.xHotspot = 0;
ii.yHotspot = 0;
@ -448,8 +459,8 @@ class Win32Window : Window {
ii.hbmColor = color;
_icon = CreateIconIndirect(&ii);
if (_icon) {
SendMessage(_hwnd, WM_SETICON, ICON_SMALL, cast(int)_icon);
SendMessage(_hwnd, WM_SETICON, ICON_BIG, cast(int)_icon);
SendMessageW(_hwnd, WM_SETICON, ICON_SMALL, cast(LPARAM)_icon);
SendMessageW(_hwnd, WM_SETICON, ICON_BIG, cast(LPARAM)_icon);
} else {
Log.e("failed to create icon");
}
@ -470,7 +481,7 @@ class Win32Window : Window {
}
void onPaint() {
Log.d("onPaint()");
debug(DebugRedraw) Log.d("onPaint()");
long paintStart = currentTimeMillis;
version (USE_OPENGL) {
if (useOpengl && _hGLRC) {
@ -482,7 +493,7 @@ class Win32Window : Window {
paintUsingGDI();
}
long paintEnd = currentTimeMillis;
Log.d("WM_PAINT handling took ", paintEnd - paintStart, " ms");
debug(DebugRedraw) Log.d("WM_PAINT handling took ", paintEnd - paintStart, " ms");
}
protected ButtonDetails _lbutton;
@ -951,12 +962,17 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (window !is null) {
WINDOWPOS * pos = cast(WINDOWPOS*)lParam;
Log.d("WM_WINDOWPOSCHANGED: ", *pos);
GetClientRect(hwnd, &rect);
int dx = rect.right - rect.left;
int dy = rect.bottom - rect.top;
//window.onResize(pos.cx, pos.cy);
window.onResize(dx, dy);
InvalidateRect(hwnd, null, FALSE);
//if (!(pos.flags & 0x8000)) { //SWP_NOACTIVATE)) {
//if (pos.x > -30000) {
int dx = rect.right - rect.left;
int dy = rect.bottom - rect.top;
window.onResize(dx, dy);
InvalidateRect(hwnd, null, FALSE);
//}
//}
}
}
return 0;

View File

@ -73,13 +73,24 @@ class ComboBoxBase : HorizontalLayout, OnClickHandler {
onItemClickListener(this, index);
}
/// change enabled state
override @property Widget enabled(bool flg) {
super.enabled(flg);
_button.enabled = flg;
return this;
}
/// return true if state has State.Enabled flag set
override @property bool enabled() { return super.enabled; }
override bool onClick(Widget source) {
showPopup();
if (enabled)
showPopup();
return true;
}
protected ImageButton createButton() {
ImageButton res = new ImageButton("COMBOBOX_BUTTON", "scrollbar_btn_down");
res.styleId = STYLE_COMBO_BOX_BUTTON;
res.layoutWeight = 0;
res.onClickListener = this;
return res;
@ -99,7 +110,7 @@ class ComboBoxBase : HorizontalLayout, OnClickHandler {
_popupList = createPopup();
_popup = window.showPopup(_popupList, this, PopupAlign.Below | PopupAlign.FitAnchorSize);
_popup.flags = PopupFlags.CloseOnClickOutside;
_popup.styleId = "POPUP_MENU";
_popup.styleId = STYLE_POPUP_MENU;
_popup.onPopupCloseListener = delegate (PopupWidget source) {
_popup = null;
_popupList = null;
@ -121,6 +132,8 @@ class ComboBoxBase : HorizontalLayout, OnClickHandler {
super(ID);
_adapter = adapter;
_ownAdapter = ownAdapter;
styleId = STYLE_COMBO_BOX;
trackHover = true;
init();
}
@ -131,6 +144,8 @@ class ComboBoxBase : HorizontalLayout, OnClickHandler {
//_body.state = State.Parent;
//focusable = true;
_button.focusable = false;
_body.focusable = false;
focusable = true;
//_body.focusable = true;
addChild(_body);
addChild(_button);
@ -198,14 +213,13 @@ class ComboBox : ComboBoxBase {
_body.focusable = false;
_body.clickable = true;
focusable = true;
//styleId = "COMBO_BOX";
clickable = true;
onClickListener = this;
}
override protected Widget createSelectedItemWidget() {
TextWidget res = new TextWidget("COMBOBOX_BODY");
res.styleId = "COMBO_BOX";
TextWidget res = new TextWidget("COMBO_BOX_BODY");
res.styleId = STYLE_COMBO_BOX_BODY;
res.clickable = true;
res.layoutWidth = FILL_PARENT;
res.layoutHeight = WRAP_CONTENT;
@ -264,8 +278,8 @@ class ComboEdit : ComboBox {
override void init() {
super.init();
focusable = false;
_body.focusable = true;
//focusable = false;
//_body.focusable = true;
}
}

View File

@ -36,7 +36,7 @@ private import std.conv : to;
/// vertical spacer to fill empty space in vertical layouts
class VSpacer : Widget {
this() {
styleId = "VSPACER";
styleId = STYLE_VSPACER;
}
//override void measure(int parentWidth, int parentHeight) {
// measuredContent(parentWidth, parentHeight, 8, 8);
@ -46,7 +46,7 @@ class VSpacer : Widget {
/// horizontal spacer to fill empty space in horizontal layouts
class HSpacer : Widget {
this() {
styleId = "HSPACER";
styleId = STYLE_HSPACER;
}
//override void measure(int parentWidth, int parentHeight) {
// measuredContent(parentWidth, parentHeight, 8, 8);
@ -57,12 +57,12 @@ class HSpacer : Widget {
class TextWidget : Widget {
this(string ID = null, string textResourceId = null) {
super(ID);
styleId = "TEXT";
styleId = STYLE_TEXT;
_text = textResourceId;
}
this(string ID, dstring rawText) {
super(ID);
styleId = "TEXT";
styleId = STYLE_TEXT;
_text = rawText;
}
protected UIString _text;
@ -199,7 +199,7 @@ class ImageButton : ImageWidget {
/// constructor by id and icon resource id
this(string ID = null, string drawableId = null) {
super(ID, drawableId);
styleId = "BUTTON";
styleId = STYLE_BUTTON;
_drawableId = drawableId;
clickable = true;
focusable = true;
@ -247,10 +247,11 @@ class ImageTextButton : HorizontalLayout {
}
protected void init(string drawableId, UIString caption) {
styleId = "BUTTON";
styleId = STYLE_BUTTON;
_icon = new ImageWidget("icon", drawableId);
_icon.styleId = STYLE_BUTTON_IMAGE;
_label = new TextWidget("label", caption);
_label.styleId = "BUTTON_LABEL";
_label.styleId = STYLE_BUTTON_LABEL;
_icon.state = State.Parent;
_label.state = State.Parent;
addChild(_icon);
@ -285,12 +286,17 @@ class ImageTextButton : HorizontalLayout {
class CheckBox : ImageTextButton {
this(string ID = null, string textResourceId = null) {
super(ID, "btn_check", textResourceId);
styleId = "TRANSPARENT_BUTTON_BACKGROUND";
checkable = true;
}
this(string ID, dstring labelText) {
super(ID, "btn_check", labelText);
styleId = "TRANSPARENT_BUTTON_BACKGROUND";
}
override protected void init(string drawableId, UIString caption) {
super.init(drawableId, caption);
styleId = STYLE_CHECKBOX;
if (_icon)
_icon.styleId = STYLE_CHECKBOX_IMAGE;
if (_label)
_label.styleId = STYLE_CHECKBOX_LABEL;
checkable = true;
}
// called to process click and notify listeners
@ -304,12 +310,17 @@ class CheckBox : ImageTextButton {
class RadioButton : ImageTextButton {
this(string ID = null, string textResourceId = null) {
super(ID, "btn_radio", textResourceId);
styleId = "TRANSPARENT_BUTTON_BACKGROUND";
checkable = true;
}
this(string ID, dstring labelText) {
super(ID, "btn_radio", labelText);
styleId = "TRANSPARENT_BUTTON_BACKGROUND";
}
override protected void init(string drawableId, UIString caption) {
super.init(drawableId, caption);
styleId = STYLE_RADIOBUTTON;
if (_icon)
_icon.styleId = STYLE_RADIOBUTTON_IMAGE;
if (_label)
_label.styleId = STYLE_RADIOBUTTON_LABEL;
checkable = true;
}
@ -351,7 +362,7 @@ class Button : Widget {
}
private void init(UIString label) {
styleId = "BUTTON";
styleId = STYLE_BUTTON;
_text = label;
clickable = true;
focusable = true;
@ -397,7 +408,7 @@ class Button : Widget {
FontRef font = font();
Point sz = font.textSize(text);
applyAlign(rc, sz);
font.drawText(buf, rc.left, rc.top, text, textColor);
font.drawText(buf, rc.left, rc.top, text, textColor, 4, 0, textFlags);
}
}
@ -497,7 +508,7 @@ class ScrollBar : AbstractSlider, OnClickHandler {
class PageScrollButton : Widget {
this(string ID) {
super(ID);
styleId = "PAGE_SCROLL";
styleId = STYLE_PAGE_SCROLL;
trackHover = true;
clickable = true;
}
@ -511,7 +522,7 @@ class ScrollBar : AbstractSlider, OnClickHandler {
this(string resourceId) {
super("SLIDER", resourceId);
styleId = "BUTTON_NOMARGINS";
styleId = STYLE_BUTTON_NOMARGINS;
trackHover = true;
}
@ -671,14 +682,14 @@ class ScrollBar : AbstractSlider, OnClickHandler {
/// create with ID parameter
this(string ID, Orientation orient = Orientation.Vertical) {
super(ID);
styleId = "SCROLLBAR";
styleId = STYLE_SCROLLBAR;
_orientation = orient;
_btnBack = new ImageButton("BACK", style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_UP : ATTR_SCROLLBAR_BUTTON_LEFT));
_btnForward = new ImageButton("FORWARD", style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_DOWN : ATTR_SCROLLBAR_BUTTON_RIGHT));
_pageUp = new PageScrollButton("PAGE_UP");
_pageDown = new PageScrollButton("PAGE_DOWN");
_btnBack.styleId("SCROLLBAR_BUTTON");
_btnForward.styleId("SCROLLBAR_BUTTON");
_btnBack.styleId = STYLE_SCROLLBAR_BUTTON;
_btnForward.styleId = STYLE_SCROLLBAR_BUTTON;
_indicator = new SliderButton(style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_INDICATOR_VERTICAL : ATTR_SCROLLBAR_INDICATOR_HORIZONTAL));
addChild(_btnBack);
addChild(_btnForward);

View File

@ -926,6 +926,13 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
return true;
}
/// returns true if widget is focusable and visible and enabled
override @property bool canFocus() {
// allow to focus even if not enabled
return focusable && visible;
}
/// override to change popup menu items state
override bool isActionEnabled(const Action action) {
switch (action.id) {
@ -1727,7 +1734,7 @@ class EditLine : EditWidgetBase {
_content = new EditableContent(false);
_content.contentChangeListeners = this;
wantTabs = false;
styleId = "EDIT_LINE";
styleId = STYLE_EDIT_LINE;
text = initialContent;
}
@ -1905,7 +1912,7 @@ class EditBox : EditWidgetBase {
super(ID, hscrollbarMode, vscrollbarMode);
_content = new EditableContent(true); // multiline
_content.contentChangeListeners = this;
styleId = "EDIT_BOX";
styleId = STYLE_EDIT_BOX;
text = initialContent;
}

View File

@ -1170,7 +1170,7 @@ class StringGridWidget : StringGridWidgetBase {
/// create with ID parameter
this(string ID) {
super(ID);
styleId = "EDIT_BOX";
styleId = STYLE_EDIT_BOX;
}
/// get cell text
override dstring cellText(int col, int row) {

View File

@ -174,15 +174,20 @@ class LayoutItems {
contentSecondarySize = maxItem;
else
contentSecondarySize = rc.width;
if (_layoutHeight == FILL_PARENT || totalSize > rc.height)
if (_layoutHeight == FILL_PARENT && totalSize < rc.height && resizableSize > 0) {
delta = rc.height - totalSize; // total space to add to fit
} else if (totalSize > rc.height) {
delta = rc.height - totalSize; // total space to reduce to fit
}
} else {
if (_layoutHeight == WRAP_CONTENT && maxItem < rc.height)
contentSecondarySize = maxItem;
else
contentSecondarySize = rc.height;
if (_layoutWidth == FILL_PARENT || totalSize > rc.width)
if (_layoutWidth == FILL_PARENT && totalSize < rc.width && resizableSize > 0)
delta = rc.width - totalSize; // total space to add to fit
else if (totalSize > rc.width)
delta = rc.width - totalSize; // total space to reduce to fit
}
// calculate resize options and scale
bool needForceResize = false;
@ -194,7 +199,7 @@ class LayoutItems {
// need resize of some children
needResize = true;
// resize all if need to shrink or only resizable are too small to correct delta
needForceResize = delta < 0 || resizableWeight == 0; // || resizableSize * 2 / 3 < delta; // do we need resize non-FILL_PARENT items?
needForceResize = /*delta < 0 || */ resizableWeight == 0; // || resizableSize * 2 / 3 < delta; // do we need resize non-FILL_PARENT items?
// calculate scale factor: weight / delta * 10000
if (needForceResize && nonresizableSize + resizableSize > 0)
scaleFactor = 10000 * delta / (nonresizableSize + resizableSize);
@ -467,7 +472,7 @@ class ResizerWidget : Widget {
/// Arranges items either vertically or horizontally
class LinearLayout : WidgetGroup {
class LinearLayout : WidgetGroupDefaultDrawing {
protected Orientation _orientation = Orientation.Vertical;
/// returns linear layout orientation (Vertical, Horizontal)
@property Orientation orientation() { return _orientation; }
@ -514,22 +519,6 @@ class LinearLayout : WidgetGroup {
applyPadding(rc);
_layoutItems.layout(rc);
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
auto saver = ClipRectSaver(buf, rc);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
}
}
@ -560,7 +549,7 @@ class HorizontalLayout : LinearLayout {
}
/// place all children into same place (usually, only one child should be visible at a time)
class FrameLayout : WidgetGroup {
class FrameLayout : WidgetGroupDefaultDrawing {
/// empty parameter list constructor - for usage by factory
this() {
this(null);
@ -612,23 +601,6 @@ class FrameLayout : WidgetGroup {
}
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
auto saver = ClipRectSaver(buf, rc);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
}
/// make one of children (with specified ID) visible, for the rest, set visibility to otherChildrenVisibility
bool showChild(string ID, Visibility otherChildrenVisibility = Visibility.Invisible, bool updateFocus = false) {
bool found = false;
@ -650,7 +622,7 @@ class FrameLayout : WidgetGroup {
}
/// layout children as table with rows and columns
class TableLayout : WidgetGroup {
class TableLayout : WidgetGroupDefaultDrawing {
this(string ID = null) {
super(ID);
@ -840,21 +812,4 @@ class TableLayout : WidgetGroup {
_cells.layout(rc);
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
auto saver = ClipRectSaver(buf, rc);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
}
}

View File

@ -114,7 +114,7 @@ class StringListAdapter : ListAdapter {
updateStatesLength();
if (_widget is null) {
_widget = new TextWidget("LIST_ITEM");
_widget.styleId = "LIST_ITEM";
_widget.styleId = STYLE_LIST_ITEM;
} else {
if (index == _lastItemIndex)
return _widget;

View File

@ -206,7 +206,7 @@ class MenuItem {
}
/// widget to draw menu item
class MenuItemWidget : WidgetGroup {
class MenuItemWidget : WidgetGroupDefaultDrawing {
protected bool _mainMenu;
protected MenuItem _item;
protected ImageWidget _icon;
@ -298,29 +298,11 @@ class MenuItemWidget : WidgetGroup {
resetState(State.Checked);
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
updateState();
auto saver = ClipRectSaver(buf, rc, alpha);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
}
this(MenuItem item, bool mainMenu) {
id="menuitem";
_mainMenu = mainMenu;
_item = item;
styleId = "MENU_ITEM";
styleId = STYLE_MENU_ITEM;
updateState();
string iconId = _item.action.iconId;
if (_item.type == MenuItemType.Check)
@ -330,7 +312,7 @@ class MenuItemWidget : WidgetGroup {
// icon
if (_item.action && iconId.length) {
_icon = new ImageWidget("MENU_ICON", iconId);
_icon.styleId = "MENU_ICON";
_icon.styleId = STYLE_MENU_ICON;
_icon.state = State.Parent;
addChild(_icon);
}
@ -352,7 +334,7 @@ class MenuItemWidget : WidgetGroup {
}
if (acc !is null) {
_accel = new TextWidget("MENU_ACCEL");
_accel.styleId = "MENU_ACCEL";
_accel.styleId = STYLE_MENU_ACCEL;
_accel.text = acc;
_accel.state = State.Parent;
if (_item.isSubmenu && !mainMenu)
@ -382,13 +364,13 @@ class MenuWidgetBase : ListWidget {
_item = item;
this.orientation = orientation;
id = "popup_menu";
styleId = "POPUP_MENU";
styleId = STYLE_POPUP_MENU;
WidgetListAdapter adapter = new WidgetListAdapter();
for (int i=0; i < _item.subitemCount; i++) {
MenuItem subitem = _item.subitem(i);
MenuItemWidget widget = new MenuItemWidget(subitem, orientation == Orientation.Horizontal);
if (orientation == Orientation.Horizontal)
widget.styleId = "MAIN_MENU_ITEM";
widget.styleId = STYLE_MAIN_MENU_ITEM;
widget.parent = this;
adapter.widgets.add(widget);
}
@ -650,7 +632,7 @@ class MainMenu : MenuWidgetBase {
this(MenuItem item) {
super(null, item, Orientation.Horizontal);
id = "MAIN_MENU";
styleId = "MAIN_MENU";
styleId = STYLE_MAIN_MENU;
_clickOnButtonDown = true;
}
@ -799,7 +781,7 @@ class PopupMenu : MenuWidgetBase {
this(MenuItem item, MenuWidgetBase parentMenu = null) {
super(parentMenu, item, Orientation.Vertical);
id = "POPUP_MENU";
styleId = "POPUP_MENU";
styleId = STYLE_POPUP_MENU;
selectOnHover = true;
}
}

View File

@ -217,11 +217,19 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
pwidth -= m.left + m.right + p.left + p.right;
if (parentHeight != SIZE_UNSPECIFIED)
pheight -= m.top + m.bottom + p.top + p.bottom;
if (_hscrollbar)
if (_hscrollbar) {
_hscrollbar.measure(pwidth, pheight);
if (_vscrollbar)
}
if (_vscrollbar) {
_vscrollbar.measure(pwidth, pheight);
}
Point sz = fullContentSize();
if (_hscrollbar) {
sz.y += _hscrollbar.measuredHeight;
}
if (_vscrollbar) {
sz.x += _vscrollbar.measuredWidth;
}
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
}
@ -292,9 +300,9 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
If size of content widget exceeds available space, allows to scroll it.
*/
class ScrollWidget : ScrollWidgetBase {
protected WidgetGroup _contentWidget;
@property WidgetGroup contentWidget() { return _contentWidget; }
@property ScrollWidget contentWidget(WidgetGroup newContent) {
protected Widget _contentWidget;
@property Widget contentWidget() { return _contentWidget; }
@property ScrollWidget contentWidget(Widget newContent) {
if (_contentWidget) {
removeChild(childIndex(_contentWidget));
destroy(_contentWidget);

View File

@ -25,14 +25,110 @@ private import std.string;
private import std.algorithm;
import dlangui.core.types;
import dlangui.graphics.colors;
import dlangui.graphics.fonts;
import dlangui.graphics.drawbuf;
import dlangui.graphics.resources;
// Standard style constants
// Themes should define all of these styles in order to support all controls
/// standard style id for TextWidget
immutable string STYLE_TEXT = "TEXT";
/// standard style id for Button
immutable string STYLE_BUTTON = "BUTTON";
/// standard style id for Button label
immutable string STYLE_BUTTON_LABEL = "BUTTON_LABEL";
/// standard style id for Button image
immutable string STYLE_BUTTON_IMAGE = "BUTTON_IMAGE";
/// style id for transparent Button
immutable string STYLE_BUTTON_TRANSPARENT = "BUTTON_TRANSPARENT";
/// style id for Button w/o margins
immutable string STYLE_BUTTON_NOMARGINS = "BUTTON_NOMARGINS";
/// standard style id for CheckBox
immutable string STYLE_CHECKBOX = "CHECKBOX";
/// standard style id for CheckBox image
immutable string STYLE_CHECKBOX_IMAGE = "CHECKBOX_IMAGE";
/// standard style id for CheckBox label
immutable string STYLE_CHECKBOX_LABEL = "CHECKBOX_LABEL";
/// standard style id for RadioButton
immutable string STYLE_RADIOBUTTON = "RADIOBUTTON";
/// standard style id for RadioButton image
immutable string STYLE_RADIOBUTTON_IMAGE = "RADIOBUTTON_IMAGE";
/// standard style id for RadioButton label
immutable string STYLE_RADIOBUTTON_LABEL = "RADIOBUTTON_LABEL";
/// standard style id for HSpacer
immutable string STYLE_HSPACER = "HSPACER";
/// standard style id for VSpacer
immutable string STYLE_VSPACER = "VSPACER";
/// standard style id for ScrollBar
immutable string STYLE_SCROLLBAR = "SCROLLBAR";
/// standard style id for ScrollBar button
immutable string STYLE_SCROLLBAR_BUTTON = "SCROLLBAR_BUTTON";
/// standard style id for ScrollBar page control
immutable string STYLE_PAGE_SCROLL = "PAGE_SCROLL";
/// standard style id for Slider
immutable string STYLE_SLIDER = "SLIDER";
/// standard style id for TabWidget
immutable string STYLE_TAB_WIDGET = "TAB_WIDGET";
/// standard style id for Tab with Up alignment
immutable string STYLE_TAB_UP = "TAB_UP";
/// standard style id for button of Tab with Up alignment
immutable string STYLE_TAB_UP_BUTTON = "TAB_UP_BUTTON";
/// standard style id for button of Tab with Up alignment
immutable string STYLE_TAB_UP_BUTTON_TEXT = "TAB_UP_BUTTON_TEXT";
/// standard style id for TabHost
immutable string STYLE_TAB_HOST = "TAB_HOST";
/// standard style id for PopupMenu
immutable string STYLE_POPUP_MENU = "POPUP_MENU";
/// standard style id for menu item
immutable string STYLE_MENU_ITEM = "MENU_ITEM";
/// standard style id for menu item label
immutable string STYLE_MENU_LABEL = "MENU_LABEL";
/// standard style id for menu item icon
immutable string STYLE_MENU_ICON = "MENU_ICON";
/// standard style id for menu item accelerators label
immutable string STYLE_MENU_ACCEL = "MENU_ACCEL";
/// standard style id for main menu item
immutable string STYLE_MAIN_MENU_ITEM = "MAIN_MENU_ITEM";
/// standard style id for main menu item label
immutable string STYLE_MAIN_MENU_LABEL = "MAIN_MENU_LABEL";
/// standard style id for main menu
immutable string STYLE_MAIN_MENU = "MAIN_MENU";
/// standard style id for list items
immutable string STYLE_LIST_ITEM = "LIST_ITEM";
/// standard style id for EditLine
immutable string STYLE_EDIT_LINE = "EDIT_LINE";
/// standard style id for EditBox
immutable string STYLE_EDIT_BOX = "EDIT_BOX";
/// standard style id for background similar to transparent button
immutable string STYLE_TRANSPARENT_BUTTON_BACKGROUND = "TRANSPARENT_BUTTON_BACKGROUND";
/// standard style id for tree item
immutable string STYLE_TREE_ITEM = "TREE_ITEM";
/// standard style id for tree item label
immutable string STYLE_TREE_ITEM_LABEL = "TREE_ITEM_LABEL";
/// standard style id for tree item icon
immutable string STYLE_TREE_ITEM_ICON = "TREE_ITEM_ICON";
/// standard style id for tree item expand icon
immutable string STYLE_TREE_ITEM_EXPAND_ICON = "TREE_ITEM_EXPAND_ICON";
/// standard style id for combo box
immutable string STYLE_COMBO_BOX = "COMBO_BOX";
/// standard style id for combo box button
immutable string STYLE_COMBO_BOX_BUTTON = "COMBO_BOX_BUTTON";
/// standard style id for combo box body (current item)
immutable string STYLE_COMBO_BOX_BODY = "COMBO_BOX_BODY";
// Layout size constants
/// layout option, to occupy all available place
immutable int FILL_PARENT = int.max - 1;
/// layout option, for size based on content
immutable int WRAP_CONTENT = int.max - 2;
/// use as widget.layout() param to avoid applying of parent size
immutable int SIZE_UNSPECIFIED = int.max;
// Other style constants
/// unspecified align - to take parent's value instead
immutable ubyte ALIGN_UNSPECIFIED = 0;
immutable uint COLOR_UNSPECIFIED = 0xFFDEADFF;
/// transparent color constant
immutable uint COLOR_TRANSPARENT = 0xFFFFFFFF;
/// unspecified font size constant - to take parent style property value
immutable ushort FONT_SIZE_UNSPECIFIED = 0xFFFF;
/// unspecified font weight constant - to take parent style property value
@ -43,20 +139,19 @@ immutable ubyte FONT_STYLE_UNSPECIFIED = 0xFF;
immutable ubyte FONT_STYLE_NORMAL = 0x00;
/// italic font style constant
immutable ubyte FONT_STYLE_ITALIC = 0x01;
/// use as widget.layout() param to avoid applying of parent size
immutable int SIZE_UNSPECIFIED = int.max;
/// use text flags from parent style
immutable uint TEXT_FLAGS_UNSPECIFIED = uint.max;
/// use text flags from parent widget
immutable uint TEXT_FLAGS_USE_PARENT = uint.max - 1;
/// layout option, to occupy all available place
immutable int FILL_PARENT = int.max - 1;
/// layout option, for size based on content
immutable int WRAP_CONTENT = int.max - 2;
/// to take layout weight from parent
immutable int WEIGHT_UNSPECIFIED = -1;
/// returns true for WRAP_CONTENT, WRAP_CONTENT, SIZE_UNSPECIFIED
bool isSpecialSize(int sz) {
// don't forget to update if more special constants added
return sz >= WRAP_CONTENT;
}
/// Align option bit constants
enum Align : ubyte {
/// alignment is not specified
@ -91,32 +186,6 @@ enum TextFlag : uint {
Underline = 8
}
/// custom drawable attribute container for styles
class DrawableAttribute {
protected string _id;
protected string _drawableId;
protected DrawableRef _drawable;
protected bool _initialized;
this(string id, string drawableId) {
_id = id;
_drawableId = drawableId;
}
@property string id() const { return _id; }
@property string drawableId() const { return _drawableId; }
@property void drawableId(string newDrawable) { _drawableId = newDrawable; clear(); }
@property ref DrawableRef drawable() const {
if (!_drawable.isNull)
return (cast(DrawableAttribute)this)._drawable;
(cast(DrawableAttribute)this)._drawable = drawableCache.get(_id);
(cast(DrawableAttribute)this)._initialized = true;
return (cast(DrawableAttribute)this)._drawable;
}
void clear() {
_drawable.clear();
_initialized = false;
}
}
/// style properties
class Style {
protected string _id;
@ -146,10 +215,13 @@ class Style {
protected int _layoutHeight = SIZE_UNSPECIFIED;
protected int _layoutWeight = WEIGHT_UNSPECIFIED;
protected uint[] _focusRectColors;
protected Style[] _substates;
protected Style[] _children;
protected DrawableAttribute[string] _customDrawables;
protected uint[string] _customColors;
protected FontRef _font;
protected DrawableRef _backgroundDrawable;
@ -226,6 +298,19 @@ class Style {
return this;
}
/// get custom color attribute
uint customColor(string id) {
if (id in _customColors)
return _customColors[id];
return parentStyle.customColor(id);
}
/// sets custom color attribute for style
Style setCustomColor(string id, uint color) {
_customColors[id] = color;
return this;
}
//===================================================
// font properties
@ -532,6 +617,22 @@ class Style {
return this;
}
/// returns colors to draw focus rectangle (one for solid, two for vertical gradient) or null if no focus rect should be drawn for style
@property const(uint[]) focusRectColors() const {
if (_focusRectColors) {
if (_focusRectColors.length == 1 && _focusRectColors[0] == COLOR_UNSPECIFIED)
return null;
return cast(const)_focusRectColors;
}
return parentStyle.focusRectColors;
}
/// sets colors to draw focus rectangle or null if no focus rect should be drawn for style
@property Style focusRectColors(uint[] colors) {
_focusRectColors = colors;
return this;
}
Style setPadding(int left, int top, int right, int bottom) {
_padding.left = left;
_padding.top = top;
@ -616,7 +717,7 @@ class Theme : Style {
this(string id) {
super(this, id);
_parentStyle = null;
_backgroundColor = 0xFFFFFFFF; // transparent
_backgroundColor = COLOR_TRANSPARENT; // transparent
_textColor = 0x000000; // black
_align = Align.TopLeft;
_fontSize = 14; // TODO: from settings or screen properties / DPI
@ -677,18 +778,32 @@ class Theme : Style {
}
private DrawableRef _emptyDrawable;
@property override ref DrawableRef customDrawable(string id) const {
override ref DrawableRef customDrawable(string id) const {
if (id in _customDrawables)
return _customDrawables[id].drawable;
return (cast(Theme)this)._emptyDrawable;
}
@property override string customDrawableId(string id) const {
override string customDrawableId(string id) const {
if (id in _customDrawables)
return _customDrawables[id].drawableId;
return null;
}
/// get custom color attribute - transparent by default
override uint customColor(string id) {
if (id in _customColors)
return _customColors[id];
return COLOR_TRANSPARENT;
}
/// returns colors to draw focus rectangle or null if no focus rect should be drawn for style
@property override const(uint[]) focusRectColors() const {
if (_focusRectColors)
return _focusRectColors;
return null;
}
/// create new named style or get existing
override Style createSubstyle(string id) {
if (id !is null && id in _byId)
@ -745,14 +860,14 @@ Theme createDefaultTheme() {
}
//res.fontFace = "Arial Narrow";
res.fontSize = 15; // TODO: choose based on DPI
Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small").alignment(Align.Center).setMargins(5,5,5,5);
res.createSubstyle("BUTTON_TRANSPARENT").backgroundImageId("btn_default_small_transparent").alignment(Align.Center);
res.createSubstyle("BUTTON_LABEL").layoutWidth(FILL_PARENT).alignment(Align.Left|Align.VCenter);
res.createSubstyle("BUTTON_ICON").alignment(Align.Center);
res.createSubstyle("TEXT").setMargins(2,2,2,2).setPadding(1,1,1,1);
res.createSubstyle("HSPACER").layoutWidth(FILL_PARENT).minWidth(5).layoutWeight(100);
res.createSubstyle("VSPACER").layoutHeight(FILL_PARENT).minHeight(5).layoutWeight(100);
res.createSubstyle("BUTTON_NOMARGINS").backgroundImageId("btn_default_small").alignment(Align.Center); // .setMargins(5,5,5,5)
Style button = res.createSubstyle(STYLE_BUTTON).backgroundImageId("btn_background").alignment(Align.Center).setMargins(5,5,5,5);
res.createSubstyle(STYLE_BUTTON_TRANSPARENT).backgroundImageId("btn_background_transparent").alignment(Align.Center);
res.createSubstyle(STYLE_BUTTON_LABEL).layoutWidth(FILL_PARENT).alignment(Align.Left|Align.VCenter);
res.createSubstyle(STYLE_BUTTON_IMAGE).alignment(Align.Center);
res.createSubstyle(STYLE_TEXT).setMargins(2,2,2,2).setPadding(1,1,1,1);
res.createSubstyle(STYLE_HSPACER).layoutWidth(FILL_PARENT).minWidth(5).layoutWeight(100);
res.createSubstyle(STYLE_VSPACER).layoutHeight(FILL_PARENT).minHeight(5).layoutWeight(100);
res.createSubstyle(STYLE_BUTTON_NOMARGINS).backgroundImageId("btn_background").alignment(Align.Center); // .setMargins(5,5,5,5)
//button.createState(State.Enabled | State.Focused, State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
//button.createState(State.Enabled, 0).backgroundImageId("btn_default_small_normal_disable");
//button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed");
@ -765,67 +880,67 @@ Theme createDefaultTheme() {
res.setCustomDrawable(ATTR_SCROLLBAR_INDICATOR_VERTICAL, "scrollbar_indicator_vertical");
res.setCustomDrawable(ATTR_SCROLLBAR_INDICATOR_HORIZONTAL, "scrollbar_indicator_horizontal");
Style scrollbar = res.createSubstyle("SCROLLBAR");
Style scrollbar = res.createSubstyle(STYLE_SCROLLBAR);
scrollbar.backgroundColor(0xC0808080);
Style scrollbarButton = button.createSubstyle("SCROLLBAR_BUTTON");
Style scrollbarSlider = res.createSubstyle("SLIDER");
Style scrollbarPage = res.createSubstyle("PAGE_SCROLL").backgroundColor(0xFFFFFFFF);
Style scrollbarButton = button.createSubstyle(STYLE_SCROLLBAR_BUTTON);
Style scrollbarSlider = res.createSubstyle(STYLE_SLIDER);
Style scrollbarPage = res.createSubstyle(STYLE_PAGE_SCROLL).backgroundColor(COLOR_TRANSPARENT);
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404080);
scrollbarPage.createState(State.Hovered, State.Hovered).backgroundColor(0xF0404080);
Style tabUp = res.createSubstyle("TAB_UP");
Style tabUp = res.createSubstyle(STYLE_TAB_UP);
tabUp.backgroundImageId("tab_up_background");
tabUp.layoutWidth(FILL_PARENT);
tabUp.createState(State.Selected, State.Selected).backgroundImageId("tab_up_backgrond_selected");
Style tabUpButtonText = res.createSubstyle("TAB_UP_BUTTON_TEXT");
Style tabUpButtonText = res.createSubstyle(STYLE_TAB_UP_BUTTON_TEXT);
tabUpButtonText.textColor(0x000000).fontSize(12).alignment(Align.Center);
tabUpButtonText.createState(State.Selected, State.Selected).textColor(0x000000);
tabUpButtonText.createState(State.Selected|State.Focused, State.Selected|State.Focused).textColor(0x000000);
tabUpButtonText.createState(State.Focused, State.Focused).textColor(0x000000);
tabUpButtonText.createState(State.Hovered, State.Hovered).textColor(0xFFE0E0);
Style tabUpButton = res.createSubstyle("TAB_UP_BUTTON");
Style tabUpButton = res.createSubstyle(STYLE_TAB_UP_BUTTON);
tabUpButton.backgroundImageId("tab_btn_up");
//tabUpButton.backgroundImageId("tab_btn_up_normal");
//tabUpButton.createState(State.Selected, State.Selected).backgroundImageId("tab_btn_up_selected");
//tabUpButton.createState(State.Selected|State.Focused, State.Selected|State.Focused).backgroundImageId("tab_btn_up_focused_selected");
//tabUpButton.createState(State.Focused, State.Focused).backgroundImageId("tab_btn_up_focused");
//tabUpButton.createState(State.Hovered, State.Hovered).backgroundImageId("tab_btn_up_hover");
Style tabHost = res.createSubstyle("TAB_HOST");
Style tabHost = res.createSubstyle(STYLE_TAB_HOST);
tabHost.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
tabHost.backgroundColor(0xF0F0F0);
Style tabWidget = res.createSubstyle("TAB_WIDGET");
Style tabWidget = res.createSubstyle(STYLE_TAB_WIDGET);
tabWidget.setPadding(3,3,3,3).backgroundColor(0xEEEEEE);
//tabWidget.backgroundImageId("frame_blue");
//res.dumpStats();
Style mainMenu = res.createSubstyle("MAIN_MENU").backgroundColor(0xEFEFF2).layoutWidth(FILL_PARENT);
Style mainMenuItem = res.createSubstyle("MAIN_MENU_ITEM").setPadding(4,2,4,2).backgroundImageId("main_menu_item_background").textFlags(TEXT_FLAGS_USE_PARENT);
Style menuItem = res.createSubstyle("MENU_ITEM").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ;
Style mainMenu = res.createSubstyle(STYLE_MAIN_MENU).backgroundColor(0xEFEFF2).layoutWidth(FILL_PARENT);
Style mainMenuItem = res.createSubstyle(STYLE_MAIN_MENU_ITEM).setPadding(4,2,4,2).backgroundImageId("main_menu_item_background").textFlags(TEXT_FLAGS_USE_PARENT);
Style menuItem = res.createSubstyle(STYLE_MENU_ITEM).setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ;
menuItem.createState(State.Focused, State.Focused).backgroundColor(0x40C0C000);
menuItem.createState(State.Pressed, State.Pressed).backgroundColor(0x4080C000);
menuItem.createState(State.Selected, State.Selected).backgroundColor(0x00F8F9Fa);
menuItem.createState(State.Hovered, State.Hovered).backgroundColor(0xC0FFFF00);
res.createSubstyle("MENU_ICON").setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).alpha(0xA0);
res.createSubstyle("MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TextFlag.UnderlineHotKeys).createState(State.Enabled,0).textColor(0x80404040);
res.createSubstyle("MAIN_MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TEXT_FLAGS_USE_PARENT).createState(State.Enabled,0).textColor(0x80404040);
res.createSubstyle("MENU_ACCEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).textColor(0x80404040);
res.createSubstyle(STYLE_MENU_ICON).setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).alpha(0xA0);
res.createSubstyle(STYLE_MENU_LABEL).setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TextFlag.UnderlineHotKeys).createState(State.Enabled,0).textColor(0x80404040);
res.createSubstyle(STYLE_MAIN_MENU_LABEL).setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TEXT_FLAGS_USE_PARENT).createState(State.Enabled,0).textColor(0x80404040);
res.createSubstyle(STYLE_MENU_ACCEL).setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).textColor(0x80404040);
Style transparentButtonBackground = res.createSubstyle("TRANSPARENT_BUTTON_BACKGROUND").backgroundImageId("transparent_button_background").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ;
Style transparentButtonBackground = res.createSubstyle(STYLE_TRANSPARENT_BUTTON_BACKGROUND).backgroundImageId("transparent_button_background").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ;
//transparentButtonBackground.createState(State.Focused, State.Focused).backgroundColor(0xC0C0C000);
//transparentButtonBackground.createState(State.Pressed, State.Pressed).backgroundColor(0x4080C000);
//transparentButtonBackground.createState(State.Selected, State.Selected).backgroundColor(0x00F8F9Fa);
//transparentButtonBackground.createState(State.Hovered, State.Hovered).backgroundColor(0xD0FFFF00);
Style poopupMenu = res.createSubstyle("POPUP_MENU").backgroundImageId("popup_menu_background_normal");
Style poopupMenu = res.createSubstyle(STYLE_POPUP_MENU).backgroundImageId("popup_menu_background_normal");
Style listItem = res.createSubstyle("LIST_ITEM").backgroundImageId("list_item_background");
Style listItem = res.createSubstyle(STYLE_LIST_ITEM).backgroundImageId("list_item_background");
//listItem.createState(State.Selected, State.Selected).backgroundColor(0xC04040FF).textColor(0x000000);
//listItem.createState(State.Enabled, 0).textColor(0x80000000); // half transparent text for disabled item
Style editLine = res.createSubstyle("EDIT_LINE").backgroundImageId("editbox_background")
Style editLine = res.createSubstyle(STYLE_EDIT_LINE).backgroundImageId("editbox_background")
.setPadding(5,6,5,6).setMargins(2,2,2,2).minWidth(40)
.fontFace("Arial").fontFamily(FontFamily.SansSerif).fontSize(16);
Style editBox = res.createSubstyle("EDIT_BOX").backgroundImageId("editbox_background")
Style editBox = res.createSubstyle(STYLE_EDIT_BOX).backgroundImageId("editbox_background")
.setPadding(5,6,5,6).setMargins(2,2,2,2).minWidth(100).minHeight(60).layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT)
.fontFace("Courier New").fontFamily(FontFamily.MonoSpace).fontSize(16);
@ -858,6 +973,25 @@ Rect decodeRect(string s) {
return Rect(0,0,0,0);
}
private import std.array : split;
/// Decode color list attribute, e.g.: "#84A, #99FFFF" -> [0x8844aa, 0x99ffff]
uint[] decodeFocusRectColors(string s) {
if (s.equal("@null"))
return [COLOR_UNSPECIFIED];
string[] colors = split(s, ",");
if (colors.length < 1)
return null;
uint[] res = new uint[colors.length];
for (int i = 0; i < colors.length; i++) {
uint cl = decodeHexColor(colors[i], COLOR_UNSPECIFIED);
if (cl == COLOR_UNSPECIFIED)
return null;
res[i] = cl;
}
return res;
}
/// parses string like "Left|VCenter" to bit set of Align flags
ubyte decodeAlignment(string s) {
ubyte res = 0;
@ -983,6 +1117,8 @@ bool loadStyleAttributes(Style style, Element elem, bool allowStates) {
style.alpha = decodeDimension(elem.tag.attr["alpha"]);
if ("textFlags" in elem.tag.attr)
style.textFlags = decodeTextFlags(elem.tag.attr["textFlags"]);
if ("focusRectColors" in elem.tag.attr)
style.focusRectColors = decodeFocusRectColors(elem.tag.attr["focusRectColors"]);
foreach(item; elem.elements) {
if (allowStates && item.tag.name.equal("state")) {
uint stateMask = 0;
@ -998,6 +1134,13 @@ bool loadStyleAttributes(Style style, Element elem, bool allowStates) {
string drawablevalue = attrValue(item, "value");
if (drawableid)
style.setCustomDrawable(drawableid, drawablevalue);
} else if (item.tag.name.equal("color")) {
// <color id="buttons_panel_color" value="#303080"/>
string colorid = attrValue(item, "id");
string colorvalue = attrValue(item, "value");
uint color = decodeHexColor(colorvalue, COLOR_TRANSPARENT);
if (colorid)
style.setCustomColor(colorid, color);
}
}
return true;
@ -1011,7 +1154,7 @@ bool loadStyleAttributes(Style style, Element elem, bool allowStates) {
* <?xml version="1.0" encoding="utf-8"?>
* <theme id="theme_custom" parent="theme_default">
* <style id="BUTTON"
* backgroundImageId="btn_default_small"
* backgroundImageId="btn_background"
* >
* </style>
* </theme>
@ -1092,6 +1235,33 @@ Theme loadTheme(string resourceId) {
return null;
}
/// custom drawable attribute container for styles
class DrawableAttribute {
protected string _id;
protected string _drawableId;
protected DrawableRef _drawable;
protected bool _initialized;
this(string id, string drawableId) {
_id = id;
_drawableId = drawableId;
}
@property string id() const { return _id; }
@property string drawableId() const { return _drawableId; }
@property void drawableId(string newDrawable) { _drawableId = newDrawable; clear(); }
@property ref DrawableRef drawable() const {
if (!_drawable.isNull)
return (cast(DrawableAttribute)this)._drawable;
(cast(DrawableAttribute)this)._drawable = drawableCache.get(_id);
(cast(DrawableAttribute)this)._initialized = true;
return (cast(DrawableAttribute)this)._drawable;
}
void clear() {
_drawable.clear();
_initialized = false;
}
}
shared static ~this() {
currentTheme = null;
}

View File

@ -67,14 +67,14 @@ class TabItemWidget : HorizontalLayout {
@property TabItem tabItem() { return _item; }
@property TabControl tabControl() { return cast(TabControl)parent; }
this(TabItem item, bool enableCloseButton = true) {
styleId = "TAB_UP_BUTTON";
styleId = STYLE_TAB_UP_BUTTON;
_enableCloseButton = enableCloseButton;
_icon = new ImageWidget();
_label = new TextWidget();
_label.styleId = "TAB_UP_BUTTON_TEXT";
_label.styleId = STYLE_TAB_UP_BUTTON_TEXT;
_label.state = State.Parent;
_closeButton = new ImageButton("CLOSE");
_closeButton.styleId = "BUTTON_TRANSPARENT";
_closeButton.styleId = STYLE_BUTTON_TRANSPARENT;
_closeButton.drawableId = "close";
_closeButton.trackHover = true;
_closeButton.onClickListener = &onClick;
@ -167,7 +167,7 @@ class TabItemList {
}
/// tab header - tab labels, with optional More button
class TabControl : WidgetGroup {
class TabControl : WidgetGroupDefaultDrawing {
protected TabItemList _items;
protected ImageButton _moreButton;
protected bool _enableCloseButton;
@ -186,11 +186,11 @@ class TabControl : WidgetGroup {
super(ID);
_items = new TabItemList();
_moreButton = new ImageButton("MORE", "tab_more");
_moreButton.styleId = "BUTTON_TRANSPARENT";
_moreButton.styleId = STYLE_BUTTON_TRANSPARENT;
_moreButton.onClickListener = &onClick;
_moreButton.margins(Rect(3,3,3,6));
_enableCloseButton = true;
styleId = "TAB_UP";
styleId = STYLE_TAB_UP;
addChild(_moreButton); // first child is always MORE button, the rest corresponds to tab list
}
/// returns tab count
@ -338,24 +338,6 @@ class TabControl : WidgetGroup {
}
//Log.d("tabControl.layout exit");
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
//Log.d("tabControl.onDraw enter");
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
auto saver = ClipRectSaver(buf, rc);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
//Log.d("tabControl.onDraw exit");
}
protected string _selectedTabId;
@ -388,7 +370,7 @@ class TabHost : FrameLayout, TabHandler {
_tabControl = tabControl;
if (_tabControl !is null)
_tabControl.onTabChangedListener = &onTabChanged;
styleId = "TAB_HOST";
styleId = STYLE_TAB_HOST;
}
protected TabControl _tabControl;
/// get currently set control widget
@ -478,7 +460,7 @@ class TabWidget : VerticalLayout, TabHandler {
_tabControl = new TabControl("TAB_CONTROL");
_tabHost = new TabHost("TAB_HOST", _tabControl);
_tabControl.onTabChangedListener.connect(this);
styleId = "TAB_WIDGET";
styleId = STYLE_TAB_WIDGET;
addChild(_tabControl);
addChild(_tabHost);
}

View File

@ -122,6 +122,10 @@ class TreeItem {
p = p._parent;
return cast(TreeItems)p;
}
void clear() {
_children.clear();
}
@property TreeItem parent() { return _parent; }
@property protected TreeItem parent(TreeItem p) { _parent = p; return this; }
@ -197,6 +201,29 @@ class TreeItem {
return _parent.topParent;
}
protected int _intParam;
protected Object _objectParam;
@property int intParam() {
return _intParam;
}
@property TreeItem intParam(int value) {
_intParam = value;
return this;
}
@property Object objectParam() {
return _objectParam;
}
@property TreeItem objectParam(Object value) {
_objectParam = value;
return this;
}
/// returns true if item has at least one child
@property bool hasChildren() { return childCount > 0; }
@ -440,7 +467,7 @@ class TreeItemWidget : HorizontalLayout {
this(TreeItem item) {
super(item.id);
styleId = "TREE_ITEM";
styleId = STYLE_TREE_ITEM;
clickable = true;
focusable = true;
@ -458,7 +485,7 @@ class TreeItemWidget : HorizontalLayout {
_tab.maxWidth = w;
if (_item.hasChildren) {
_expander = new ImageWidget("expander", _item.hasChildren && _item.expanded ? "arrow_right_down_black" : "arrow_right_hollow");
_expander.styleId = "TREE_ITEM_EXPAND_ICON";
_expander.styleId = STYLE_TREE_ITEM_EXPAND_ICON;
_expander.clickable = true;
_expander.trackHover = true;
//_expander.setState(State.Parent);
@ -484,11 +511,11 @@ class TreeItemWidget : HorizontalLayout {
};
if (_item.iconRes.length > 0) {
_icon = new ImageWidget("icon", _item.iconRes);
_icon.styleId = "TREE_ITEM_ICON";
_icon.styleId = STYLE_TREE_ITEM_ICON;
_icon.setState(State.Parent);
}
_label = new TextWidget("label", _item.text);
_label.styleId = "TREE_ITEM_LABEL";
_label.styleId = STYLE_TREE_ITEM_LABEL;
_label.setState(State.Parent);
// append children
addChild(_tab);
@ -643,7 +670,7 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener, OnTreeStateCh
super.layout(rc);
}
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) {
if (visibility == Visibility.Gone) {
return;

View File

@ -38,6 +38,7 @@ module dlangui.widgets.widget;
public import dlangui.core.types;
public import dlangui.core.events;
public import dlangui.core.i18n;
public import dlangui.core.collections;
public import dlangui.widgets.styles;
public import dlangui.graphics.drawbuf;
@ -315,8 +316,9 @@ class Widget {
requestLayout();
return this;
}
immutable static int FOCUS_RECT_PADDING = 2;
/// get padding (between background bounds and content of widget)
@property Rect padding() const {
@property Rect padding() const {
// get max padding from style padding and background drawable padding
Rect p = style.padding;
DrawableRef d = backgroundDrawable;
@ -331,6 +333,10 @@ class Widget {
if (p.bottom < dp.bottom)
p.bottom = dp.bottom;
}
if ((focusable || ((state & State.Parent) && parent.focusable)) && focusRectColors) {
// add two pixels to padding when focus rect is required - one pixel for focus rect, one for additional space
p.offset(FOCUS_RECT_PADDING, FOCUS_RECT_PADDING);
}
return p;
}
/// set padding for widget - override one from style
@ -358,7 +364,12 @@ class Widget {
ownStyle.backgroundImageId = imageId;
return this;
}
/// returns colors to draw focus rectangle (one for solid, two for vertical gradient) or null if no focus rect should be drawn for style
@property const(uint[]) focusRectColors() const {
return style.focusRectColors;
}
/// background drawable
@property DrawableRef backgroundDrawable() const {
return stateStyle.backgroundDrawable;
@ -588,10 +599,10 @@ class Widget {
protected bool _focusable;
/// whether widget can be focused
@property bool focusable() { return _focusable; }
@property bool focusable() const { return _focusable; }
@property Widget focusable(bool flg) { _focusable = flg; return this; }
@property bool focused() {
@property bool focused() const {
return (window !is null && window.focusedWidget is this && (state & State.Focused));
}
@ -694,7 +705,7 @@ class Widget {
applyPadding(rc);
if (!rc.intersects(clipRect))
return; // out of clip rectangle
if (focusable) {
if (canFocus) {
TabOrderInfo item = new TabOrderInfo(this, rc);
results ~= item;
return;
@ -839,9 +850,9 @@ class Widget {
return parent.visible;
}
/// returns true if widget is focusable and visible
/// returns true if widget is focusable and visible and enabled
@property bool canFocus() {
return focusable && visible;
return focusable && visible && enabled;
}
/// sets focus to this widget or suitable focusable child, returns previously focused widget
@ -1056,19 +1067,31 @@ class Widget {
// summarize margins, padding, and content size
int dx = m.left + m.right + p.left + p.right + contentWidth;
int dy = m.top + m.bottom + p.top + p.bottom + contentHeight;
// check for fixed size set in layoutWidth, layoutHeight
int lh = layoutHeight;
int lw = layoutWidth;
if (!isSpecialSize(lh))
dy = lh;
if (!isSpecialSize(lw))
dx = lw;
// apply min/max width and height constraints
int minw = minWidth;
int maxw = maxWidth;
int minh = minHeight;
int maxh = maxHeight;
if (dx < minw)
if (minw != SIZE_UNSPECIFIED && dx < minw)
dx = minw;
if (dy < minh)
if (minh != SIZE_UNSPECIFIED && dy < minh)
dy = minh;
if (maxw != SIZE_UNSPECIFIED && dx > maxw)
dx = maxw;
if (maxh != SIZE_UNSPECIFIED && dy > maxh)
dy = maxh;
// apply FILL_PARENT
//if (parentWidth != SIZE_UNSPECIFIED && layoutWidth == FILL_PARENT)
// dx = parentWidth;
//if (parentHeight != SIZE_UNSPECIFIED && layoutHeight == FILL_PARENT)
// dy = parentHeight;
// apply max parent size constraint
if (parentWidth != SIZE_UNSPECIFIED && dx > parentWidth)
dx = parentWidth;
@ -1095,6 +1118,14 @@ class Widget {
_needLayout = false;
}
/// draws focus rectangle, if enabled in styles
void drawFocusRect(DrawBuf buf, Rect rc) {
const uint[] colors = focusRectColors;
if (colors) {
buf.drawFocusRect(rc, colors);
}
}
/// Draw widget at its position to buffer
void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
@ -1107,6 +1138,10 @@ class Widget {
bg.drawTo(buf, rc, state);
}
applyPadding(rc);
if (state & State.Focused) {
rc.expand(FOCUS_RECT_PADDING, FOCUS_RECT_PADDING);
drawFocusRect(buf, rc);
}
_needDraw = false;
}
@ -1229,15 +1264,15 @@ class Widget {
}
/// returns parent widget, null for top level widget
@property Widget parent() { return _parent; }
@property Widget parent() const { return cast(Widget)_parent; }
/// sets parent for widget
@property Widget parent(Widget parent) { _parent = parent; return this; }
/// returns window (if widget or its parent is attached to window)
@property Window window() {
Widget p = this;
@property Window window() const {
Widget p = cast(Widget)this;
while (p !is null) {
if (p._window !is null)
return p._window;
return cast(Window)p._window;
p = p.parent;
}
return null;
@ -1251,80 +1286,6 @@ class Widget {
}
/** object list holder, owning its objects - on destroy of holder, all own objects will be destroyed */
struct ObjectList(T) {
protected T[] _list;
protected int _count;
/** returns count of items */
@property int count() const { return _count; }
/** get item by index */
T get(int index) {
assert(index >= 0 && index < _count, "child index out of range");
return _list[index];
}
/** add item to list */
T add(T item) {
if (_list.length <= _count) // resize
_list.length = _list.length < 4 ? 4 : _list.length * 2;
_list[_count++] = item;
return item;
}
/** add item to list */
T insert(T item, int index = -1) {
if (index > _count || index < 0)
index = _count;
if (_list.length <= _count) // resize
_list.length = _list.length < 4 ? 4 : _list.length * 2;
for (int i = _count; i > index; i--)
_list[i] = _list[i - 1];
_list[index] = item;
_count++;
return item;
}
/** find child index for item, return -1 if not found */
int indexOf(T item) {
for (int i = 0; i < _count; i++)
if (_list[i] == item)
return i;
return -1;
}
/** find child index for item by id, return -1 if not found */
static if (__traits(hasMember, T, "compareId")) {
int indexOf(string id) {
for (int i = 0; i < _count; i++)
if (_list[i].compareId(id))
return i;
return -1;
}
}
/** remove item from list, return removed item */
T remove(int index) {
assert(index >= 0 && index < _count, "child index out of range");
T item = _list[index];
for (int i = index; i < _count - 1; i++)
_list[i] = _list[i + 1];
_count--;
return item;
}
/** Replace item with another value, destroy old value. */
void replace(T item, int index) {
T old = _list[index];
_list[index] = item;
destroy(old);
}
/** remove and destroy all items */
void clear() {
for (int i = 0; i < _count; i++) {
destroy(_list[i]);
_list[i] = null;
}
_count = 0;
}
~this() {
clear();
}
}
/** Widget list holder. */
alias WidgetList = ObjectList!Widget;
@ -1375,6 +1336,34 @@ class WidgetGroup : Widget {
}
/** WidgetGroup with default drawing of children (just draw all children) */
class WidgetGroupDefaultDrawing : WidgetGroup {
/// empty parameter list constructor - for usage by factory
this() {
this(null);
}
/// create with ID parameter
this(string ID) {
super(ID);
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
return;
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
auto saver = ClipRectSaver(buf, rc);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility != Visibility.Visible)
continue;
item.onDraw(buf);
}
}
}
immutable long ONE_SECOND = 10000000L;
/// Helper to handle animation progress