diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c5f5a33d..b9fd33416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,6 @@ include(${SCRIPTS_CMAKE_DIR}/googletest.cmake) include(${SCRIPTS_CMAKE_DIR}/manifests.cmake) include(${SCRIPTS_CMAKE_DIR}/docker.cmake) include(${SCRIPTS_CMAKE_DIR}/lua.cmake) -include(${SCRIPTS_CMAKE_DIR}/ui.cmake) include(${SCRIPTS_CMAKE_DIR}/macros.cmake) include(${SCRIPTS_CMAKE_DIR}/pack.cmake) include(CPack) @@ -58,7 +57,6 @@ option(VOXEDIT "Builds voxedit" ON) option(THUMBNAILER "Builds thumbnailer" ON) option(VOXCONVERT "Builds voxconvert" ON) option(MAPVIEW "Builds mapview" ON) -option(NOISETOOL "Builds noisetool" ON) option(USE_CCACHE "Use ccache" ON) option(USE_GPROF "Use gprof - will become slow" OFF) option(USE_GCOV "Use gcov - will become slow" OFF) diff --git a/Makefile b/Makefile index 1f346ba7e..770f85006 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,6 @@ update-stb: cp $(UPDATEDIR)/stb.sync/stb_image.h src/modules/image/stb_image.h cp $(UPDATEDIR)/stb.sync/stb_image_write.h src/modules/image/stb_image_write.h cp $(UPDATEDIR)/stb.sync/stb_truetype.h src/modules/voxelfont/stb_truetype.h - cp $(UPDATEDIR)/stb.sync/stb_truetype.h src/modules/ui/turbobadger/tb/thirdparty update-googletest: $(call UPDATE_GIT,googletest,https://github.com/google/googletest.git) diff --git a/cmake/ui.cmake b/cmake/ui.cmake deleted file mode 100644 index 4c1c1dcb5..000000000 --- a/cmake/ui.cmake +++ /dev/null @@ -1,17 +0,0 @@ -macro(check_ui_turbobadger TARGET) - if (NOT CMAKE_CROSS_COMPILING) - set(_files ${ARGN}) - set(_workingdir "${DATA_DIR}/${TARGET}") - foreach(_file ${_files}) - get_filename_component(_filename ${_file} NAME) - add_custom_target( - ${TARGET}-${_filename} - COMMAND uitool ../${_file} - DEPENDS uitool ${DATA_DIR}/${_file} - WORKING_DIRECTORY ${_workingdir} - SOURCES ${DATA_DIR}/${_file} - ) - add_dependencies(${TARGET} ${TARGET}-${_filename}) - endforeach() - endif() -endmacro() diff --git a/data/noisetool/ui/lang/en.tb.txt b/data/noisetool/ui/lang/en.tb.txt deleted file mode 100644 index 82b27ff6f..000000000 --- a/data/noisetool/ui/lang/en.tb.txt +++ /dev/null @@ -1,10 +0,0 @@ -cut Cut -copy Copy -paste Paste -delete Delete -selectall Select all -TBMessageWindow.ok Ok -TBMessageWindow.cancel Cancel -TBMessageWindow.yes Yes -TBMessageWindow.no No -TBList.header Showing %d of %d diff --git a/data/noisetool/ui/skin/noisetool-skin.tb.txt b/data/noisetool/ui/skin/noisetool-skin.tb.txt deleted file mode 100644 index cbbb8724d..000000000 --- a/data/noisetool/ui/skin/noisetool-skin.tb.txt +++ /dev/null @@ -1,13 +0,0 @@ -elements - node-mover - text-color #b8b8b8 - node-header - bitmap window_mover_bg_tile.png - type Tile - expand -1 - padding 3 16 - node - bitmap window.png - cut 16 - expand 12 - padding 2 diff --git a/data/noisetool/ui/widget/noisetool-noisedata-item.tb.txt b/data/noisetool/ui/widget/noisetool-noisedata-item.tb.txt deleted file mode 100644 index 60259f537..000000000 --- a/data/noisetool/ui/widget/noisetool-noisedata-item.tb.txt +++ /dev/null @@ -1,16 +0,0 @@ -TBLayout: axis: y, position: left, spacing: 3, position: left top - TBLayout: axis: x, spacing: 10, position: left, distribution: gravity, ignore-input: 1 - TBTextField: id: name - font: size: 28px - TBTextField: id: frequency - TBTextField: id: octaves - TBTextField: id: gain - TBTextField: id: lacunarity - TBTextField: id: millis - TBWidget: gravity: left right - ImageWidget: id: graph, ignore-input: 1 - TBSection: value: 0, text: "2D noise" - TBLayout: position: left top - ImageWidget: id: noise - -TBButton: id: delete, skin: TBWindow.close diff --git a/data/noisetool/ui/widget/noisetool-noisedata-list.tb.txt b/data/noisetool/ui/widget/noisetool-noisedata-list.tb.txt deleted file mode 100644 index be87022ec..000000000 --- a/data/noisetool/ui/widget/noisetool-noisedata-list.tb.txt +++ /dev/null @@ -1,10 +0,0 @@ -TBLayout - distribution gravity - TBEditField - id filter - gravity left right - placeholder Search - type search -TBSelectList - id list - gravity all diff --git a/data/noisetool/ui/widget/noisetool-noisedata-node.tb.txt b/data/noisetool/ui/widget/noisetool-noisedata-node.tb.txt deleted file mode 100644 index fbce72195..000000000 --- a/data/noisetool/ui/widget/noisetool-noisedata-node.tb.txt +++ /dev/null @@ -1,12 +0,0 @@ -TBMover: axis: y, position: left, spacing: 3, position: left top, gravity: all, skin: node - TBLayout: axis: y, spacing: 2, position: left, distribution: gravity, ignore-input: 1, gravity: all - TBTextField: id: name, skin: node-header - TBLayout: axis: y - TBTextField: id: frequency - font: size: 8px - TBTextField: id: octaves - font: size: 8px - TBTextField: id: gain - font: size: 8px - TBTextField: id: lacunarity - font: size: 8px diff --git a/data/noisetool/ui/window/noisetool-main.tb.txt b/data/noisetool/ui/window/noisetool-main.tb.txt deleted file mode 100644 index e3e9dad85..000000000 --- a/data/noisetool/ui/window/noisetool-main.tb.txt +++ /dev/null @@ -1,77 +0,0 @@ -definitions - layoutparamsx - axis x - distribution gravity - position left - layoutparamsy - axis y - distribution available - gravity all - position left - -WindowInfo - title Noise generator - fullscreen 1 - -definitions - menubutton - lp: height: 28 - skin TBButton.flat - -TBLayout: distribution: gravity, axis: y - TBContainer: gravity: all, id: maincontainer - TBLayout: distribution: gravity, axis: y - TBLayout: distribution: gravity - TBContainer: skin: container, gravity: left right - TBLayout: distribution: gravity - TBButton: gravity: left, @include: definitions>menubutton, text: Generate, id: ok, autofocus: 1 - TBSkinImage: skin: noisetool-generate - TBSelectDropdown - lp: width: 280 - gravity left right - id type - TBButton: gravity: left, @include: definitions>menubutton, text: All, id: all - TBSkinImage: skin: noisetool-generate - TBButton: gravity: left, @include: definitions>menubutton, text: Nodes, id: nodes - TBSkinImage: skin: noisetool-nodes - TBLayout: gravity: left right - TBWidget - - TBLayout: distribution: gravity, position: top - TBLayout: distribution: gravity, axis: y, position: left, gravity: top bottom, skin: container - TBLayout: position: left top, axis: y - TBClickLabel: text: Frequency - TBEditField: id: frequency, type: number, text: 0.01 - TBClickLabel: text: Offset - TBEditField: id: offset, type: number, text: 1.0 - TBClickLabel: text: Octaves - TBEditField: id: octaves, type: number, text: 4 - TBClickLabel: text: Gain - TBEditField: id: gain, type: number, text: 0.5 - TBClickLabel: text: Lacunarity - TBEditField: id: lacunarity, type: number, text: 2.0 - TBSection: value: 1, text: "Poisson Disk Distribution", is-group-root: 1 - TBLayout: position: left top, axis: y - TBClickLabel: text: Separation - TBEditField: id: separation, type: number, text: 5.0 - TBSection: value: 1, text: "Ridged noise", is-group-root: 1 - TBLayout: position: left top, axis: y - TBClickLabel: text: Ridged Offset - TBEditField: id: ridgedoffset, type: number, text: 1.0 - TBSection: value: 1, text: "Voronoi", is-group-root: 1 - TBLayout: position: left top, axis: y - TBClickLabel: text: Distance - TBCheckBox: id: enabledistance - TBWidget: gravity: top bottom - - TBLayout: distribution: gravity, axis: y - NoiseDataList - - TBLayout: distribution: gravity - TBContainer: skin: container, gravity: left right - TBLayout: distribution: gravity - TBLayout: gravity: left right - TBWidget - TBButton: @include: definitions>menubutton, text: Quit, id: quit - TBSkinImage: skin: voxedit-quit - diff --git a/data/noisetool/ui/window/noisetool-nodes.tb.txt b/data/noisetool/ui/window/noisetool-nodes.tb.txt deleted file mode 100644 index b229ae7b8..000000000 --- a/data/noisetool/ui/window/noisetool-nodes.tb.txt +++ /dev/null @@ -1,9 +0,0 @@ -WindowInfo - title Noise data - position 770 50 - size 600 600 -TBLayout - axis y - distribution gravity - gravity all - id nodes diff --git a/data/shared/ui/font/DejaVuSansMono.ttf b/data/shared/ui/font/DejaVuSansMono.ttf deleted file mode 100644 index f5786022f..000000000 Binary files a/data/shared/ui/font/DejaVuSansMono.ttf and /dev/null differ diff --git a/data/shared/ui/font/font.tb.txt b/data/shared/ui/font/font.tb.txt deleted file mode 100644 index b15c81f81..000000000 --- a/data/shared/ui/font/font.tb.txt +++ /dev/null @@ -1,15 +0,0 @@ -info - glyph_str !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ - name Segoe -size 14 - bitmap font_14.png - ascent 15 - descent 5 - advance_delta -3 - space_advance 5 -size 28 - bitmap font_28.png - ascent 31 - descent 10 - advance_delta -7 - space_advance 10 diff --git a/data/shared/ui/font/font_14.png b/data/shared/ui/font/font_14.png deleted file mode 100644 index 95f90947d..000000000 Binary files a/data/shared/ui/font/font_14.png and /dev/null differ diff --git a/data/shared/ui/font/font_28.png b/data/shared/ui/font/font_28.png deleted file mode 100644 index ff7c56c8e..000000000 Binary files a/data/shared/ui/font/font_28.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_down.png b/data/shared/ui/skin/arrow_down.png deleted file mode 100644 index 589c9fc10..000000000 Binary files a/data/shared/ui/skin/arrow_down.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_down@288.png b/data/shared/ui/skin/arrow_down@288.png deleted file mode 100644 index 10548c859..000000000 Binary files a/data/shared/ui/skin/arrow_down@288.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_left.png b/data/shared/ui/skin/arrow_left.png deleted file mode 100644 index 7f5447b68..000000000 Binary files a/data/shared/ui/skin/arrow_left.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_left@288.png b/data/shared/ui/skin/arrow_left@288.png deleted file mode 100644 index 4d62c84fc..000000000 Binary files a/data/shared/ui/skin/arrow_left@288.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_right.png b/data/shared/ui/skin/arrow_right.png deleted file mode 100644 index fce7cf397..000000000 Binary files a/data/shared/ui/skin/arrow_right.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_right@288.png b/data/shared/ui/skin/arrow_right@288.png deleted file mode 100644 index 3bbf7a065..000000000 Binary files a/data/shared/ui/skin/arrow_right@288.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_up.png b/data/shared/ui/skin/arrow_up.png deleted file mode 100644 index a07789fc7..000000000 Binary files a/data/shared/ui/skin/arrow_up.png and /dev/null differ diff --git a/data/shared/ui/skin/arrow_up@288.png b/data/shared/ui/skin/arrow_up@288.png deleted file mode 100644 index c1bad6fca..000000000 Binary files a/data/shared/ui/skin/arrow_up@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button.png b/data/shared/ui/skin/button.png deleted file mode 100644 index 6516534fc..000000000 Binary files a/data/shared/ui/skin/button.png and /dev/null differ diff --git a/data/shared/ui/skin/button@288.png b/data/shared/ui/skin/button@288.png deleted file mode 100644 index c9f4ac6b1..000000000 Binary files a/data/shared/ui/skin/button@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_flat_outline.png b/data/shared/ui/skin/button_flat_outline.png deleted file mode 100644 index ba3e2b704..000000000 Binary files a/data/shared/ui/skin/button_flat_outline.png and /dev/null differ diff --git a/data/shared/ui/skin/button_flat_outline@288.png b/data/shared/ui/skin/button_flat_outline@288.png deleted file mode 100644 index 9cee66e41..000000000 Binary files a/data/shared/ui/skin/button_flat_outline@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_flat_pressed.png b/data/shared/ui/skin/button_flat_pressed.png deleted file mode 100644 index ee7e65677..000000000 Binary files a/data/shared/ui/skin/button_flat_pressed.png and /dev/null differ diff --git a/data/shared/ui/skin/button_flat_pressed@288.png b/data/shared/ui/skin/button_flat_pressed@288.png deleted file mode 100644 index a61982fde..000000000 Binary files a/data/shared/ui/skin/button_flat_pressed@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_first_down.png b/data/shared/ui/skin/button_grouped_x_first_down.png deleted file mode 100644 index ace624e01..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_first_down.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_first_down@288.png b/data/shared/ui/skin/button_grouped_x_first_down@288.png deleted file mode 100644 index e2412ab23..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_first_down@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_first_up.png b/data/shared/ui/skin/button_grouped_x_first_up.png deleted file mode 100644 index 525841117..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_first_up.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_first_up@288.png b/data/shared/ui/skin/button_grouped_x_first_up@288.png deleted file mode 100644 index 20e0592f9..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_first_up@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_last_down.png b/data/shared/ui/skin/button_grouped_x_last_down.png deleted file mode 100644 index b12dc5b23..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_last_down.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_last_down@288.png b/data/shared/ui/skin/button_grouped_x_last_down@288.png deleted file mode 100644 index 82bea402f..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_last_down@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_last_up.png b/data/shared/ui/skin/button_grouped_x_last_up.png deleted file mode 100644 index f9ea08f34..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_last_up.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_last_up@288.png b/data/shared/ui/skin/button_grouped_x_last_up@288.png deleted file mode 100644 index 1ed599527..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_last_up@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_middle_down.png b/data/shared/ui/skin/button_grouped_x_middle_down.png deleted file mode 100644 index 922d1dd30..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_middle_down.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_middle_down@288.png b/data/shared/ui/skin/button_grouped_x_middle_down@288.png deleted file mode 100644 index 012c25189..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_middle_down@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_middle_up.png b/data/shared/ui/skin/button_grouped_x_middle_up.png deleted file mode 100644 index acac147d7..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_middle_up.png and /dev/null differ diff --git a/data/shared/ui/skin/button_grouped_x_middle_up@288.png b/data/shared/ui/skin/button_grouped_x_middle_up@288.png deleted file mode 100644 index a7dd3e9dd..000000000 Binary files a/data/shared/ui/skin/button_grouped_x_middle_up@288.png and /dev/null differ diff --git a/data/shared/ui/skin/button_pressed.png b/data/shared/ui/skin/button_pressed.png deleted file mode 100644 index 56cecfb3e..000000000 Binary files a/data/shared/ui/skin/button_pressed.png and /dev/null differ diff --git a/data/shared/ui/skin/button_pressed@288.png b/data/shared/ui/skin/button_pressed@288.png deleted file mode 100644 index c3e861956..000000000 Binary files a/data/shared/ui/skin/button_pressed@288.png and /dev/null differ diff --git a/data/shared/ui/skin/checkbox.png b/data/shared/ui/skin/checkbox.png deleted file mode 100644 index 532d647c8..000000000 Binary files a/data/shared/ui/skin/checkbox.png and /dev/null differ diff --git a/data/shared/ui/skin/checkbox@288.png b/data/shared/ui/skin/checkbox@288.png deleted file mode 100644 index 769ae9478..000000000 Binary files a/data/shared/ui/skin/checkbox@288.png and /dev/null differ diff --git a/data/shared/ui/skin/checkbox_mark.png b/data/shared/ui/skin/checkbox_mark.png deleted file mode 100644 index ed217d582..000000000 Binary files a/data/shared/ui/skin/checkbox_mark.png and /dev/null differ diff --git a/data/shared/ui/skin/checkbox_mark@288.png b/data/shared/ui/skin/checkbox_mark@288.png deleted file mode 100644 index 8b2bcb1f5..000000000 Binary files a/data/shared/ui/skin/checkbox_mark@288.png and /dev/null differ diff --git a/data/shared/ui/skin/checkbox_pressed.png b/data/shared/ui/skin/checkbox_pressed.png deleted file mode 100644 index a490b9004..000000000 Binary files a/data/shared/ui/skin/checkbox_pressed.png and /dev/null differ diff --git a/data/shared/ui/skin/checkbox_pressed@288.png b/data/shared/ui/skin/checkbox_pressed@288.png deleted file mode 100644 index e86430329..000000000 Binary files a/data/shared/ui/skin/checkbox_pressed@288.png and /dev/null differ diff --git a/data/shared/ui/skin/container.png b/data/shared/ui/skin/container.png deleted file mode 100644 index b37f936b6..000000000 Binary files a/data/shared/ui/skin/container.png and /dev/null differ diff --git a/data/shared/ui/skin/container@288.png b/data/shared/ui/skin/container@288.png deleted file mode 100644 index fc690485f..000000000 Binary files a/data/shared/ui/skin/container@288.png and /dev/null differ diff --git a/data/shared/ui/skin/editfield.png b/data/shared/ui/skin/editfield.png deleted file mode 100644 index 09e6be55a..000000000 Binary files a/data/shared/ui/skin/editfield.png and /dev/null differ diff --git a/data/shared/ui/skin/editfield@288.png b/data/shared/ui/skin/editfield@288.png deleted file mode 100644 index e3c80dc82..000000000 Binary files a/data/shared/ui/skin/editfield@288.png and /dev/null differ diff --git a/data/shared/ui/skin/fadeout_x.png b/data/shared/ui/skin/fadeout_x.png deleted file mode 100644 index 830c6db0b..000000000 Binary files a/data/shared/ui/skin/fadeout_x.png and /dev/null differ diff --git a/data/shared/ui/skin/fadeout_x@288.png b/data/shared/ui/skin/fadeout_x@288.png deleted file mode 100644 index d9e906e9b..000000000 Binary files a/data/shared/ui/skin/fadeout_x@288.png and /dev/null differ diff --git a/data/shared/ui/skin/fadeout_y.png b/data/shared/ui/skin/fadeout_y.png deleted file mode 100644 index 085b6a2a8..000000000 Binary files a/data/shared/ui/skin/fadeout_y.png and /dev/null differ diff --git a/data/shared/ui/skin/fadeout_y@288.png b/data/shared/ui/skin/fadeout_y@288.png deleted file mode 100644 index c68d17c53..000000000 Binary files a/data/shared/ui/skin/fadeout_y@288.png and /dev/null differ diff --git a/data/shared/ui/skin/filedialog-dir.png b/data/shared/ui/skin/filedialog-dir.png deleted file mode 100644 index f29e19d7f..000000000 Binary files a/data/shared/ui/skin/filedialog-dir.png and /dev/null differ diff --git a/data/shared/ui/skin/filedialog-dir@288.png b/data/shared/ui/skin/filedialog-dir@288.png deleted file mode 100644 index 3a1d3bf94..000000000 Binary files a/data/shared/ui/skin/filedialog-dir@288.png and /dev/null differ diff --git a/data/shared/ui/skin/filedialog-file.png b/data/shared/ui/skin/filedialog-file.png deleted file mode 100644 index 2ca2630cb..000000000 Binary files a/data/shared/ui/skin/filedialog-file.png and /dev/null differ diff --git a/data/shared/ui/skin/filedialog-file@288.png b/data/shared/ui/skin/filedialog-file@288.png deleted file mode 100644 index 986257252..000000000 Binary files a/data/shared/ui/skin/filedialog-file@288.png and /dev/null differ diff --git a/data/shared/ui/skin/focus_r4.png b/data/shared/ui/skin/focus_r4.png deleted file mode 100644 index 82c1e63e1..000000000 Binary files a/data/shared/ui/skin/focus_r4.png and /dev/null differ diff --git a/data/shared/ui/skin/focus_r4@288.png b/data/shared/ui/skin/focus_r4@288.png deleted file mode 100644 index 4e765b1b1..000000000 Binary files a/data/shared/ui/skin/focus_r4@288.png and /dev/null differ diff --git a/data/shared/ui/skin/focus_tabbutton_left.png b/data/shared/ui/skin/focus_tabbutton_left.png deleted file mode 100644 index 1b445fec6..000000000 Binary files a/data/shared/ui/skin/focus_tabbutton_left.png and /dev/null differ diff --git a/data/shared/ui/skin/focus_tabbutton_left@288.png b/data/shared/ui/skin/focus_tabbutton_left@288.png deleted file mode 100644 index 6793df096..000000000 Binary files a/data/shared/ui/skin/focus_tabbutton_left@288.png and /dev/null differ diff --git a/data/shared/ui/skin/focus_tabbutton_top.png b/data/shared/ui/skin/focus_tabbutton_top.png deleted file mode 100644 index db015e236..000000000 Binary files a/data/shared/ui/skin/focus_tabbutton_top.png and /dev/null differ diff --git a/data/shared/ui/skin/focus_tabbutton_top@288.png b/data/shared/ui/skin/focus_tabbutton_top@288.png deleted file mode 100644 index ea2bf41c8..000000000 Binary files a/data/shared/ui/skin/focus_tabbutton_top@288.png and /dev/null differ diff --git a/data/shared/ui/skin/item_hover.png b/data/shared/ui/skin/item_hover.png deleted file mode 100644 index 90a6e723c..000000000 Binary files a/data/shared/ui/skin/item_hover.png and /dev/null differ diff --git a/data/shared/ui/skin/item_hover@288.png b/data/shared/ui/skin/item_hover@288.png deleted file mode 100644 index b11195c06..000000000 Binary files a/data/shared/ui/skin/item_hover@288.png and /dev/null differ diff --git a/data/shared/ui/skin/item_selected.png b/data/shared/ui/skin/item_selected.png deleted file mode 100644 index cf5dd472d..000000000 Binary files a/data/shared/ui/skin/item_selected.png and /dev/null differ diff --git a/data/shared/ui/skin/item_selected@288.png b/data/shared/ui/skin/item_selected@288.png deleted file mode 100644 index 5a3cc031a..000000000 Binary files a/data/shared/ui/skin/item_selected@288.png and /dev/null differ diff --git a/data/shared/ui/skin/item_separator_x.png b/data/shared/ui/skin/item_separator_x.png deleted file mode 100644 index 19073e907..000000000 Binary files a/data/shared/ui/skin/item_separator_x.png and /dev/null differ diff --git a/data/shared/ui/skin/item_separator_x@288.png b/data/shared/ui/skin/item_separator_x@288.png deleted file mode 100644 index b2c6b4bee..000000000 Binary files a/data/shared/ui/skin/item_separator_x@288.png and /dev/null differ diff --git a/data/shared/ui/skin/item_separator_y.png b/data/shared/ui/skin/item_separator_y.png deleted file mode 100644 index baf7f51bf..000000000 Binary files a/data/shared/ui/skin/item_separator_y.png and /dev/null differ diff --git a/data/shared/ui/skin/item_separator_y@288.png b/data/shared/ui/skin/item_separator_y@288.png deleted file mode 100644 index 7b5eb7ccd..000000000 Binary files a/data/shared/ui/skin/item_separator_y@288.png and /dev/null differ diff --git a/data/shared/ui/skin/progress_spinner_strip.png b/data/shared/ui/skin/progress_spinner_strip.png deleted file mode 100644 index 212a10629..000000000 Binary files a/data/shared/ui/skin/progress_spinner_strip.png and /dev/null differ diff --git a/data/shared/ui/skin/progress_spinner_strip@288.png b/data/shared/ui/skin/progress_spinner_strip@288.png deleted file mode 100644 index 180eac8c5..000000000 Binary files a/data/shared/ui/skin/progress_spinner_strip@288.png and /dev/null differ diff --git a/data/shared/ui/skin/radio.png b/data/shared/ui/skin/radio.png deleted file mode 100644 index bfff79668..000000000 Binary files a/data/shared/ui/skin/radio.png and /dev/null differ diff --git a/data/shared/ui/skin/radio@288.png b/data/shared/ui/skin/radio@288.png deleted file mode 100644 index accbc38d3..000000000 Binary files a/data/shared/ui/skin/radio@288.png and /dev/null differ diff --git a/data/shared/ui/skin/radio_mark.png b/data/shared/ui/skin/radio_mark.png deleted file mode 100644 index 570f48ad8..000000000 Binary files a/data/shared/ui/skin/radio_mark.png and /dev/null differ diff --git a/data/shared/ui/skin/radio_mark@288.png b/data/shared/ui/skin/radio_mark@288.png deleted file mode 100644 index 3bb4ec0be..000000000 Binary files a/data/shared/ui/skin/radio_mark@288.png and /dev/null differ diff --git a/data/shared/ui/skin/radio_pressed.png b/data/shared/ui/skin/radio_pressed.png deleted file mode 100644 index a9836185e..000000000 Binary files a/data/shared/ui/skin/radio_pressed.png and /dev/null differ diff --git a/data/shared/ui/skin/radio_pressed@288.png b/data/shared/ui/skin/radio_pressed@288.png deleted file mode 100644 index e98c52a9a..000000000 Binary files a/data/shared/ui/skin/radio_pressed@288.png and /dev/null differ diff --git a/data/shared/ui/skin/remove.png b/data/shared/ui/skin/remove.png deleted file mode 100644 index 80412d782..000000000 Binary files a/data/shared/ui/skin/remove.png and /dev/null differ diff --git a/data/shared/ui/skin/remove@288.png b/data/shared/ui/skin/remove@288.png deleted file mode 100644 index 9b6a3304c..000000000 Binary files a/data/shared/ui/skin/remove@288.png and /dev/null differ diff --git a/data/shared/ui/skin/resizer.png b/data/shared/ui/skin/resizer.png deleted file mode 100644 index 5918a7b20..000000000 Binary files a/data/shared/ui/skin/resizer.png and /dev/null differ diff --git a/data/shared/ui/skin/resizer@288.png b/data/shared/ui/skin/resizer@288.png deleted file mode 100644 index f6654b034..000000000 Binary files a/data/shared/ui/skin/resizer@288.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_bg_x.png b/data/shared/ui/skin/scroll_bg_x.png deleted file mode 100644 index fc2eb5df9..000000000 Binary files a/data/shared/ui/skin/scroll_bg_x.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_bg_x@288.png b/data/shared/ui/skin/scroll_bg_x@288.png deleted file mode 100644 index e6abf5e56..000000000 Binary files a/data/shared/ui/skin/scroll_bg_x@288.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_bg_y.png b/data/shared/ui/skin/scroll_bg_y.png deleted file mode 100644 index 2775e3b43..000000000 Binary files a/data/shared/ui/skin/scroll_bg_y.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_bg_y@288.png b/data/shared/ui/skin/scroll_bg_y@288.png deleted file mode 100644 index ecb61d10b..000000000 Binary files a/data/shared/ui/skin/scroll_bg_y@288.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_fg_x.png b/data/shared/ui/skin/scroll_fg_x.png deleted file mode 100644 index 6bc8a5a10..000000000 Binary files a/data/shared/ui/skin/scroll_fg_x.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_fg_x@288.png b/data/shared/ui/skin/scroll_fg_x@288.png deleted file mode 100644 index 0f1ad31d4..000000000 Binary files a/data/shared/ui/skin/scroll_fg_x@288.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_fg_y.png b/data/shared/ui/skin/scroll_fg_y.png deleted file mode 100644 index 861e1bdf0..000000000 Binary files a/data/shared/ui/skin/scroll_fg_y.png and /dev/null differ diff --git a/data/shared/ui/skin/scroll_fg_y@288.png b/data/shared/ui/skin/scroll_fg_y@288.png deleted file mode 100644 index c99e3a3eb..000000000 Binary files a/data/shared/ui/skin/scroll_fg_y@288.png and /dev/null differ diff --git a/data/shared/ui/skin/search.png b/data/shared/ui/skin/search.png deleted file mode 100644 index 79cd11f14..000000000 Binary files a/data/shared/ui/skin/search.png and /dev/null differ diff --git a/data/shared/ui/skin/search@288.png b/data/shared/ui/skin/search@288.png deleted file mode 100644 index 027e8918e..000000000 Binary files a/data/shared/ui/skin/search@288.png and /dev/null differ diff --git a/data/shared/ui/skin/section_container.png b/data/shared/ui/skin/section_container.png deleted file mode 100644 index 331a3f555..000000000 Binary files a/data/shared/ui/skin/section_container.png and /dev/null differ diff --git a/data/shared/ui/skin/section_container@288.png b/data/shared/ui/skin/section_container@288.png deleted file mode 100644 index a107ccb70..000000000 Binary files a/data/shared/ui/skin/section_container@288.png and /dev/null differ diff --git a/data/shared/ui/skin/selection.png b/data/shared/ui/skin/selection.png deleted file mode 100644 index 541c02a23..000000000 Binary files a/data/shared/ui/skin/selection.png and /dev/null differ diff --git a/data/shared/ui/skin/selection@288.png b/data/shared/ui/skin/selection@288.png deleted file mode 100644 index b728e06d9..000000000 Binary files a/data/shared/ui/skin/selection@288.png and /dev/null differ diff --git a/data/shared/ui/skin/skin.tb.txt b/data/shared/ui/skin/skin.tb.txt deleted file mode 100644 index c41518ada..000000000 --- a/data/shared/ui/skin/skin.tb.txt +++ /dev/null @@ -1,517 +0,0 @@ -# This is the default skin of Turbo Badger -# The license of the skin is Public Domain. -# -# For more information about Turbo Badger and its license, -# see tb_core.h. -description - name Turbo Badger Default skin - # Dimensions in dp (the default unit for padding and such) and bitmaps are specified for this DPI. - base-dpi 96 - # The skin also has graphics available in these DPI modes - supported-dpi 96 288 -defaults - spacing 0 - text-color #fefefe - placeholder - opacity 0.2 - disabled - opacity 0.3 -elements - TBButton - text-color #fefefe - bitmap button.png - cut 17 - expand 7 - padding 6 8 - overrides - element TBButtonInGroup - condition: target: parent, property: skin, value: button_group - condition: target: parent, property: axis, value: x - element TBButton.pressed - state pressed - TBButton.pressed - text-color #fefefe - bitmap button_pressed.png - cut 17 - expand 7 - content-ofs-x 1 - content-ofs-y 1 - - TBButton.flat - text-color #fefefe - padding 6 8 - children - element TBButton.flat.hovered - state hovered - element TBButton.flat.pressed - state pressed - TBButton.flat.hovered - bitmap button_flat_outline.png - cut 15 - expand 6 - TBButton.flat.pressed - bitmap button_flat_pressed.png - cut 8 - - # == TBButtonInGroup is not a widget. It's only used as override for TBButton under a "button_group" ==== - TBButtonInGroup - bitmap button_grouped_x_middle_up.png - cut 17 - expand 7 - padding 6 8 - overrides - element TBButtonInGroup.first - condition: target: prev sibling, property: skin, value: TBButton, test: != - element TBButtonInGroup.last - condition: target: next sibling, property: skin, value: TBButton, test: != - element TBButtonInGroup.pressed - state pressed - TBButtonInGroup.pressed - clone TBButtonInGroup - bitmap button_grouped_x_middle_down.png - content-ofs-x 1 - content-ofs-y 1 - overrides - TBButtonInGroup.first - clone TBButtonInGroup - bitmap button_grouped_x_first_up.png - overrides - element TBButtonInGroup.first.pressed - state pressed - TBButtonInGroup.first.pressed - clone TBButtonInGroup.pressed - bitmap button_grouped_x_first_down.png - overrides - TBButtonInGroup.last - clone TBButtonInGroup - bitmap button_grouped_x_last_up.png - overrides - element TBButtonInGroup.last.pressed - state pressed - TBButtonInGroup.last.pressed - clone TBButtonInGroup.pressed - bitmap button_grouped_x_last_down.png - overrides - - # == button_group is not a widget. It's specified on widgets that should group TBButton ======= - button_group - spacing -1px - - # == TBSection skins ========================================================================== - TBSection.layout - spacing -1px - TBSection.container - bitmap section_container.png - type StretchBorder - cut 11 - expand 6 - padding 2 - TBSectionHeader - bitmap window_mover_bg_tile.png - type Tile - expand -1 - padding 3 16 - children - element TBSectionHeader.icon_0 - condition: target: this, property: value, value: 0 - condition: target: this, property: capture, test: != - element TBSectionHeader.icon_1 - condition: target: this, property: value, value: 1 - condition: target: this, property: capture, test: != - element TBSectionHeader.icon_pressed - condition: target: this, property: capture - overlays - element TBSectionHeader.overlay - state all - TBSectionHeader.overlay - bitmap window_mover_overlay.png - type StretchBorder - cut 16 - expand 3 - TBSectionHeader.icon_0 - bitmap toggle_section_icon_up.png - type Image - img-position-x 0 - img-ofs-x 4 - TBSectionHeader.icon_1 - bitmap toggle_section_icon_down.png - type Image - img-position-x 0 - img-ofs-x 4 - TBSectionHeader.icon_pressed - bitmap toggle_section_icon_middle.png - type Image - img-position-x 0 - img-ofs-x 4 - - Header - bitmap window_mover_bg_tile.png - type Tile - expand -1 - padding 3 16 - overlays - element Header.overlay - state all - Header.overlay - bitmap window_mover_overlay.png - type StretchBorder - cut 16 - expand 3 - - # == TBTabContainer skins - Create strong overrides for the tab buttons four alignments ============ - TBTabContainer.rootlayout - spacing -1px - TBTabContainer.tablayout_x - padding 0 10 - TBTabContainer.tablayout_y - padding 10 0 - TBTabContainer.container - clone TBContainer - TBTabContainer.tab - strong-overrides - element TBTabContainer.tab_top - condition: target: ancestors, property: align, value: top - element TBTabContainer.tab_bottom - condition: target: ancestors, property: align, value: bottom - element TBTabContainer.tab_left - condition: target: ancestors, property: align, value: left - element TBTabContainer.tab_right - condition: target: ancestors, property: align, value: right - # == Top tab button ============================= - TBTabContainer.tab_top - bitmap tab_button_top_inactive.png - cut 12 - expand 6 - padding 6 6 3 6 - min-width 50 - overrides - element TBTabContainer.tab_top.pressed - state pressed - children - element tab_top_focus - state focused - tab_top_focus - type StretchBorder - bitmap focus_tabbutton_top.png - cut 12 - expand 6 - TBTabContainer.tab_top.pressed - bitmap tab_button_top_active.png - cut 13 - expand 6 - # == Bottom tab button ============================= - TBTabContainer.tab_bottom - bitmap tab_button_bottom_inactive.png - cut 12 - expand 6 - padding 3 6 6 6 - min-width 50 - overrides - element TBTabContainer.tab_bottom.pressed - state pressed - children - element tab_bottom_focus - state focused - tab_bottom_focus - clone tab_top_focus - flip-y 1 - TBTabContainer.tab_bottom.pressed - bitmap tab_button_bottom_active.png - cut 13 - expand 6 - # == Left tab button ============================= - TBTabContainer.tab_left - bitmap tab_button_left_inactive.png - cut 12 - expand 6 - padding 6 3 6 6 - min-width 50 - overrides - element TBTabContainer.tab_left.pressed - state pressed - children - element tab_left_focus - state focused - tab_left_focus - type StretchBorder - bitmap focus_tabbutton_left.png - cut 12 - expand 6 - TBTabContainer.tab_left.pressed - bitmap tab_button_left_active.png - cut 13 - expand 6 - # == Right tab button ============================= - TBTabContainer.tab_right - bitmap tab_button_right_inactive.png - cut 12 - expand 6 - padding 6 6 6 3 - min-width 50 - overrides - element TBTabContainer.tab_right.pressed - state pressed - children - element tab_right_focus - state focused - tab_right_focus - clone tab_left_focus - flip-x 1 - TBTabContainer.tab_right.pressed - bitmap tab_button_right_active.png - cut 13 - expand 6 - - # == TBEditField skin. Create strong overrides for the search type to add magnifier glass icon ===== - TBEditField.selection - cut 2 - bitmap selection.png - TBEditField - bitmap editfield.png - cut 12 - expand 4 - padding 4 - strong-overrides - element TBEditField.search - condition: target: this, property: edit-type, value: "search" - - TBEditField.search - clone TBEditField - padding 4 4 4 24 - min-width 50 - children - element TBEditField.search.icon - TBEditField.search.icon - bitmap search.png - type Image - img-position-x 0 - img-ofs-x 5 - - # == TBWindow skin ================================================================================= - TBWindow - bitmap window.png - cut 16 - expand 12 - padding 0 - overrides - element TBWindow.selected - state selected - TBWindow.selected - bitmap window_active.png - cut 16 - expand 12 - TBWindow.close - bitmap window_close.png - type Image - children - element TBWindow.close.pressed - state pressed - TBWindow.close.pressed - bitmap window_close_pressed.png - type Image - TBWindow.mover - text-color #b8b8b8 - bitmap window_mover_bg_tile.png - type Tile - expand -1 - padding 4 - overrides - element TBWindow.mover.active - state selected - children - element TBWindow.mover.overlay - state all - TBWindow.mover.overlay - bitmap window_mover_overlay.png - cut 12 - expand 3 - TBWindow.mover.active - clone TBWindow.mover - text-color #fefefe - overrides - overlays - children - - # == Misc skins ==================================================================================== - TBContainer - bitmap container.png - type StretchBorder - cut 12 - expand 6 - padding 0 - TBCheckBox - bitmap checkbox.png - cut 19 - expand 7 - children - element TBCheckBox.selected - state selected - element TBCheckBox.pressed - state pressed - TBCheckBox.selected - bitmap checkbox_mark.png - type Image - expand 7 - TBCheckBox.pressed - bitmap checkbox_pressed.png - type Image - TBRadioButton - bitmap radio.png - cut 19 - expand 7 - children - element TBRadioButton.selected - state selected - element TBRadioButton.pressed - state pressed - TBRadioButton.selected - bitmap radio_mark.png - type Image - expand 7 - TBRadioButton.pressed - bitmap radio_pressed.png - type Image - expand 7 - TBLayout.fadeout_x - bitmap fadeout_x.png - TBLayout.fadeout_y - bitmap fadeout_y.png - TBSelectList - clone TBEditField - padding 2 - TBSelectDropdown - clone TBButton - TBSelectDropdown.arrow - clone arrow.down - TBSelectDropdown.window - clone TBPopupWindow - TBPopupWindow - clone TBWindow - padding 2 - TBMenuWindow - clone TBPopupWindow - TBSelectItem - padding 4 10 - children - element TBSelectItem.hovered - state hovered - element TBSelectItem.selected - state selected - TBSelectItem.selected - cut 7 - bitmap item_selected.png - TBSelectItem.hovered - cut 7 - bitmap item_hover.png - TBSelectItem.separator - clone TBSeparator - - TBSeparator - bitmap item_separator_x.png - type Stretch Image - overrides - element TBSeparator.y - condition: target: parent, property: axis, value: x - TBSeparator.y - bitmap item_separator_y.png - type Stretch Image - - TBScrollBarBgX - bitmap scroll_bg_x.png - cut 11 - expand 5 - TBScrollBarFgX - bitmap scroll_fg_x.png - cut 11 - expand 5 - TBScrollBarBgY - bitmap scroll_bg_y.png - cut 11 - expand 5 - TBScrollBarFgY - bitmap scroll_fg_y.png - cut 11 - expand 5 - - TBSliderBgX - bitmap slider_bg_x.png - cut 9 - min-height 19 - max-height 19 - min-width 50 - TBSliderFgX - bitmap slider_handle.png - expand 5 - - TBSliderBgY - bitmap slider_bg_y.png - cut 9 - min-width 19 - max-width 19 - min-height 50 - TBSliderFgY - clone TBSliderFgX - - TBResizer - bitmap resizer.png - TBInlineSelect - max-width 110 - TBDimmer - background-color #00000088 - - TBProgressSpinner - min-width 28 - min-height 28 - TBProgressSpinner.fg - bitmap progress_spinner_strip.png - - # The TBList header is always disabled, thus gets slightly less opacity default. - TBList.header - - arrow.left: type: Image, bitmap: arrow_left.png, min-width: 5, min-height: 10 - arrow.right: type: Image, bitmap: arrow_right.png, min-width: 5, min-height: 10 - arrow.up: type: Image, bitmap: arrow_up.png, min-width: 10, min-height: 5 - arrow.down: type: Image, bitmap: arrow_down.png, min-width: 10, min-height: 5 - generic_focus - bitmap focus_r4.png - cut 11 - expand 4 - - image_caption - background-color #00000088 - - button_remove - bitmap remove.png - type Image - padding 5 - - accent_color - text-color #001111 - - no-padding-container - type StretchBox - bitmap window.png - cut 16 - expand 12 - padding 0 - - container - type StretchBox - bitmap window.png - cut 16 - expand 12 - padding 6 - - separator - bitmap item_separator_x.png - type Stretch Image - - icon-filedialog-file - bitmap filedialog-file.png - type Image - padding 5 - - icon-filedialog-dir - bitmap filedialog-dir.png - type Image - padding 5 diff --git a/data/shared/ui/skin/slider_bg_x.png b/data/shared/ui/skin/slider_bg_x.png deleted file mode 100644 index 71c31634b..000000000 Binary files a/data/shared/ui/skin/slider_bg_x.png and /dev/null differ diff --git a/data/shared/ui/skin/slider_bg_x@288.png b/data/shared/ui/skin/slider_bg_x@288.png deleted file mode 100644 index 943c440af..000000000 Binary files a/data/shared/ui/skin/slider_bg_x@288.png and /dev/null differ diff --git a/data/shared/ui/skin/slider_bg_y.png b/data/shared/ui/skin/slider_bg_y.png deleted file mode 100644 index d756e841a..000000000 Binary files a/data/shared/ui/skin/slider_bg_y.png and /dev/null differ diff --git a/data/shared/ui/skin/slider_bg_y@288.png b/data/shared/ui/skin/slider_bg_y@288.png deleted file mode 100644 index 4fab0890d..000000000 Binary files a/data/shared/ui/skin/slider_bg_y@288.png and /dev/null differ diff --git a/data/shared/ui/skin/slider_handle.png b/data/shared/ui/skin/slider_handle.png deleted file mode 100644 index 94489a013..000000000 Binary files a/data/shared/ui/skin/slider_handle.png and /dev/null differ diff --git a/data/shared/ui/skin/slider_handle@288.png b/data/shared/ui/skin/slider_handle@288.png deleted file mode 100644 index 8b28ce1b9..000000000 Binary files a/data/shared/ui/skin/slider_handle@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_bottom_active.png b/data/shared/ui/skin/tab_button_bottom_active.png deleted file mode 100644 index 77ee097db..000000000 Binary files a/data/shared/ui/skin/tab_button_bottom_active.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_bottom_active@288.png b/data/shared/ui/skin/tab_button_bottom_active@288.png deleted file mode 100644 index 0dbf119ea..000000000 Binary files a/data/shared/ui/skin/tab_button_bottom_active@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_bottom_inactive.png b/data/shared/ui/skin/tab_button_bottom_inactive.png deleted file mode 100644 index 99d3c9062..000000000 Binary files a/data/shared/ui/skin/tab_button_bottom_inactive.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_bottom_inactive@288.png b/data/shared/ui/skin/tab_button_bottom_inactive@288.png deleted file mode 100644 index ab5d043fb..000000000 Binary files a/data/shared/ui/skin/tab_button_bottom_inactive@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_left_active.png b/data/shared/ui/skin/tab_button_left_active.png deleted file mode 100644 index ed6f4620c..000000000 Binary files a/data/shared/ui/skin/tab_button_left_active.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_left_active@288.png b/data/shared/ui/skin/tab_button_left_active@288.png deleted file mode 100644 index 5310f792f..000000000 Binary files a/data/shared/ui/skin/tab_button_left_active@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_left_inactive.png b/data/shared/ui/skin/tab_button_left_inactive.png deleted file mode 100644 index d2e207909..000000000 Binary files a/data/shared/ui/skin/tab_button_left_inactive.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_left_inactive@288.png b/data/shared/ui/skin/tab_button_left_inactive@288.png deleted file mode 100644 index 94a206761..000000000 Binary files a/data/shared/ui/skin/tab_button_left_inactive@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_right_active.png b/data/shared/ui/skin/tab_button_right_active.png deleted file mode 100644 index 276d23f79..000000000 Binary files a/data/shared/ui/skin/tab_button_right_active.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_right_active@288.png b/data/shared/ui/skin/tab_button_right_active@288.png deleted file mode 100644 index 2bab17ee2..000000000 Binary files a/data/shared/ui/skin/tab_button_right_active@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_right_inactive.png b/data/shared/ui/skin/tab_button_right_inactive.png deleted file mode 100644 index 47f4def72..000000000 Binary files a/data/shared/ui/skin/tab_button_right_inactive.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_right_inactive@288.png b/data/shared/ui/skin/tab_button_right_inactive@288.png deleted file mode 100644 index 67c81150c..000000000 Binary files a/data/shared/ui/skin/tab_button_right_inactive@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_top_active.png b/data/shared/ui/skin/tab_button_top_active.png deleted file mode 100644 index 271b679f0..000000000 Binary files a/data/shared/ui/skin/tab_button_top_active.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_top_active@288.png b/data/shared/ui/skin/tab_button_top_active@288.png deleted file mode 100644 index 04de8e142..000000000 Binary files a/data/shared/ui/skin/tab_button_top_active@288.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_top_inactive.png b/data/shared/ui/skin/tab_button_top_inactive.png deleted file mode 100644 index 16023b187..000000000 Binary files a/data/shared/ui/skin/tab_button_top_inactive.png and /dev/null differ diff --git a/data/shared/ui/skin/tab_button_top_inactive@288.png b/data/shared/ui/skin/tab_button_top_inactive@288.png deleted file mode 100644 index 0c3141075..000000000 Binary files a/data/shared/ui/skin/tab_button_top_inactive@288.png and /dev/null differ diff --git a/data/shared/ui/skin/toggle_section_icon_down.png b/data/shared/ui/skin/toggle_section_icon_down.png deleted file mode 100644 index 23bb510d6..000000000 Binary files a/data/shared/ui/skin/toggle_section_icon_down.png and /dev/null differ diff --git a/data/shared/ui/skin/toggle_section_icon_down@288.png b/data/shared/ui/skin/toggle_section_icon_down@288.png deleted file mode 100644 index ea0cf4060..000000000 Binary files a/data/shared/ui/skin/toggle_section_icon_down@288.png and /dev/null differ diff --git a/data/shared/ui/skin/toggle_section_icon_middle.png b/data/shared/ui/skin/toggle_section_icon_middle.png deleted file mode 100644 index 919848274..000000000 Binary files a/data/shared/ui/skin/toggle_section_icon_middle.png and /dev/null differ diff --git a/data/shared/ui/skin/toggle_section_icon_middle@288.png b/data/shared/ui/skin/toggle_section_icon_middle@288.png deleted file mode 100644 index fcb7e700e..000000000 Binary files a/data/shared/ui/skin/toggle_section_icon_middle@288.png and /dev/null differ diff --git a/data/shared/ui/skin/toggle_section_icon_up.png b/data/shared/ui/skin/toggle_section_icon_up.png deleted file mode 100644 index e9bdf693a..000000000 Binary files a/data/shared/ui/skin/toggle_section_icon_up.png and /dev/null differ diff --git a/data/shared/ui/skin/toggle_section_icon_up@288.png b/data/shared/ui/skin/toggle_section_icon_up@288.png deleted file mode 100644 index 8db5cee2a..000000000 Binary files a/data/shared/ui/skin/toggle_section_icon_up@288.png and /dev/null differ diff --git a/data/shared/ui/skin/window.png b/data/shared/ui/skin/window.png deleted file mode 100644 index 93ffeccaf..000000000 Binary files a/data/shared/ui/skin/window.png and /dev/null differ diff --git a/data/shared/ui/skin/window@288.png b/data/shared/ui/skin/window@288.png deleted file mode 100644 index 29b793848..000000000 Binary files a/data/shared/ui/skin/window@288.png and /dev/null differ diff --git a/data/shared/ui/skin/window_active.png b/data/shared/ui/skin/window_active.png deleted file mode 100644 index 20232c3fd..000000000 Binary files a/data/shared/ui/skin/window_active.png and /dev/null differ diff --git a/data/shared/ui/skin/window_active@288.png b/data/shared/ui/skin/window_active@288.png deleted file mode 100644 index 0f18a6735..000000000 Binary files a/data/shared/ui/skin/window_active@288.png and /dev/null differ diff --git a/data/shared/ui/skin/window_close.png b/data/shared/ui/skin/window_close.png deleted file mode 100644 index 810045118..000000000 Binary files a/data/shared/ui/skin/window_close.png and /dev/null differ diff --git a/data/shared/ui/skin/window_close@288.png b/data/shared/ui/skin/window_close@288.png deleted file mode 100644 index 20e5defb8..000000000 Binary files a/data/shared/ui/skin/window_close@288.png and /dev/null differ diff --git a/data/shared/ui/skin/window_close_pressed.png b/data/shared/ui/skin/window_close_pressed.png deleted file mode 100644 index 91e2166cf..000000000 Binary files a/data/shared/ui/skin/window_close_pressed.png and /dev/null differ diff --git a/data/shared/ui/skin/window_close_pressed@288.png b/data/shared/ui/skin/window_close_pressed@288.png deleted file mode 100644 index f04cb49d7..000000000 Binary files a/data/shared/ui/skin/window_close_pressed@288.png and /dev/null differ diff --git a/data/shared/ui/skin/window_mover_bg_tile.png b/data/shared/ui/skin/window_mover_bg_tile.png deleted file mode 100644 index 5d00a2562..000000000 Binary files a/data/shared/ui/skin/window_mover_bg_tile.png and /dev/null differ diff --git a/data/shared/ui/skin/window_mover_bg_tile@288.png b/data/shared/ui/skin/window_mover_bg_tile@288.png deleted file mode 100644 index 621f2724f..000000000 Binary files a/data/shared/ui/skin/window_mover_bg_tile@288.png and /dev/null differ diff --git a/data/shared/ui/skin/window_mover_overlay.png b/data/shared/ui/skin/window_mover_overlay.png deleted file mode 100644 index 2490d5c13..000000000 Binary files a/data/shared/ui/skin/window_mover_overlay.png and /dev/null differ diff --git a/data/shared/ui/skin/window_mover_overlay@288.png b/data/shared/ui/skin/window_mover_overlay@288.png deleted file mode 100644 index 625ae8e73..000000000 Binary files a/data/shared/ui/skin/window_mover_overlay@288.png and /dev/null differ diff --git a/data/shared/ui/window/filedialog.tb.txt b/data/shared/ui/window/filedialog.tb.txt deleted file mode 100644 index d148cc285..000000000 --- a/data/shared/ui/window/filedialog.tb.txt +++ /dev/null @@ -1,25 +0,0 @@ -WindowInfo - title File dialog - size 800 500 - -TBLayout: distribution: gravity, axis: x, size: available - TBLayout: distribution: gravity, axis: y - TBSelectList - id dirs - gravity all - - TBLayout: distribution: gravity, axis: y, size: available - TBEditField - id input - autofocus 1 - - TBSeparator: gravity: left right, skin: separator - TBSelectList - id files - gravity all - - TBSeparator: gravity: left right, skin: separator - TBLayout: distribution: gravity, axis: x - TBButton: text: Ok, id: ok, autofocus: 1, gravity: left right, size: gravity - TBButton: text: Cancel, id: cancel, gravity: left right, size: gravity - TBSelectDropdown: id: filter diff --git a/data/shared/ui/window/filedialog_dir.tb.txt b/data/shared/ui/window/filedialog_dir.tb.txt deleted file mode 100644 index 1ad7730ed..000000000 --- a/data/shared/ui/window/filedialog_dir.tb.txt +++ /dev/null @@ -1,2 +0,0 @@ -TBSkinImage: id: icon, skin: icon-filedialog-dir, ignore-input: 1 -TBTextField: id: name, text-align: left, ignore-input: 1 diff --git a/data/shared/ui/window/filedialog_file.tb.txt b/data/shared/ui/window/filedialog_file.tb.txt deleted file mode 100644 index accb78912..000000000 --- a/data/shared/ui/window/filedialog_file.tb.txt +++ /dev/null @@ -1,2 +0,0 @@ -TBSkinImage: id: icon, skin: icon-filedialog-file, ignore-input: 1 -TBTextField: id: name, text-align: left, ignore-input: 1 diff --git a/data/testturbobadger/demo01/images/image_1.png b/data/testturbobadger/demo01/images/image_1.png deleted file mode 100644 index 1e9ce2efe..000000000 Binary files a/data/testturbobadger/demo01/images/image_1.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_2.png b/data/testturbobadger/demo01/images/image_2.png deleted file mode 100644 index c784af65c..000000000 Binary files a/data/testturbobadger/demo01/images/image_2.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_3.png b/data/testturbobadger/demo01/images/image_3.png deleted file mode 100644 index 95b0ed5ed..000000000 Binary files a/data/testturbobadger/demo01/images/image_3.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_4.png b/data/testturbobadger/demo01/images/image_4.png deleted file mode 100644 index fcfbb81b4..000000000 Binary files a/data/testturbobadger/demo01/images/image_4.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_5.png b/data/testturbobadger/demo01/images/image_5.png deleted file mode 100644 index 7a7a36df7..000000000 Binary files a/data/testturbobadger/demo01/images/image_5.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_6.png b/data/testturbobadger/demo01/images/image_6.png deleted file mode 100644 index e363a8dc0..000000000 Binary files a/data/testturbobadger/demo01/images/image_6.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_7.png b/data/testturbobadger/demo01/images/image_7.png deleted file mode 100644 index adbbec037..000000000 Binary files a/data/testturbobadger/demo01/images/image_7.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_8.png b/data/testturbobadger/demo01/images/image_8.png deleted file mode 100644 index 79dba923f..000000000 Binary files a/data/testturbobadger/demo01/images/image_8.png and /dev/null differ diff --git a/data/testturbobadger/demo01/images/image_9.png b/data/testturbobadger/demo01/images/image_9.png deleted file mode 100644 index ba59ceb67..000000000 Binary files a/data/testturbobadger/demo01/images/image_9.png and /dev/null differ diff --git a/data/testturbobadger/demo01/language/lng_en.tb.txt b/data/testturbobadger/demo01/language/lng_en.tb.txt deleted file mode 100644 index 046301bfd..000000000 --- a/data/testturbobadger/demo01/language/lng_en.tb.txt +++ /dev/null @@ -1,16 +0,0 @@ -cut Cut -copy Copy -paste Paste -delete Delete -selectall Select all -undo Undo -redo Redo -TBMessageWindow.ok Ok -TBMessageWindow.cancel Cancel -TBMessageWindow.yes Yes -TBMessageWindow.no No -TBList.header Showing %d of %d -new New -save Save -close Close -search Search diff --git a/data/testturbobadger/demo01/language/lng_sv.tb.txt b/data/testturbobadger/demo01/language/lng_sv.tb.txt deleted file mode 100644 index be22d2649..000000000 --- a/data/testturbobadger/demo01/language/lng_sv.tb.txt +++ /dev/null @@ -1,16 +0,0 @@ -cut Klipp ut -copy Kopiera -paste Klistra in -delete Ta bort -selectall Markera all -undo Ångra -redo Gör om -TBMessageWindow.ok Ok -TBMessageWindow.cancel Avbryt -TBMessageWindow.yes Ja -TBMessageWindow.no Nej -TBList.header Visar %d av %d -new Ny -save Spara -close Stäng -search Sök diff --git a/data/testturbobadger/demo01/skin/bg_tile.png b/data/testturbobadger/demo01/skin/bg_tile.png deleted file mode 100644 index 36c3fca41..000000000 Binary files a/data/testturbobadger/demo01/skin/bg_tile.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/focus_r8.png b/data/testturbobadger/demo01/skin/focus_r8.png deleted file mode 100644 index bbd923bbc..000000000 Binary files a/data/testturbobadger/demo01/skin/focus_r8.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/icon128.png b/data/testturbobadger/demo01/skin/icon128.png deleted file mode 100644 index 02797f3b6..000000000 Binary files a/data/testturbobadger/demo01/skin/icon128.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/icon16.png b/data/testturbobadger/demo01/skin/icon16.png deleted file mode 100644 index ddaa54fe6..000000000 Binary files a/data/testturbobadger/demo01/skin/icon16.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/icon48.png b/data/testturbobadger/demo01/skin/icon48.png deleted file mode 100644 index a113662f8..000000000 Binary files a/data/testturbobadger/demo01/skin/icon48.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/image_frame.png b/data/testturbobadger/demo01/skin/image_frame.png deleted file mode 100644 index af92850e1..000000000 Binary files a/data/testturbobadger/demo01/skin/image_frame.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/remove.png b/data/testturbobadger/demo01/skin/remove.png deleted file mode 100644 index 0cc7abca5..000000000 Binary files a/data/testturbobadger/demo01/skin/remove.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/special_button.png b/data/testturbobadger/demo01/skin/special_button.png deleted file mode 100644 index db759f8ff..000000000 Binary files a/data/testturbobadger/demo01/skin/special_button.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/special_button_pressed.png b/data/testturbobadger/demo01/skin/special_button_pressed.png deleted file mode 100644 index 96235c324..000000000 Binary files a/data/testturbobadger/demo01/skin/special_button_pressed.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gold.png b/data/testturbobadger/demo01/skin/star_gold.png deleted file mode 100644 index 1c4bebac9..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gold.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gold@192.png b/data/testturbobadger/demo01/skin/star_gold@192.png deleted file mode 100644 index 4591227bb..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gold@192.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gold@288.png b/data/testturbobadger/demo01/skin/star_gold@288.png deleted file mode 100644 index 0941e68b8..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gold@288.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gold@384.png b/data/testturbobadger/demo01/skin/star_gold@384.png deleted file mode 100644 index 3ec2fdb52..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gold@384.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gray.png b/data/testturbobadger/demo01/skin/star_gray.png deleted file mode 100644 index 2266b33f8..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gray.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gray@192.png b/data/testturbobadger/demo01/skin/star_gray@192.png deleted file mode 100644 index 95a5d4537..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gray@192.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gray@288.png b/data/testturbobadger/demo01/skin/star_gray@288.png deleted file mode 100644 index 119f88a29..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gray@288.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/star_gray@384.png b/data/testturbobadger/demo01/skin/star_gray@384.png deleted file mode 100644 index a6bc66ad2..000000000 Binary files a/data/testturbobadger/demo01/skin/star_gray@384.png and /dev/null differ diff --git a/data/testturbobadger/demo01/skin/testturbobadger-skin.tb.txt b/data/testturbobadger/demo01/skin/testturbobadger-skin.tb.txt deleted file mode 100644 index 4ab3a753a..000000000 --- a/data/testturbobadger/demo01/skin/testturbobadger-skin.tb.txt +++ /dev/null @@ -1,80 +0,0 @@ -# Demo skin override (contains some additions for the demo) -description - name Turbo Badger Default skin - # Dimensions in dp (the default unit for padding and such) and bitmaps are specified for this DPI. - base-dpi 96 - # The skin also has graphics available in these DPI modes - supported-dpi 96 288 -elements - Icon16 - bitmap icon16.png - min-width 16 - min-height 16 - Icon48 - bitmap icon48.png - cut 4 - min-width 20 - min-height 20 - Icon128 - bitmap icon128.png - background - bitmap bg_tile.png - type Tile - background_solid - background-color #3b3b3b - padding 10 - SpecialButton - bitmap special_button.png - cut 17 - expand 7 - padding 6 8 - overrides - element SpecialButton.pressed - state pressed - overlays - element generic_focus_r8 - state focused - element Star - condition: target: ancestors, property: skin, value: "Starry buttons" - SpecialButton.pressed - bitmap special_button_pressed.png - cut 17 - expand 7 - content-ofs-x 1 - content-ofs-y 1 - ImageCaption - background-color #00000066 - ImageFrame - overlays - element ImageFrame.overlay - state all - ImageFrame.overlay - bitmap image_frame.png - cut 22 - expand 15 - Remove - bitmap remove.png - type Image - padding 5 - Star - bitmap star_gray.png - type Image - img-position-x 0 - img-position-y 0 - img-ofs-x -8 - img-ofs-y -12 - overrides - element Star.active - condition: target: this, property: "window active" - condition: target: parent, property: hover - Star.active - bitmap star_gold.png - type Image - img-position-x 0 - img-position-y 0 - img-ofs-x -10 - img-ofs-y -14 - generic_focus_r8 - bitmap focus_r8.png - cut 14 - expand 4 diff --git a/data/testturbobadger/demo01/ui_resources/resource_edit_test.tb.txt b/data/testturbobadger/demo01/ui_resources/resource_edit_test.tb.txt deleted file mode 100644 index 8370807b1..000000000 --- a/data/testturbobadger/demo01/ui_resources/resource_edit_test.tb.txt +++ /dev/null @@ -1,21 +0,0 @@ -TBLayout - distribution available - axis y - TBLayout - TBButton - text Button 1 - TBRadioButton - TBCheckBox - @if 1 - TBContainer - TBLayout - TBSlider - value 0.5 - TBScrollBar - TBEditField - multiline 1 - readonly 1 - TBEditField - text Hello world\nHello world - adapt-to-content 1 - multiline 1 diff --git a/data/testturbobadger/demo01/ui_resources/resource_edit_window.tb.txt b/data/testturbobadger/demo01/ui_resources/resource_edit_window.tb.txt deleted file mode 100644 index 87b84c9a3..000000000 --- a/data/testturbobadger/demo01/ui_resources/resource_edit_window.tb.txt +++ /dev/null @@ -1,38 +0,0 @@ -TBLayout - distribution gravity - size available - TBLayout - gravity top bottom - distribution available - distribution-position top left - size available - axis y - TBLayout - distribution available - TBButton - text @new - state disabled - TBButton - text @save - state disabled - TBButton - text Test - id test - TBEditField - placeholder @search - type search - id widget_list_search - TBSelectList - id widget_list - TBLayout - distribution available - TBEditField - id source_edit - multiline 1 - gravity all - TBLayout: axis: y, distribution: available, position: left - TBScrollContainer - id scroll_container - gravity all - TBClickLabel: text: "Adapt to container" - TBCheckBox: id: "constrained" diff --git a/data/testturbobadger/demo01/ui_resources/test_animations.tb.txt b/data/testturbobadger/demo01/ui_resources/test_animations.tb.txt deleted file mode 100644 index e0bc8f5e4..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_animations.tb.txt +++ /dev/null @@ -1,16 +0,0 @@ -WindowInfo - title Animations -TBLayout: axis: y - TBTextField: text: "Duration (in milliseconds):" - TBInlineSelect: id: "duration", min: 0, max: 100000, value: 500 - TBTextField: text: "Curve:" - TBSelectList: id: "curve", value: 1 - items - item: text: "Linear" - item: text: "Slow down" - item: text: "Speed up" - item: text: "Bezier" - item: text: "Smooth" - TBClickLabel: text: Fade - TBCheckBox: id: fade, value: 1 - TBButton: text: "Animate!", id: "Animate!", skin: "SpecialButton" diff --git a/data/testturbobadger/demo01/ui_resources/test_batching01.tb.txt b/data/testturbobadger/demo01/ui_resources/test_batching01.tb.txt deleted file mode 100644 index 134368d0d..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_batching01.tb.txt +++ /dev/null @@ -1,381 +0,0 @@ -TBLayout - axis y - TBTextField: text: "For enabling \"Continuous repaint\" and test batching speed. - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" - TBLayout - TBButton: text: "A" - TBButton: text: "B" - TBButton: text: "C" - TBButton: text: "D" - TBButton: text: "E" - TBButton: text: "F" - TBButton: text: "G" - TBButton: text: "H" - TBButton: text: "I" - TBButton: text: "J" - TBButton: text: "K" - TBButton: text: "L" - TBButton: text: "M" - TBButton: text: "N" - TBButton: text: "O" - TBButton: text: "P" - TBButton: text: "Q" - TBButton: text: "R" - TBButton: text: "S" - TBButton: text: "T" - TBButton: text: "U" - TBButton: text: "V" - TBButton: text: "W" - TBButton: text: "X" - TBButton: text: "Y" - TBButton: text: "Z" diff --git a/data/testturbobadger/demo01/ui_resources/test_connections.tb.txt b/data/testturbobadger/demo01/ui_resources/test_connections.tb.txt deleted file mode 100644 index 80ce79862..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_connections.tb.txt +++ /dev/null @@ -1,43 +0,0 @@ -WindowInfo - title TBWidgetValue connections -TBLayout - axis y - TBTextField - text Numeric widgets sharing a connection - TBContainer - gravity all - TBLayout - TBInlineSelect - connection master-volume - min 0 - max 100 - TBSlider - connection master-volume - min 0 - max 100 - TBSlider - axis y - connection master-volume - min 0 - max 100 - TBButton - text Reset - id reset-master-volume - TBTextField - text Text widgets sharing a connection - TBContainer - gravity all - TBLayout - TBEditField - placeholder User name - connection user-name - min 0 - max 100 - TBEditField - placeholder User name - connection user-name - min 0 - max 100 - TBButton - text Reset - id reset-user-name diff --git a/data/testturbobadger/demo01/ui_resources/test_image_widget.tb.txt b/data/testturbobadger/demo01/ui_resources/test_image_widget.tb.txt deleted file mode 100644 index cee787d87..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_image_widget.tb.txt +++ /dev/null @@ -1,33 +0,0 @@ -WindowInfo - title TBImageWidget - size 350 500 -TBScrollContainer - adapt-content 1 - TBLayout - axis y - spacing 20 - TBEditField: gravity: all, skin: 0, multiline: 1, readonly: 1, adapt-to-content: 1 - text: "Some images shown by TBImageWidget. This test requires enabling TB_IMAGE (see tb_config.h).\n" \ - "Images are unloaded when all references are removed (this window is closed)." - TBImageWidget: filename: "Demo/demo01/images/image_1.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBImageWidget: filename: "Demo/demo01/images/image_2.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBImageWidget: filename: "Demo/demo01/images/image_3.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBImageWidget: filename: "Demo/demo01/images/image_4.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBImageWidget: filename: "Demo/demo01/images/image_5.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBImageWidget: filename: "Demo/demo01/images/image_6.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBTextField: skin: "ImageCaption", text: "Öland", gravity: bottom left right - TBImageWidget: filename: "Demo/demo01/images/image_7.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBTextField: skin: "ImageCaption", text: "Öland", gravity: bottom left right - TBImageWidget: filename: "Demo/demo01/images/image_8.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBTextField: skin: "ImageCaption", text: "Örebro", gravity: bottom left right - TBImageWidget: filename: "Demo/demo01/images/image_9.png", skin: ImageFrame - TBButton: skin: "Remove", id: "remove", gravity: right - TBTextField: skin: "ImageCaption", text: "Stockholm", gravity: bottom left right diff --git a/data/testturbobadger/demo01/ui_resources/test_layout01.tb.txt b/data/testturbobadger/demo01/ui_resources/test_layout01.tb.txt deleted file mode 100644 index 4dc9c7a2c..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_layout01.tb.txt +++ /dev/null @@ -1,55 +0,0 @@ -WindowInfo - title TBLayout size & gravity -# Declare a branch of layout nodes that is included in all three containers below. -test_layout_content - TBLayout - TBButton - text Default - TBCheckBox - value 1 - TBButton - axis y - text Foo Bar - TBSkinImage - skin Icon16 - TBButton - gravity all - text Gravity all - TBButton - gravity top - text Gravity top - TBButton - gravity bottom - text Gravity bottom -TBLayout - axis y - TBLayout - TBSelectDropdown - text Select position - id select position - items - item: text: "LAYOUT_POSITION_CENTER" - item: text: "LAYOUT_POSITION_LEFT_TOP" - item: text: "LAYOUT_POSITION_RIGHT_BOTTOM" - item: text: "LAYOUT_POSITION_GRAVITY" - TBButton - text Toggle axis - id toggle axis - TBLayout - id switch_layout - axis y - TBTextField: text: "size: preferred" - TBContainer - @include test_layout_content - size preferred - id 1 - TBTextField: text: "size: available" - TBContainer - @include test_layout_content - size available - id 2 - TBTextField: text: "size: gravity" - TBContainer - @include test_layout_content - size gravity - id 3 diff --git a/data/testturbobadger/demo01/ui_resources/test_layout02.tb.txt b/data/testturbobadger/demo01/ui_resources/test_layout02.tb.txt deleted file mode 100644 index 31e0adc87..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_layout02.tb.txt +++ /dev/null @@ -1,39 +0,0 @@ -WindowInfo - title TBLayout distribution -TBLayout: position: left top, axis: y - TBTextField: text: "distribution: preferred" - TBLayout: distribution: preferred - TBButton: text: tab 1 - TBButton: text: tab 2 - TBButton: text: tab 3 - TBButton: text: tab 4 - TBEditField: placeholder: @search, gravity: left right, type: "search" - TBTextField: text: "distribution: available" - TBLayout: distribution: available - TBButton: text: tab 1 - TBButton: text: tab 2 - TBButton: text: tab 3 - TBButton: text: tab 4 - TBEditField: placeholder: @search, gravity: left right, type: "search" - TBTextField: text: "distribution: gravity" - TBLayout: distribution: gravity - TBButton: text: tab 1 - TBButton: text: tab 2 - TBButton: text: tab 3 - TBButton: text: tab 4 - TBEditField: placeholder: @search, gravity: left right, type: "search" - TBTextField: text: gravity + trailing layout for right align - TBLayout: distribution: gravity - TBButton: text: tab 1 - TBButton: text: tab 2 - TBButton: text: tab 3 - TBButton: text: tab 4 - TBLayout: gravity: left right, distribution-position: right bottom - TBEditField: placeholder: @search, type: "search" - TBTextField: text: "gravity + mixed gravity" - TBLayout: distribution: gravity - TBButton: text: tab 1 - TBEditField: placeholder: @search, gravity: left right, type: "search" - TBButton: text: tab 3 - TBButton: text: tab 4 - TBEditField: placeholder: @search, gravity: left right, type: "search" diff --git a/data/testturbobadger/demo01/ui_resources/test_layout03.tb.txt b/data/testturbobadger/demo01/ui_resources/test_layout03.tb.txt deleted file mode 100644 index 590ead2dd..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_layout03.tb.txt +++ /dev/null @@ -1,21 +0,0 @@ -WindowInfo - title Default TBWidget positioning - size 350 350 -TBLayout: axis: y, distribution: gravity - TBLayout: distribution: available, gravity: left right - TBEditField: gravity: all, skin: 0, multiline: 1, readonly: 1 - text: "Any widget may contain children and by default applies basic gravity positioning.\n" \ - "To achieve more advanced layout, you would normally wrap a layouting " \ - "widget (f.ex TBLayout) but sometimes the default is enough.\n" \ - "The default preferred size is calculated to the largest child " \ - "(or the skin if there are no children)." - TBContainer: gravity: all - TBTextField: text: "all", gravity: all - TBTextField: text: "top, left, right", gravity: top left right - TBTextField: text: "bottom, left, right", gravity: bottom left right - TBTextField: text: "left, top, bottom", gravity: left top bottom - TBTextField: text: "right, top, bottom", gravity: right top bottom - TBButton: text: Upper left - TBButton: text: Upper right, gravity: right - TBButton: text: Lower left, gravity: bottom - TBButton: text: Lower right, gravity: bottom right diff --git a/data/testturbobadger/demo01/ui_resources/test_list_item.tb.txt b/data/testturbobadger/demo01/ui_resources/test_list_item.tb.txt deleted file mode 100644 index 65ae1a1bb..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_list_item.tb.txt +++ /dev/null @@ -1,6 +0,0 @@ -TBCheckBox: id: check -TBSkinImage: id: icon, skin: Icon48, ignore-input: 1 -TBLayout: axis: y, position: left, spacing: 0, ignore-input: 1, size: available, position: left top - TBTextField: id: name, text-align: left - TBTextField: id: info, text-align: left, state: disabled -TBButton: id: delete, skin: TBWindow.close \ No newline at end of file diff --git a/data/testturbobadger/demo01/ui_resources/test_radio_checkbox.tb.txt b/data/testturbobadger/demo01/ui_resources/test_radio_checkbox.tb.txt deleted file mode 100644 index f9ef455ca..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_radio_checkbox.tb.txt +++ /dev/null @@ -1,38 +0,0 @@ -WindowInfo - title Radiobutton & checkbox -TBLayout: position: left top, axis: y - TBTextField: text: "Radiobuttons, anonymous group:" - TBLayout: distribution: preferred - TBRadioButton - TBRadioButton - TBRadioButton: state: disabled - TBTextField: text: "Radiobuttons, group-id: 'R1'": - TBLayout: distribution: preferred - TBRadioButton: group-id: R1 - TBRadioButton: group-id: R1 - TBRadioButton: group-id: R1, state: disabled - TBTextField: text: "Radiobuttons, group-id: 'R2' value set": - TBLayout: distribution: preferred - TBRadioButton: group-id: R2, value: 1 - TBRadioButton: group-id: R2 - TBRadioButton: group-id: R2, state: disabled - TBTextField: text: "Checkboxes" - TBLayout: distribution: preferred - TBCheckBox - TBCheckBox: value: 1 - TBCheckBox: state: disabled - TBCheckBox: state: disabled, value: 1 - TBTextField: text: "TBClickLabel with radio & check" - TBClickLabel: text: Some text to click - TBCheckBox - TBClickLabel: text: Some text to click - TBRadioButton - TBClickLabel: text: Some text to click, state: disabled - TBCheckBox - TBClickLabel: text: Some text to click, state: disabled - TBRadioButton - TBTextField: text: "Regular buttons with group-id: 'R3'": - TBLayout: distribution: preferred - TBButton: text: "A", group-id: "R3", value: 1 - TBButton: text: "B", group-id: "R3" - TBButton: text: "C", group-id: "R3" diff --git a/data/testturbobadger/demo01/ui_resources/test_scrollcontainer.tb.txt b/data/testturbobadger/demo01/ui_resources/test_scrollcontainer.tb.txt deleted file mode 100644 index 4b97c3d90..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_scrollcontainer.tb.txt +++ /dev/null @@ -1,123 +0,0 @@ -WindowInfo - title TBScrollContainer with misc widgets - size 500 500 -TBLayout - gravity all - distribution available - axis y - TBScrollContainer - TBLayout - position left top - axis y - TBTextField: text: Buttons with different fonts: - TBLayout - TBButton - text: "Orange" - font: name: Orange - TBButton - text: "Segoe 14" - font: size: 14px - TBButton - text: "Segoe 28" - font: size: 28px - TBTextField: text: TBButton containing TBSkinImage: - TBLayout - TBButton - text Click for image! - id add img - TBSkinImage - skin Icon16 - TBButton - axis y - text Click for image! - id add img - TBSkinImage - skin Icon16 - TBTextField: text: "TBButton containing TBEditField:" - TBLayout - TBButton - TBEditField: gravity: all, skin: 0, multiline: 1, readonly: 1, adapt-to-content: 1, styling: 1 - text: "A TBButton with styled text that is able to wrap to multiple lines." - TBTextField: text: "Showing menu manually, and TBSelectDropdown:" - TBLayout - TBButton - text Give me a popup menu! - id showpopupmenu1 - TBSelectDropdown - text Dropdown - id name dropdown - TBSelectDropdown - text Customized items in Dropdown - id advanced dropdown - TBLayout - overflow scroll - TBRadioButton - TBCheckBox - value 1 - TBSkinImage - skin Icon16 - TBSkinImage - skin Icon48 - TBButton - TBButton: toggle-mode: 1 - TBTextField: text: "TBEditField:" - TBLayout - TBEditField - text Edit type text - TBEditField - type search - text Edit type search - TBEditField - type password - text pwd goes here - TBEditField - text In this field, multiple lines are enabled. - multiline 1 - TBTextField: text: "TBSlider:" - TBLayout - TBSlider - TBSlider - min 100 - max 200 - value 150 - TBSlider - axis y - min 0 - max 100 - connection master-volume - TBTextField: text: "TBScrollbar:" - TBLayout - TBScrollBar - TBScrollBar - axis y - TBTextField: text: Horizontal layout: - TBLayout - id horizontal_layout - TBButton - text "1 new button" - id new buttons - data 1 - TBButton - text "100 new buttons!" - id new buttons - data 100 - TBButton - text "10 new with delay!" - id new buttons delayed - data 10 - TBTextField: text: Vertical layout: - TBLayout - id vertical_layout - axis y - TBButton - text "1 new button" - id new buttons - data 1 - TBButton - text "100 new buttons!" - id new buttons - data 100 - TBButton - text "10 new with delay!" - id new buttons delayed - data 10 diff --git a/data/testturbobadger/demo01/ui_resources/test_scroller_snap.tb.txt b/data/testturbobadger/demo01/ui_resources/test_scroller_snap.tb.txt deleted file mode 100644 index 1ced50c0f..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_scroller_snap.tb.txt +++ /dev/null @@ -1,25 +0,0 @@ -WindowInfo - title TBScrollerSnapListener -definitions - pagesize - lp: width: 200, height: 300 -TBScrollContainer: id: "page-scroller" - @include definitions>pagesize - scroll-mode off - TBLayout - spacing 0 - TBContainer - @include definitions>pagesize - TBTextField: text: "One - Swipe to next page" - TBContainer - @include definitions>pagesize - TBTextField: text: "Two" - TBContainer - @include definitions>pagesize - TBTextField: text: "Three" - TBContainer - @include definitions>pagesize - TBTextField: text: "Four" - TBContainer - @include definitions>pagesize - TBTextField: text: "Five - Last page" diff --git a/data/testturbobadger/demo01/ui_resources/test_select.tb.txt b/data/testturbobadger/demo01/ui_resources/test_select.tb.txt deleted file mode 100644 index d0028516f..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_select.tb.txt +++ /dev/null @@ -1,15 +0,0 @@ -WindowInfo - title List and filter - position 1100 50 -TBLayout - axis y - distribution gravity - gravity all - TBEditField - id filter - gravity left right - placeholder @search - type search - TBSelectList - id list - gravity all diff --git a/data/testturbobadger/demo01/ui_resources/test_select_advanced.tb.txt b/data/testturbobadger/demo01/ui_resources/test_select_advanced.tb.txt deleted file mode 100644 index 6f73dc795..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_select_advanced.tb.txt +++ /dev/null @@ -1,30 +0,0 @@ -WindowInfo - title Customized list items - position 770 50 - size 300 300 -TBLayout - axis y - distribution gravity - gravity all - TBLayout - distribution gravity - TBEditField - id filter - gravity left right - placeholder @search - type search - TBSelectList - id list - gravity all - TBLayout - distribution gravity - TBEditField - id add_name - gravity left right - placeholder Name - TBButton - id add - text Add - TBButton - id delete all - text Delete all diff --git a/data/testturbobadger/demo01/ui_resources/test_skin_conditions01.tb.txt b/data/testturbobadger/demo01/ui_resources/test_skin_conditions01.tb.txt deleted file mode 100644 index 4439ddf47..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_skin_conditions01.tb.txt +++ /dev/null @@ -1,18 +0,0 @@ -WindowInfo - title Skin conditions - position 200 170 -TBLayout: axis: y - TBTextField: text: Normal layout with buttons: - TBContainer - TBLayout - TBButton: text: " A ", skin: "SpecialButton" - TBButton: text: " B ", skin: "SpecialButton" - TBButton: text: " C ", skin: "SpecialButton" - TBTextField: text: Layout with skin "Starry buttons": - TBContainer - TBLayout: skin: Starry buttons - TBButton: text: " A ", skin: "SpecialButton" - TBButton: text: " B ", skin: "SpecialButton" - TBButton: text: " C ", skin: "SpecialButton" - TBEditField: gravity: all, skin: 0, multiline: 1, readonly: 1, adapt-to-content: 1 - text The skin has a condition to show a upper left positioned overlay "Star" if placed under a ancestor widget with skin "Starry buttons". The Star skin has conditions to change bitmap if the window is active and the parent widget or any child to it is hovered. diff --git a/data/testturbobadger/demo01/ui_resources/test_skin_conditions02.tb.txt b/data/testturbobadger/demo01/ui_resources/test_skin_conditions02.tb.txt deleted file mode 100644 index f249ae93f..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_skin_conditions02.tb.txt +++ /dev/null @@ -1,44 +0,0 @@ -WindowInfo - title Skin conditions - position 500 170 -TBLayout - axis y - TBEditField: gravity: all, skin: 0, multiline: 1, readonly: 1, adapt-to-content: 1 - text The TBButton skin has a condition to show a different skin when placed under a parent widget with skin "button_group". There is also conditions to use a different skin for the first and last button. - TBLayout - TBLayout: skin: button_group - TBButton - text < - TBButton - text > - TBLayout: skin: button_group - TBButton - text 1 - TBButton - text 2 - TBButton - text 3 - TBLayout: skin: button_group - TBButton - text A - TBButton - text B - TBButton - text C - TBLayout: skin: button_group - TBButton - text Hello - TBButton - text World! - TBEditField: gravity: all, skin: 0, multiline: 1, readonly: 1, adapt-to-content: 1 - text The "button_group" skin overrides default layout spacing. It can also be overridden in the UI resource. - TBLayout: skin: button_group - spacing 2 - TBButton - text Group - TBButton - text with - TBButton - text spacing - TBButton - text 2 diff --git a/data/testturbobadger/demo01/ui_resources/test_tabcontainer01.tb.txt b/data/testturbobadger/demo01/ui_resources/test_tabcontainer01.tb.txt deleted file mode 100644 index fa7b50a1f..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_tabcontainer01.tb.txt +++ /dev/null @@ -1,38 +0,0 @@ -WindowInfo - title TBTabContainer & misc tests - position 770 430 -TBTabContainer - gravity all - id tabcontainer - tabs - TBButton: text: "Tab with long text" - TBButton: text: "Skin" - TBButton: text: "Spinner" - TBSkinImage: skin: Icon16 - TBLayout: axis: y - TBTextField: text: "Tab alignment:" - TBLayout - TBClickLabel: text: Left - TBRadioButton: group-id: set_align, id: set_align, data: 0 - TBClickLabel: text: Up - TBRadioButton: group-id: set_align, id: set_align, data: 1, value: 1 - TBClickLabel: text: Right - TBRadioButton: group-id: set_align, id: set_align, data: 2 - TBClickLabel: text: Bottom - TBRadioButton: group-id: set_align, id: set_align, data: 3 - TBButton - text Toggle tab axis - id toggle_tab_axis - TBLayout - TBLayout: axis: y, position: right bottom - TBButton: text: Normal button - TBButton: text: Overridden skin, skin: SpecialButton - TBButton: text: Disabled button, state: disabled - TBEditField - text TBEditField - multiline 1 - TBLayout - axis y - TBButton: id: start_spinner, text: Start spinner - TBButton: id: stop_spinner, text: Stop spinner - TBProgressSpinner: id: spinner diff --git a/data/testturbobadger/demo01/ui_resources/test_textwindow.tb.txt b/data/testturbobadger/demo01/ui_resources/test_textwindow.tb.txt deleted file mode 100644 index 680499f2b..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_textwindow.tb.txt +++ /dev/null @@ -1,35 +0,0 @@ -WindowInfo - title Text editing - position 280 50 - size 450 630 -TBLayout: axis: y, distribution: available - TBLayout: distribution: gravity - TBButton: id: undo - TBSkinImage: skin: arrow.left - TBButton: id: redo - TBSkinImage: skin: arrow.right - TBButton: text: Clear, id: clear - TBLayout - gravity left right - distribution-position right bottom - TBTextField: id: info - TBButton: id: menu - TBSkinImage: skin: arrow.down - TBTextField: text: Menu - skin TBButton.flat - TBEditField: multiline: 1, styling: 1, gravity: all, id: editfield, autofocus: 1 - text: "Turbo Badger - Fast UI toolkit\n\n" \ - "Test zone\n" \ - "The menu to the left spawns some examples of features in turbo badger.\n\n" \ - "The code in Demo/* is more like a developers test zone than organized tutorial-like samples. " \ - "This would be good to fix of course (help is welcome! ;)\n\n" \ - "Some things to try out\n" \ - " • All layouts provide panning of content automatically when squashed below the minimal size, so try resizing windows and pan.\n" \ - " • Lines starting with a bullet sequence (like this one) should wrap in a smart way.\n" \ - " • Message windows whose target is removed, are automatically removed. Close the menu window with open messages.\n" \ - " • Keyboard navigation using tab.\n" \ - " • Underline, Red, Green, Blue\n\n" \ - "Good to know\n" \ - " • The text widget handles styling and embedded content (Example: ), but does not handle editing of those fully. Those features are only ment for read-only text widgets for now.\n" \ - " • You can emulate touch input by holding down shift, ctrl or alt. Touch input behaves differently in some widgets (f.ex long click to open context menu in textfields), and only invoke movement when down.\n" \ - " • All resources are UTF-8.\n" diff --git a/data/testturbobadger/demo01/ui_resources/test_toggle_containers.tb.txt b/data/testturbobadger/demo01/ui_resources/test_toggle_containers.tb.txt deleted file mode 100644 index 7bd1f05cb..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_toggle_containers.tb.txt +++ /dev/null @@ -1,54 +0,0 @@ -WindowInfo - title TBToggleContainer -TBLayout: axis: y - TBEditField: gravity: all, adapt-to-content: 1, multiline: 1, readonly: 1, skin: 0, virtual-width: 400 - text: "TBToggleContainer used with TBWidgetValue connections to automatically toggle a group of items (no programming needed)." - - TBLayout: position: top left - TBContainer - TBLayout: axis: y - TBClickLabel: text: "Check to enable stuff:" - TBCheckBox: connection: toggle_demo_1 - - # == Toggle enabled ===================================================== - TBToggleContainer: connection: toggle_demo_1, toggle: enabled - TBLayout: axis: y - TBClickLabel: text: "Option 1" - TBRadioButton: group-id: group1, value: 1 - TBClickLabel: text: "Option 2" - TBRadioButton: group-id: group1 - - TBClickLabel: text: "Check to disable stuff:" - TBCheckBox: connection: toggle_demo_2 - - # == Toggle enabled, with the invert flag set =========================== - TBToggleContainer: connection: toggle_demo_2, toggle: enabled, invert: 1 - TBLayout: axis: y - TBClickLabel: text: "Option 1" - TBRadioButton: group-id: group2, value: 1 - TBClickLabel: text: "Option 2" - TBRadioButton: group-id: group2 - - TBContainer - TBLayout: axis: y - TBClickLabel: text: "Check to show stuff:" - TBCheckBox: connection: toggle_demo_3 - - # == Toggle opacity ===================================================== - TBToggleContainer: connection: toggle_demo_3, toggle: opacity - TBLayout: axis: y - TBClickLabel: text: "Option 1" - TBRadioButton: group-id: group3, value: 1 - TBClickLabel: text: "Option 2" - TBRadioButton: group-id: group3 - - TBClickLabel: text: "Check to expand stuff:" - TBCheckBox: connection: toggle_demo_4 - - # == Toggle expand ====================================================== - TBToggleContainer: connection: toggle_demo_4, toggle: expanded - TBLayout: axis: y - TBClickLabel: text: "Option 1" - TBRadioButton: group-id: group4, value: 1 - TBClickLabel: text: "Option 2" - TBRadioButton: group-id: group4 diff --git a/data/testturbobadger/demo01/ui_resources/test_ui.tb.txt b/data/testturbobadger/demo01/ui_resources/test_ui.tb.txt deleted file mode 100644 index 944012b4d..000000000 --- a/data/testturbobadger/demo01/ui_resources/test_ui.tb.txt +++ /dev/null @@ -1,58 +0,0 @@ -WindowInfo - title Turbo Badger - position 10 50 - size 250 630 - -TBLayout: axis: y, distribution-position: "left top", distribution: "available" - - TBSection: value: 1, text: "Misc features" - TBLayout: axis: y, spacing: 0, size: available - TBButton: skin: "TBButton.flat", text: "Radiobutton & Checkbox" - id: "test-layout" - data: "test_radio_checkbox.tb.txt" - TBButton: skin: "TBButton.flat", text: "TBSelectList", id: "test-list" - TBButton: skin: "TBButton.flat", text: "ScrollContainer & misc.", id: "test-scroll-container" - TBButton: skin: "TBButton.flat", text: "TBWidgetValue connections", id: "test-connections" - TBButton: skin: "TBButton.flat", text: "TBImage", id: "test-image" - TBButton: skin: "TBButton.flat", text: "TBScrollerSnapListener", id: "test-page" - TBButton: skin: "TBButton.flat", text: "Animations", id: "test-animations" - TBButton: skin: "TBButton.flat", text: "Skin conditions", id: "test-skin-conditions" - TBButton: skin: "TBButton.flat", text: "TBToggleContainer" - id: "test-layout" - data: "test_toggle_containers.tb.txt" - TBButton: skin: "TBButton.flat", text: "Close with dim & alert", id: "TBWindow.close" - TBButton: skin: "TBButton.flat", text: "ResourceEditWindow", id: "test-resource-edit" - - TBSection: value: 0, text: "Layout tests" - TBLayout: axis: y, spacing: 0, size: available - TBButton: skin: "TBButton.flat", text: "Size, gravity, position" - id: "test-layout" - data: "test_layout01.tb.txt" - TBButton: skin: "TBButton.flat", text: "Distribution" - id: "test-layout" - data: "test_layout02.tb.txt" - TBButton: skin: "TBButton.flat", text: "TBWidget default" - id: "test-layout" - data: "test_layout03.tb.txt" - - TBSection: value: 0, text: "Graphics tests" - TBLayout: axis: y, spacing: 0, size: available - TBClickLabel: text: "Continous repaint" - lp: max-width: 0 - TBCheckBox: connection: continous-repaint - TBButton: skin: "TBButton.flat", text: "Reload skin bitmaps", id: "reload skin bitmaps" - TBButton: skin: "TBButton.flat", text: "Context lost & restore", id: "test context lost" - - TBSection: value: 0, text: "Message tests" - TBLayout: axis: y, spacing: 0, size: available - TBButton: skin: "TBButton.flat", text: "PostMessage", id: "msg" - TBButton: skin: "TBButton.flat", text: "PostMessageDelayed", id: "delayedmsg" - TBClickLabel: text: "Busy message loop" - lp: max-width: 0 - TBCheckBox: id: "busymsg" - - # We want the debug button at the bottom if there is space over, - # so use an trailing layout which may expand a lot. - TBLayout: axis: y, distribution-position: bottom - lp: max-height: 10000 - TBButton: id: "debug settings", text: "Runtime debug settings..." diff --git a/data/voxedit-ui/ui/lang/en.tb.txt b/data/voxedit-ui/ui/lang/en.tb.txt deleted file mode 100644 index 0bf40fa50..000000000 --- a/data/voxedit-ui/ui/lang/en.tb.txt +++ /dev/null @@ -1,13 +0,0 @@ -cut Cut -copy Copy -paste Paste -delete Delete -selectall Select all -TBMessageWindow.ok Ok -TBMessageWindow.cancel Cancel -TBMessageWindow.yes Yes -TBMessageWindow.no No -TBList.header Showing %d of %d -ok Ok -cancel Cancel -error Error diff --git a/data/voxedit-ui/ui/skin/voxedit-add.png b/data/voxedit-ui/ui/skin/voxedit-add.png deleted file mode 100644 index bceea08db..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-add.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-add@288.png b/data/voxedit-ui/ui/skin/voxedit-add@288.png deleted file mode 100644 index a0cb22e71..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-add@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-anim-play-selected.png b/data/voxedit-ui/ui/skin/voxedit-anim-play-selected.png deleted file mode 100644 index d222c7ff9..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-anim-play-selected.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-anim-play-selected@288.png b/data/voxedit-ui/ui/skin/voxedit-anim-play-selected@288.png deleted file mode 100644 index a41d3ab70..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-anim-play-selected@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-anim-play.png b/data/voxedit-ui/ui/skin/voxedit-anim-play.png deleted file mode 100644 index 51c20a98a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-anim-play.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-anim-play@288.png b/data/voxedit-ui/ui/skin/voxedit-anim-play@288.png deleted file mode 100644 index 70fc96361..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-anim-play@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-animation-selected.png b/data/voxedit-ui/ui/skin/voxedit-animation-selected.png deleted file mode 100644 index 99da90847..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-animation-selected.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-animation-selected@288.png b/data/voxedit-ui/ui/skin/voxedit-animation-selected@288.png deleted file mode 100644 index a8ce3ec95..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-animation-selected@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-animation.png b/data/voxedit-ui/ui/skin/voxedit-animation.png deleted file mode 100644 index 7faa5349f..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-animation.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-animation@288.png b/data/voxedit-ui/ui/skin/voxedit-animation@288.png deleted file mode 100644 index e07509b4e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-animation@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-axis-x.png b/data/voxedit-ui/ui/skin/voxedit-axis-x.png deleted file mode 100644 index 86eb0446c..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-axis-x.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-axis-x@288.png b/data/voxedit-ui/ui/skin/voxedit-axis-x@288.png deleted file mode 100644 index de8cc77ff..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-axis-x@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-axis-y.png b/data/voxedit-ui/ui/skin/voxedit-axis-y.png deleted file mode 100644 index 9fde72f9f..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-axis-y.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-axis-y@288.png b/data/voxedit-ui/ui/skin/voxedit-axis-y@288.png deleted file mode 100644 index 9dc622dc5..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-axis-y@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-axis-z.png b/data/voxedit-ui/ui/skin/voxedit-axis-z.png deleted file mode 100644 index eaff6ef84..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-axis-z.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-axis-z@288.png b/data/voxedit-ui/ui/skin/voxedit-axis-z@288.png deleted file mode 100644 index 3e56542b4..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-axis-z@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-color-picker.png b/data/voxedit-ui/ui/skin/voxedit-color-picker.png deleted file mode 100644 index ca2f656cf..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-color-picker.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-color-picker@288.png b/data/voxedit-ui/ui/skin/voxedit-color-picker@288.png deleted file mode 100644 index 662270a80..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-color-picker@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-color.png b/data/voxedit-ui/ui/skin/voxedit-color.png deleted file mode 100644 index 0c8166618..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-color.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-color@288.png b/data/voxedit-ui/ui/skin/voxedit-color@288.png deleted file mode 100644 index 25138ad48..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-color@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-colors.png b/data/voxedit-ui/ui/skin/voxedit-colors.png deleted file mode 100644 index 40fc13a97..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-colors.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-colors@288.png b/data/voxedit-ui/ui/skin/voxedit-colors@288.png deleted file mode 100644 index 966f60fe1..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-colors@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-crop.png b/data/voxedit-ui/ui/skin/voxedit-crop.png deleted file mode 100644 index a1a50d138..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-crop.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-crop@288.png b/data/voxedit-ui/ui/skin/voxedit-crop@288.png deleted file mode 100644 index 492b04af9..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-crop@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-delete.png b/data/voxedit-ui/ui/skin/voxedit-delete.png deleted file mode 100644 index a8170625e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-delete.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-delete@288.png b/data/voxedit-ui/ui/skin/voxedit-delete@288.png deleted file mode 100644 index 2cda07d62..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-delete@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-export.png b/data/voxedit-ui/ui/skin/voxedit-export.png deleted file mode 100644 index 3b5f85505..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-export.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-export@288.png b/data/voxedit-ui/ui/skin/voxedit-export@288.png deleted file mode 100644 index 3a2289e73..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-export@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-extend.png b/data/voxedit-ui/ui/skin/voxedit-extend.png deleted file mode 100644 index ac1141790..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-extend.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-extend@288.png b/data/voxedit-ui/ui/skin/voxedit-extend@288.png deleted file mode 100644 index 7f6e6129e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-extend@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-flip-horizontal.png b/data/voxedit-ui/ui/skin/voxedit-flip-horizontal.png deleted file mode 100644 index 1c815dd39..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-flip-horizontal.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-flip-horizontal@288.png b/data/voxedit-ui/ui/skin/voxedit-flip-horizontal@288.png deleted file mode 100644 index dd3ca5719..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-flip-horizontal@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-flip-vertical.png b/data/voxedit-ui/ui/skin/voxedit-flip-vertical.png deleted file mode 100644 index bc3907d92..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-flip-vertical.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-flip-vertical@288.png b/data/voxedit-ui/ui/skin/voxedit-flip-vertical@288.png deleted file mode 100644 index c1ff6e698..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-flip-vertical@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-import-palette.png b/data/voxedit-ui/ui/skin/voxedit-import-palette.png deleted file mode 100644 index cfa391970..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-import-palette.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-import-palette@288.png b/data/voxedit-ui/ui/skin/voxedit-import-palette@288.png deleted file mode 100644 index 9f0b320de..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-import-palette@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-load.png b/data/voxedit-ui/ui/skin/voxedit-load.png deleted file mode 100644 index 3a6064ecb..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-load.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-load@288.png b/data/voxedit-ui/ui/skin/voxedit-load@288.png deleted file mode 100644 index 0ff62389e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-load@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-lock-layer-selected.png b/data/voxedit-ui/ui/skin/voxedit-lock-layer-selected.png deleted file mode 100644 index d661e2a43..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-lock-layer-selected.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-lock-layer-selected@288.png b/data/voxedit-ui/ui/skin/voxedit-lock-layer-selected@288.png deleted file mode 100644 index a9bc50258..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-lock-layer-selected@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-lock-layer.png b/data/voxedit-ui/ui/skin/voxedit-lock-layer.png deleted file mode 100644 index af9c0ed51..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-lock-layer.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-lock-layer@288.png b/data/voxedit-ui/ui/skin/voxedit-lock-layer@288.png deleted file mode 100644 index e4a13702f..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-lock-layer@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-move.png b/data/voxedit-ui/ui/skin/voxedit-move.png deleted file mode 100644 index 83f485970..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-move.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-move@288.png b/data/voxedit-ui/ui/skin/voxedit-move@288.png deleted file mode 100644 index 811572c6e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-move@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-new.png b/data/voxedit-ui/ui/skin/voxedit-new.png deleted file mode 100644 index 45eafb50a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-new.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-new@288.png b/data/voxedit-ui/ui/skin/voxedit-new@288.png deleted file mode 100644 index 69090ffb4..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-new@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-options.png b/data/voxedit-ui/ui/skin/voxedit-options.png deleted file mode 100644 index 169b97695..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-options.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-options@288.png b/data/voxedit-ui/ui/skin/voxedit-options@288.png deleted file mode 100644 index 0e266e3b4..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-options@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-override.png b/data/voxedit-ui/ui/skin/voxedit-override.png deleted file mode 100644 index d3f2bb62e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-override.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-override@288.png b/data/voxedit-ui/ui/skin/voxedit-override@288.png deleted file mode 100644 index 0d9457ed8..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-override@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-quadview-selected.png b/data/voxedit-ui/ui/skin/voxedit-quadview-selected.png deleted file mode 100644 index 7d335b973..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-quadview-selected.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-quadview-selected@288.png b/data/voxedit-ui/ui/skin/voxedit-quadview-selected@288.png deleted file mode 100644 index 4abb2c158..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-quadview-selected@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-quadview.png b/data/voxedit-ui/ui/skin/voxedit-quadview.png deleted file mode 100644 index 60128d119..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-quadview.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-quadview@288.png b/data/voxedit-ui/ui/skin/voxedit-quadview@288.png deleted file mode 100644 index d69b977ff..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-quadview@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-quit.png b/data/voxedit-ui/ui/skin/voxedit-quit.png deleted file mode 100644 index 0ed80bb40..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-quit.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-quit@288.png b/data/voxedit-ui/ui/skin/voxedit-quit@288.png deleted file mode 100644 index 9a2b60ed4..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-quit@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-reset-camera.png b/data/voxedit-ui/ui/skin/voxedit-reset-camera.png deleted file mode 100644 index 5e2caf68a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-reset-camera.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-reset-camera@288.png b/data/voxedit-ui/ui/skin/voxedit-reset-camera@288.png deleted file mode 100644 index a2326a933..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-reset-camera@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-rotate.png b/data/voxedit-ui/ui/skin/voxedit-rotate.png deleted file mode 100644 index 3f4581bdc..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-rotate.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-rotate@288.png b/data/voxedit-ui/ui/skin/voxedit-rotate@288.png deleted file mode 100644 index 893c82aba..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-rotate@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-save.png b/data/voxedit-ui/ui/skin/voxedit-save.png deleted file mode 100644 index e6fe9a211..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-save.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-save@288.png b/data/voxedit-ui/ui/skin/voxedit-save@288.png deleted file mode 100644 index 8e79be49d..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-save@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-settings.png b/data/voxedit-ui/ui/skin/voxedit-settings.png deleted file mode 100644 index 23e0a7858..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-settings.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-settings@288.png b/data/voxedit-ui/ui/skin/voxedit-settings@288.png deleted file mode 100644 index 00a8e33e2..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-settings@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-shift.png b/data/voxedit-ui/ui/skin/voxedit-shift.png deleted file mode 100644 index 1d930c7e5..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-shift.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-shift@288.png b/data/voxedit-ui/ui/skin/voxedit-shift@288.png deleted file mode 100644 index 60806ae87..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-shift@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-show-grid-selected.png b/data/voxedit-ui/ui/skin/voxedit-show-grid-selected.png deleted file mode 100644 index 8ee008c2e..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-show-grid-selected.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-show-grid-selected@288.png b/data/voxedit-ui/ui/skin/voxedit-show-grid-selected@288.png deleted file mode 100644 index 2049de734..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-show-grid-selected@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-show-grid.png b/data/voxedit-ui/ui/skin/voxedit-show-grid.png deleted file mode 100644 index e40ca413f..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-show-grid.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-show-grid@288.png b/data/voxedit-ui/ui/skin/voxedit-show-grid@288.png deleted file mode 100644 index df5d626c9..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-show-grid@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-skin.tb.txt b/data/voxedit-ui/ui/skin/voxedit-skin.tb.txt deleted file mode 100644 index be5d5935e..000000000 --- a/data/voxedit-ui/ui/skin/voxedit-skin.tb.txt +++ /dev/null @@ -1,173 +0,0 @@ -elements - voxedit-color - bitmap voxedit-color.png - voxedit-delete - bitmap voxedit-delete.png - voxedit-flip-horizontal - bitmap voxedit-flip-horizontal.png - voxedit-flip-vertical - bitmap voxedit-flip-vertical.png - voxedit-load - bitmap voxedit-load.png - voxedit-import-palette - bitmap voxedit-import-palette.png - voxedit-down - bitmap voxedit-up.png - flip-y 1 - voxedit-colors - bitmap voxedit-colors.png - voxedit-add - bitmap voxedit-add.png - voxedit-up - bitmap voxedit-up.png - voxedit-new - bitmap voxedit-new.png - voxedit-options - bitmap voxedit-options.png - voxedit-quit - bitmap voxedit-quit.png - voxedit-move - bitmap voxedit-move.png - voxedit-shift - bitmap voxedit-shift.png - voxedit-redo - bitmap voxedit-undo-redo.png - voxedit-rotate-left - bitmap voxedit-rotate.png - voxedit-rotate-right - bitmap voxedit-rotate.png - flip-x 1 - voxedit-save - bitmap voxedit-save.png - voxedit-show-grid - bitmap voxedit-show-grid.png - voxedit-undo - bitmap voxedit-undo-redo.png - flip-x 1 - voxedit-voxel - bitmap voxedit-voxel.png - voxedit-reset-camera - bitmap voxedit-reset-camera.png - voxedit-override - bitmap voxedit-override.png - voxedit-export - bitmap voxedit-export.png - voxedit-axis-x - bitmap voxedit-axis-x.png - voxedit-axis-y - bitmap voxedit-axis-y.png - voxedit-axis-z - bitmap voxedit-axis-z.png - voxedit-tree - bitmap voxedit-tree.png - voxedit-structure - bitmap voxedit-tree.png - voxedit-extend - bitmap voxedit-extend.png - voxedit-crop - bitmap voxedit-crop.png - voxedit-tree_pine - bitmap voxedit-tree_pine.png - - voxedit-animation-button - bitmap voxedit-animation.png - children - element voxedit-animation-button.selected - state selected - element voxedit-animation-button.pressed - state pressed - voxedit-animation-button.selected - bitmap voxedit-animation-selected.png - type Image - voxedit-animation-button.pressed - bitmap voxedit-animation-selected.png - type Image - - voxedit-quadview-button - bitmap voxedit-quadview.png - children - element voxedit-quadview-button.selected - state selected - element voxedit-quadview-button.pressed - state pressed - voxedit-quadview-button.selected - bitmap voxedit-quadview-selected.png - type Image - voxedit-quadview-button.pressed - bitmap voxedit-quadview-selected.png - type Image - - voxedit-anim-button - bitmap voxedit-anim-play.png - children - element voxedit-anim-button.selected - state selected - element voxedit-anim-button.pressed - state pressed - voxedit-anim-button.selected - bitmap voxedit-anim-play-selected.png - type Image - voxedit-anim-button.pressed - bitmap voxedit-anim-play-selected.png - type Image - - voxedit-visible-button - bitmap voxedit-visible.png - children - element voxedit-visible-button.selected - state selected - element voxedit-visible-button.pressed - state pressed - voxedit-visible-button.selected - bitmap voxedit-visible-selected.png - type Image - voxedit-visible-button.pressed - bitmap voxedit-visible-selected.png - type Image - - voxedit-lock-layer-button - bitmap voxedit-lock-layer.png - children - element voxedit-lock-layer-button.selected - state selected - element voxedit-lock-layer-button.pressed - state pressed - voxedit-lock-layer-button.selected - bitmap voxedit-lock-layer-selected.png - type Image - voxedit-lock-layer-button.pressed - bitmap voxedit-lock-layer-selected.png - type Image - - voxedit-grid-button - bitmap voxedit-show-grid.png - children - element voxedit-grid-button.selected - state selected - element voxedit-grid-button.pressed - state pressed - voxedit-grid-button.selected - bitmap voxedit-show-grid-selected.png - type Image - voxedit-grid-button.pressed - bitmap voxedit-show-grid-selected.png - type Image - - color-picker - type StretchBox - bitmap voxedit-color-picker.png - - voxedit-file - bitmap editfield.png - cut 12 - expand 4 - padding 4 4 4 24 - min-width 50 - children - element voxedit-file.icon - - voxedit-file.icon - bitmap voxedit-load.png - type Image - img-position-x 0 - img-ofs-x 5 diff --git a/data/voxedit-ui/ui/skin/voxedit-tree.png b/data/voxedit-ui/ui/skin/voxedit-tree.png deleted file mode 100644 index ae98d1930..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-tree.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-tree@288.png b/data/voxedit-ui/ui/skin/voxedit-tree@288.png deleted file mode 100644 index c128c9e1a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-tree@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-tree_pine.png b/data/voxedit-ui/ui/skin/voxedit-tree_pine.png deleted file mode 100644 index ae98d1930..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-tree_pine.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-tree_pine@288.png b/data/voxedit-ui/ui/skin/voxedit-tree_pine@288.png deleted file mode 100644 index c128c9e1a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-tree_pine@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-undo-redo.png b/data/voxedit-ui/ui/skin/voxedit-undo-redo.png deleted file mode 100644 index e4ea4bcc7..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-undo-redo.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-undo-redo@288.png b/data/voxedit-ui/ui/skin/voxedit-undo-redo@288.png deleted file mode 100644 index 6816217eb..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-undo-redo@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-up.png b/data/voxedit-ui/ui/skin/voxedit-up.png deleted file mode 100644 index b06ca9de9..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-up.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-up@288.png b/data/voxedit-ui/ui/skin/voxedit-up@288.png deleted file mode 100644 index a92b28713..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-up@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-visible-selected.png b/data/voxedit-ui/ui/skin/voxedit-visible-selected.png deleted file mode 100644 index 2b2df955a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-visible-selected.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-visible-selected@288.png b/data/voxedit-ui/ui/skin/voxedit-visible-selected@288.png deleted file mode 100644 index 6e3add499..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-visible-selected@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-visible.png b/data/voxedit-ui/ui/skin/voxedit-visible.png deleted file mode 100644 index 9d70d6b5d..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-visible.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-visible@288.png b/data/voxedit-ui/ui/skin/voxedit-visible@288.png deleted file mode 100644 index 36a19ced2..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-visible@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-voxel.png b/data/voxedit-ui/ui/skin/voxedit-voxel.png deleted file mode 100644 index f33bc824a..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-voxel.png and /dev/null differ diff --git a/data/voxedit-ui/ui/skin/voxedit-voxel@288.png b/data/voxedit-ui/ui/skin/voxedit-voxel@288.png deleted file mode 100644 index 919dc5fb4..000000000 Binary files a/data/voxedit-ui/ui/skin/voxedit-voxel@288.png and /dev/null differ diff --git a/data/voxedit-ui/ui/widget/voxedit-layer-item.tb.txt b/data/voxedit-ui/ui/widget/voxedit-layer-item.tb.txt deleted file mode 100644 index f7caa30fd..000000000 --- a/data/voxedit-ui/ui/widget/voxedit-layer-item.tb.txt +++ /dev/null @@ -1,5 +0,0 @@ -TBCheckBox: id: visible, skin: voxedit-visible-button -TBCheckBox: id: locked, skin: voxedit-lock-layer-button -TBLayout: axis: y, position: left, spacing: 0, ignore-input: 1, size: available, position: left top - TBTextField: id: name, text-align: left -TBButton: id: delete, skin: TBWindow.close diff --git a/data/voxedit-ui/ui/widget/voxedit-layer.tb.txt b/data/voxedit-ui/ui/widget/voxedit-layer.tb.txt deleted file mode 100644 index d34ddd528..000000000 --- a/data/voxedit-ui/ui/widget/voxedit-layer.tb.txt +++ /dev/null @@ -1,27 +0,0 @@ -TBLayout - lp: width: 200 - axis y - distribution gravity - gravity all - TBLayout - distribution gravity - TBSelectList - id list - gravity all - TBLayout - distribution gravity - TBCheckBox - id anim - skin voxedit-anim-button - command animate - TBButton - command layermovedown - skin voxedit-down - TBButton - command layermoveup - skin voxedit-up - TBWidget - gravity left right - TBButton - id layeradd - skin voxedit-add diff --git a/data/voxedit-ui/ui/window/voxedit-layer-move.tb.txt b/data/voxedit-ui/ui/window/voxedit-layer-move.tb.txt deleted file mode 100644 index 02384e318..000000000 --- a/data/voxedit-ui/ui/window/voxedit-layer-move.tb.txt +++ /dev/null @@ -1,14 +0,0 @@ -TBLayout: distribution: available, size: available, axis: y - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-x - TBInlineSelect: id: move.x - lp: width: 120 - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-y - TBInlineSelect: id: move.y - lp: width: 120 - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-z - TBInlineSelect: id: move.z - lp: width: 120 - TBLayout: distribution-position: right bottom, id: buttons diff --git a/data/voxedit-ui/ui/window/voxedit-layer-rename.tb.txt b/data/voxedit-ui/ui/window/voxedit-layer-rename.tb.txt deleted file mode 100644 index 65eb245c0..000000000 --- a/data/voxedit-ui/ui/window/voxedit-layer-rename.tb.txt +++ /dev/null @@ -1,5 +0,0 @@ -TBLayout: axis: y, distribution: available, size: available - TBClickLabel: text: Name - TBEditField: id: name - lp: width: 100 - TBLayout: distribution-position: right bottom, id: buttons diff --git a/data/voxedit-ui/ui/window/voxedit-layer.tb.txt b/data/voxedit-ui/ui/window/voxedit-layer.tb.txt deleted file mode 100644 index 66f487027..000000000 --- a/data/voxedit-ui/ui/window/voxedit-layer.tb.txt +++ /dev/null @@ -1,23 +0,0 @@ -TBLayout: axis: y, distribution: available, size: available - TBClickLabel: text: Position - TBLayout: axis: x - TBEditField: type: number, id: pos.x - lp: width: 60 - TBEditField: type: number, id: pos.y - lp: width: 60 - TBEditField: type: number, id: pos.z - lp: width: 60 - TBClickLabel: text: Size - TBLayout: axis: x - TBEditField: type: number, id: size.x - lp: width: 60 - TBEditField: type: number, id: size.y - lp: width: 60 - TBEditField: type: number, id: size.z - lp: width: 60 - TBSeparator - TBClickLabel: text: Name - TBEditField: id: name - lp: width: 100 - TBEditField: id: note, size: available, readonly: 1, multiline: 1, multiline: 1, wrapping: 1 - TBLayout: distribution-position: right bottom, id: buttons diff --git a/data/voxedit-ui/ui/window/voxedit-main.tb.txt b/data/voxedit-ui/ui/window/voxedit-main.tb.txt deleted file mode 100644 index ee3d66e73..000000000 --- a/data/voxedit-ui/ui/window/voxedit-main.tb.txt +++ /dev/null @@ -1,378 +0,0 @@ -WindowInfo - fullscreen 1 - -definitions - menubutton - lp: height: 28 - skin TBButton.flat - menutextfield - lp: height: 28 - gravity left right - text-align right - type number - toolbutton - group-id toolgroup - cursorbutton - group-id cursorgroup - selectbutton - group-id selectgroup - -shaderselection - TBSelectDropdown: gravity: right top, @include: definitions>menubutton, id: shader - items - item: text: "Normal", id: 0 - item: text: "Edge", id: 1 - -cameramode - TBSelectDropdown: gravity: right bottom, @include: definitions>menubutton, id: cammode - items - item: text: "Solid", id: 0 - item: text: "Points", id: 1 - item: text: "Lines", id: 2 - -camerarottype - TBSelectDropdown: gravity: left bottom, @include: definitions>menubutton, id: camrottype - items - item: text: "Reference Point", id: 0 - item: text: "Eye", id: 1 - -TBLayout: distribution: gravity, axis: y - TBContainer: gravity: all, id: maincontainer - TBLayout: distribution: gravity, axis: y - TBLayout: distribution: gravity - TBContainer: skin: container, gravity: left right - TBLayout: distribution: gravity - TBButton: gravity: left, @include: definitions>menubutton, text: File, id: menu_file - TBSkinImage: skin: voxedit-load - TBButton: gravity: left, @include: definitions>menubutton, text: Undo, id: undo, command: undo - TBSkinImage: skin: voxedit-undo - TBButton: gravity: left, @include: definitions>menubutton, text: Redo, id: redo, command: redo - TBSkinImage: skin: voxedit-redo - TBButton: gravity: left, @include: definitions>menubutton, text: Settings, id: scene_settings_open - TBSkinImage: skin: voxedit-options - TBButton: gravity: left, @include: definitions>menubutton, text: Trees, id: show_tree_panel - TBSkinImage: skin: voxedit-tree - TBButton: gravity: left, @include: definitions>menubutton, text: Scripts, id: show_script_panel - TBSkinImage: skin: voxedit-script - TBButton: gravity: left, @include: definitions>menubutton, text: Noise, id: show_noise_panel - TBButton: gravity: left, @include: definitions>menubutton, text: L-System, id: show_lsystem_panel - - TBLayout: gravity: left right - TBWidget - TBLayout: distribution: gravity, position: top - TBLayout: distribution: gravity, axis: y, position: left, gravity: top bottom, skin: no-padding-container - TBSection: value: 1, text: "Palette" - TBLayout: position: left top, axis: y - lp: padding: 0 - PaletteWidget: id: palettecontainer, width: 18, height: 18, amount-x: 14, connection: current-voxel-color - TBLayout: position: right top, axis: x - lp: padding: 0 - TBButton: @include: definitions>menubutton, text: Import, command: "importpalette" - TBSkinImage: skin: voxedit-import-palette - TBButton: @include: definitions>menubutton, text: Load, id: loadpalette - TBSkinImage: skin: voxedit-load - TBLayout: position: right top, axis: x - lp: padding: 0 - TBButton: @include: definitions>menubutton, text: "Extract color", command: colortolayer - TBEditField: id: paletteindex, placeholder: index, gravity: left: right, text-align: left, readonly: 1 - lp: height: 28 - - TBSection: value: 1, text: "Tools", is-group-root: 1 - TBLayout: position: left top, axis: y - TBClickLabel - text Place - TBSkinImage: skin: voxedit-voxel - TBRadioButton - id actionplace - value 1 - @include definitions>toolbutton - TBClickLabel - text Select - TBSkinImage: skin: voxedit-voxel - TBRadioButton - id actionselect - @include definitions>toolbutton - TBClickLabel - text Delete - TBSkinImage: skin: voxedit-delete - TBRadioButton - id actiondelete - @include definitions>toolbutton - TBClickLabel - text Override - TBSkinImage: skin: voxedit-override - TBRadioButton - id actionoverride - @include definitions>toolbutton - TBClickLabel - text Colorize - TBSkinImage: skin: voxedit-color - TBRadioButton - id actioncolorize - @include definitions>toolbutton - TBSelectDropdown: gravity: right top, @include: definitions>menubutton, id: shapetype - items - item: text: "Cube", id: 0 - item: text: "Torus", id: 1 - item: text: "Cylinder", id: 2 - item: text: "Cone", id: 3 - item: text: "Dome", id: 4 - item: text: "Ellipse", id: 5 - - TBSeparator: gravity: left right, skin: separator - - TBWidget: gravity: top bottom - - TBLayout: distribution: gravity, axis: y - TBLayout: distribution: gravity, id: editorcontainer - TBLayout: distribution: gravity, axis: y - TBLayout: distribution: gravity, axis: x - Viewport: id: editorscene, gravity: left right top bottom, skin: no-padding-container, camera: free, want-focus-on-hover: 1 - @include cameramode - @include camerarottype - @include shaderselection - Viewport: id: editorscenetop, gravity: left right top bottom, skin: no-padding-container, camera: top, visibility: gone, want-focus-on-hover: 1 - @include cameramode - @include camerarottype - @include shaderselection - TBLayout: distribution: gravity, skin: no-padding-container, id: animationwidget, gravity: left right top bottom, visibility: gone, axis: y - TBSelectDropdown: text: Animations, gravity: right top, skin: no-padding-container, @include: definitions>menubutton, id: animationlist - Viewport: id: animationscene, gravity: left right top bottom, skin: no-padding-container, camera: free, mode: animation, want-focus-on-hover: 1 - @include cameramode - @include camerarottype - TBLayout: distribution: gravity, skin: no-padding-container, id: animationsettings, gravity: left right top bottom, axis: y - lp: height: 400 - TBLayout: distribution: gravity, axis: x - Viewport: id: editorsceneleft, gravity: left right top bottom, skin: no-padding-container, camera: left, visibility: gone, want-focus-on-hover: 1 - @include cameramode - @include camerarottype - @include shaderselection - Viewport: id: editorscenefront, gravity: left right top bottom, skin: no-padding-container, camera: front, visibility: gone, want-focus-on-hover: 1 - @include cameramode - @include camerarottype - @include shaderselection - - TBLayout: distribution: gravity, axis: y, position: right, gravity: top bottom, skin: no-padding-container, id: treesection, visibility: gone - TBTextField: gravity: left right, skin: Header, text: Trees - TBLayout: position: left top, axis: y, distribution: gravity - TBSelectDropdown: gravity: left right, id: treetype - - TBLayout: position: left top, axis: y, id: treeparameterlayout, distribution: gravity - - TBClickLabel: text: Auto generate - TBCheckBox: id: treeautogen, value: 0 - - TBButton: text: Generate, id: treegenerate, gravity: left right - - TBWidget: gravity: top bottom - - TBLayout: distribution: gravity, axis: y, position: right, gravity: top bottom, skin: no-padding-container, id: scriptsection, visibility: gone - TBTextField: gravity: left right, skin: Header, text: Scripts - TBLayout: position: left top, axis: y, distribution: gravity - TBSelectDropdown: gravity: left right, id: scripttype - - TBLayout: position: left top, axis: y, id: scriptparameterslayout, distribution: gravity - - TBButton: text: Execute, id: scriptexecute, gravity: left right - - TBWidget: gravity: top bottom - - TBLayout: distribution: gravity, axis: y, position: right, gravity: top bottom, skin: no-padding-container, id: lsystemsection, visibility: gone - lp: min-width: 350 - - TBTextField: gravity: left right, skin: Header, text: L-System - TBLayout: position: left top, axis: y, distribution: gravity - TBEditField: multiline: 1, styling: 1, gravity: all, readonly: 1, skin: TBTextField - text: "F: Draw line forwards with current step width\n" \ - "b: Move backward without drawing\n" \ - "L: Draw leaves\n" \ - "+: Rotate right\n" \ - "-: Rotate left\n" \ - ">: Rotate forward\n" \ - "<: Rotate backward\n" \ - "#: Increment width\n" \ - "[: Push\n" \ - "]: Pop\n" \ - "(colorindex): Set voxel type to palette index\n" \ - "\n" - - TBTextField: text: Axiom, gravity: left right - TBEditField: id: lsystem_axiom, placeholder: Axiom, text: "F", gravity: left right - - TBTextField: text: Rules, gravity: left right - TBEditField: multiline: 1, id: lsystem_rules, gravity: all - text: "{\n" \ - " F\n" \ - " (67)F+[!+F-F-F(37)L]-[!-F+F+F(142)L]>[!FF>F(123)L]\n" \ - "}\n" - - TBLayout: position: left top, axis: x, distribution: gravity - TBTextField: text: Angle, gravity: left right - TBInlineSelectDouble: id: lsystem_angle, value: 22.5, position: right - - TBLayout: position: left top, axis: x, distribution: gravity - TBTextField: text: Length, gravity: left right - TBInlineSelectDouble: id: lsystem_length, value: 12.0, position: right - - TBLayout: position: left top, axis: x, distribution: gravity - TBTextField: text: Width, gravity: left right - TBInlineSelectDouble: id: lsystem_width, value: 4.0, position: right - - TBLayout: position: left top, axis: x, distribution: gravity - TBTextField: text: WidthIncrement, gravity: left right - TBInlineSelectDouble: id: lsystem_widthincrement, value: 1.5, position: right - - TBLayout: position: left top, axis: x, distribution: gravity - TBTextField: text: Leaves radius, gravity: left right - TBInlineSelectDouble: id: lsystem_leavesradius, value: 8.0, position: right - - TBLayout: position: left top, axis: x, distribution: gravity - TBTextField: text: Iterations, gravity: left right - TBInlineSelect: id: lsystem_iterations, value: 2, position: right - - TBButton: text: Generate, id: lsystemgenerate, gravity: left right - - TBLayout: distribution: gravity, axis: y, position: right, gravity: top bottom, skin: no-padding-container, id: noisesection, visibility: gone - TBTextField: gravity: left right, skin: Header, text: Noise - TBLayout: position: left top, axis: y, distribution: gravity - TBTextField: text: Octaves, gravity: left right - TBInlineSelect: id: noise_octaves, value: 4, gravity: left right - - TBTextField: text: Frequency, gravity: left right - TBInlineSelectDouble: id: noise_frequency, value: 0.01, gravity: left right - - TBTextField: text: Offset, gravity: left right - TBInlineSelectDouble: id: noise_offset, value: 1.0, gravity: left right - - TBTextField: text: Gain, gravity: left right - TBInlineSelectDouble: id: noise_gain, value: 0.5, gravity: left right - - TBTextField: text: Lacunarity, gravity: left right - TBInlineSelectDouble: id: noise_lacunarity, value: 2.0, gravity: left right - - TBButton: text: Generate, id: noisegenerate, gravity: left right - - TBWidget: gravity: top bottom - - TBLayout: distribution: gravity, axis: y, position: left, gravity: top bottom, skin: no-padding-container - - TBSection: value: 1, text: "Operations" - TBLayout: position: left top, axis: y - TBButton: @include: definitions>menubutton, text: Crop, id: crop, command: "crop" - TBSkinImage: skin: voxedit-crop - TBButton: @include: definitions>menubutton, text: Extend, id: extend, command: "resize", auto-repeat: 1 - TBSkinImage: skin: voxedit-extend - TBButton: @include: definitions>menubutton, text: Layer from color, id: colortolayer, command: "colortolayer" - TBButton: @include: definitions>menubutton, text: Scale, id: scale, command: "scale" - - TBSection: value: 1, text: "Translate" - TBLayout: position: left top, axis: y, distribution: gravity - TBInlineSelect: id: translatex, min: -128, max: 128, value: 0, gravity: left right - TBInlineSelect: id: translatey, min: -128, max: 128, value: 0, gravity: left right - TBInlineSelect: id: translatez, min: -128, max: 128, value: 0, gravity: left right - TBLayout: position: left top, axis: x - TBButton: @include: definitions>menubutton, text: Volumes, id: shiftvolumes - TBSkinImage: skin: voxedit-shift - TBButton: @include: definitions>menubutton, text: Voxels, id: movevoxels - TBSkinImage: skin: voxedit-move - - TBSection: value: 1, text: "Cursor" - TBLayout: position: left top, axis: y, distribution: gravity - TBLayout: axis: x - TBEditField: @include: definitions>menutextfield, id: cursorx, placeholder: x - TBClickLabel - TBSkinImage: skin: voxedit-axis-x - TBCheckBox: command: lockx - TBLayout: axis: x - TBEditField: @include: definitions>menutextfield, id: cursory, placeholder: y - TBClickLabel - TBSkinImage: skin: voxedit-axis-y - TBCheckBox: command: locky - TBLayout: axis: x - TBEditField: @include: definitions>menutextfield, id: cursorz, placeholder: z - TBClickLabel - TBSkinImage: skin: voxedit-axis-z - TBCheckBox: command: lockz - - TBSection: value: 1, text: "Rotate on axis" - TBLayout: position: left top, axis: x - TBButton: id: rotatex, @include: definitions>menubutton, command: "rotate 90 0 0" - TBSkinImage: skin: voxedit-axis-x - TBButton: id: rotatey, @include: definitions>menubutton, command: "rotate 0 90 0" - TBSkinImage: skin: voxedit-axis-y - TBButton: id: rotatez, @include: definitions>menubutton, command: "rotate 0 0 90" - TBSkinImage: skin: voxedit-axis-z - - TBSection: value: 1, text: "Flip on axis" - TBLayout: position: left top, axis: x - TBButton: id: mirrorx, @include: definitions>menubutton, command: "flip x" - TBSkinImage: skin: voxedit-axis-x - TBButton: id: mirrory, @include: definitions>menubutton, command: "flip y" - TBSkinImage: skin: voxedit-axis-y - TBButton: id: mirrorz, @include: definitions>menubutton, command: "flip z" - TBSkinImage: skin: voxedit-axis-z - - TBSection: value: 1, text: "Mirror axis" - TBLayout: position: left top, axis: x - TBClickLabel: text: none - TBRadioButton: group-id: mirrorgroup, id: mirroraxisnone - TBClickLabel: text: x - TBRadioButton: group-id: mirrorgroup, id: mirroraxisx - TBClickLabel: text: y - TBRadioButton: group-id: mirrorgroup, id: mirroraxisy - TBClickLabel: text: z - TBRadioButton: group-id: mirrorgroup, id: mirroraxisz - - TBTextField: gravity: left right, skin: Header, text: Layers - LayerWidget: id: layercontainer, gravity: top bottom - - TBSection: value: 0, text: "Options" - TBLayout: position: left top, axis: y - TBClickLabel: text: Show axis - TBCheckBox: id: optionshowaxis, value: 1, varref: ve_showaxis - TBClickLabel: text: Model space - TBCheckBox: id: optionaxismodelspace, value: 1, varref: ve_modelspace - TBClickLabel: text: Show locked axis - TBCheckBox: id: optionshowlockaxis, value: 1, varref: ve_showlockedaxis - TBClickLabel: text: Bounding box - TBCheckBox: id: optionshowaabb, value: 0, varref: ve_showaabb - TBClickLabel: text: Shadow - TBCheckBox: id: optionrendershadow, value: 1, varref: ve_rendershadow - TBClickLabel: text: Outlines - TBCheckBox: id: optionoutline, value: 1, varref: r_renderoutline - TBLayout: axis: y - TBTextField: text: Animation speed - TBEditField: varref: ve_animspeed - - TBLayout: distribution: gravity - TBContainer: skin: container, gravity: left right - TBLayout: distribution: gravity - TBLayout: gravity: left - TBTextField: id: status, text: - - TBWidget - lp: width: 40 - TBTextField: id: dimension, text: - - TBLayout: gravity: left right - TBWidget - TBClickLabel: text: Grid - TBCheckBox: id: optionshowgrid, value: 1, skin: voxedit-grid-button, varref: ve_showgrid - TBWidget - lp: width: 10 - TBClickLabel: text: Grid size - TBInlineSelect: id: optionvoxelsize, min: 1, max: 64, value: 4, varref: ve_gridsize - TBWidget - lp: width: 10 - TBButton: @include: definitions>menubutton, text: Reset View, id: resetcamera, command: resetcamera - TBSkinImage: skin: voxedit-reset-camera - TBWidget - lp: width: 10 - TBClickLabel: text: Quad view - TBCheckBox: id: toggleviewport, skin: voxedit-quadview-button, command: toggleviewport - TBWidget - lp: width: 10 - TBClickLabel: text: Animation view, id: toggleanimationlabel - TBCheckBox: id: toggleanimation, skin: voxedit-animation-button, command: toggleanimation - TBWidget - lp: width: 10 - TBClickLabel: text: Scene view, id: togglescenelabel - TBCheckBox: id: togglescene, skin: voxedit-scene-button, command: togglescene diff --git a/data/voxedit-ui/ui/window/voxedit-palette-selector.tb.txt b/data/voxedit-ui/ui/window/voxedit-palette-selector.tb.txt deleted file mode 100644 index 13b46af16..000000000 --- a/data/voxedit-ui/ui/window/voxedit-palette-selector.tb.txt +++ /dev/null @@ -1,13 +0,0 @@ -WindowInfo - title Select existing palette - position 500 0 - -TBLayout: distribution: gravity, axis: y - TBSelectList - id palettes - gravity all - - TBSeparator: gravity: left right, skin: separator - TBLayout: distribution: gravity, axis: x - TBButton: text: Ok, id: ok, autofocus: 1, gravity: left right, size: gravity - TBButton: text: Cancel, id: cancel, gravity: left right, size: gravity diff --git a/data/voxedit-ui/ui/window/voxedit-scene-settings.tb.txt b/data/voxedit-ui/ui/window/voxedit-scene-settings.tb.txt deleted file mode 100644 index 90690dac6..000000000 --- a/data/voxedit-ui/ui/window/voxedit-scene-settings.tb.txt +++ /dev/null @@ -1,80 +0,0 @@ -lighting_settings - TBContainer - TBLayout: axis: y - TBTextField: text: Sun position - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-x - TBInlineSelect: id: position.x - lp: width: 120 - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-y - TBInlineSelect: id: position.y - lp: width: 120 - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-z - TBInlineSelect: id: position.z - lp: width: 120 - TBContainer - TBLayout: axis: y - TBTextField: text: Sun angle - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-x - TBInlineSelect: id: direction.x - lp: width: 120 - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-y - TBInlineSelect: id: direction.y - lp: width: 120 - TBLayout: axis: x - TBSkinImage: skin: voxedit-axis-z - TBInlineSelect: id: direction.z - lp: width: 120 - TBContainer - TBLayout: axis: y - TBTextField: text: Ambient color - TBLayout: axis: x - TBInlineSelect: id: ambient.r - min: 0 - max: 255 - lp: width: 120 - TBLayout: axis: x - TBInlineSelect: id: ambient.g - min: 0 - max: 255 - lp: width: 120 - TBLayout: axis: x - TBInlineSelect: id: ambient.b - min: 0 - max: 255 - lp: width: 120 - TBContainer - TBLayout: axis: y - TBTextField: text: Diffuse color - TBLayout: axis: x - TBInlineSelect: id: diffuse.r - min: 0 - max: 255 - lp: width: 120 - TBLayout: axis: x - TBInlineSelect: id: diffuse.g - min: 0 - max: 255 - lp: width: 120 - TBLayout: axis: x - TBInlineSelect: id: diffuse.b - min: 0 - max: 255 - lp: width: 120 - -TBLayout: axis: y - TBTabContainer - gravity all - id tabcontainer - tabs - TBButton: text: "Lighting" - TBLayout - @include lighting_settings - axis y - distribution-position top - - TBLayout: distribution-position: right bottom, id: buttons diff --git a/docs/VisualTests.md b/docs/VisualTests.md index 5a4aad37e..6e30e6f7d 100644 --- a/docs/VisualTests.md +++ b/docs/VisualTests.md @@ -80,10 +80,6 @@ Uses GLSL compute shader to render a circle. Conversion of OpenCL marching cubes taken from: -## testturbobadger - -Renders the turbobadger demo. - ## testluaui Test the nuklear lua ui binding diff --git a/src/modules/ui/CMakeLists.txt b/src/modules/ui/CMakeLists.txt index f1aa358d7..0e6f56465 100644 --- a/src/modules/ui/CMakeLists.txt +++ b/src/modules/ui/CMakeLists.txt @@ -1,3 +1,2 @@ add_subdirectory(imgui) -add_subdirectory(turbobadger) add_subdirectory(nuklear) diff --git a/src/modules/ui/turbobadger/CMakeLists.txt b/src/modules/ui/turbobadger/CMakeLists.txt deleted file mode 100644 index 585f42330..000000000 --- a/src/modules/ui/turbobadger/CMakeLists.txt +++ /dev/null @@ -1,223 +0,0 @@ -set(LIB turbobadger) -set(SRCS - tb/animation/tb_animation_utils.h - tb/animation/tb_animation.cpp - tb/animation/tb_widget_animation.h - tb/animation/tb_animation.h - tb/animation/tb_widget_animation.cpp - tb/utf8/utf8.cpp - tb/utf8/utf8.h - tb/renderers/tb_renderer_batcher.cpp - tb/renderers/tb_renderer_batcher.h - tb/thirdparty/stb_truetype.h - tb/parser/tb_parser.h - tb/parser/tb_parser.cpp - tb/image/tb_image_manager.cpp - tb/image/tb_image_widget.cpp - tb/image/tb_image_widget.h - tb/image/tb_image_manager.h - tb/tb_bitmap_fragment.cpp - tb/tb_color.cpp - tb/tb_core.cpp - tb/tb_debug.cpp - tb/tb_dimension.cpp - tb/tb_editfield.cpp - tb/tb_font_renderer.cpp - tb/tb_font_renderer_stb.cpp - tb/tb_font_renderer_tbbf.cpp - tb/tb_geometry.cpp - tb/tb_hashtable.cpp - tb/tb_inline_select.cpp - tb/tb_language.cpp - tb/tb_layout.cpp - tb/tb_linklist.cpp - tb/tb_list.cpp - tb/tb_menu_window.cpp - tb/tb_message_window.cpp - tb/tb_msg.cpp - tb/tb_node_ref_tree.cpp - tb/tb_node_tree.cpp - tb/tb_popup_window.cpp - tb/tb_renderer.cpp - tb/tb_scroll_container.cpp - tb/tb_scroller.cpp - tb/tb_select.cpp - tb/tb_select_item.cpp - tb/tb_skin.cpp - tb/tb_skin_util.cpp - tb/tb_str.cpp - tb/tb_style_edit.cpp - tb/tb_style_edit_content.cpp - tb/tb_system.cpp - tb/tb_tab_container.cpp - tb/tb_tempbuffer.cpp - tb/tb_toggle_container.cpp - tb/tb_value.cpp - tb/tb_widget_skin_condition_context.cpp - tb/tb_widget_value.cpp - tb/tb_widgets.cpp - tb/tb_widgets_common.cpp - tb/tb_widgets_listener.cpp - tb/tb_widgets_reader.cpp - tb/tb_window.cpp - tb/tb_bitmap_fragment.h - tb/tb_color.h - tb/tb_config.h - tb/tb_core.h - tb/tb_debug.h - tb/tb_dimension.h - tb/tb_editfield.h - tb/tb_font_desc.h - tb/tb_font_renderer.h - tb/tb_geometry.h - tb/tb_hash.h - tb/tb_hashtable.h - tb/tb_id.h - tb/tb_inline_select.h - tb/tb_language.h - tb/tb_layout.h - tb/tb_linklist.h - tb/tb_list.h - tb/tb_menu_window.h - tb/tb_message_window.h - tb/tb_msg.h - tb/tb_node_ref_tree.h - tb/tb_node_tree.h - tb/tb_object.h - tb/tb_popup_window.h - tb/tb_renderer.h - tb/tb_scroll_container.h - tb/tb_scroller.h - tb/tb_select.h - tb/tb_select_item.h - tb/tb_skin.h - tb/tb_skin_util.h - tb/tb_sort.h - tb/tb_str.h - tb/tb_style_edit.h - tb/tb_style_edit_content.h - tb/tb_system.h - tb/tb_tab_container.h - tb/tb_tempbuffer.h - tb/tb_toggle_container.h - tb/tb_types.h - tb/tb_value.h - tb/tb_widget_skin_condition_context.h - tb/tb_widget_value.h - tb/tb_widgets.h - tb/tb_widgets_common.h - tb/tb_widgets_listener.h - tb/tb_widgets_reader.h - tb/tb_window.h - - UIApp.cpp UIApp.h - TurboBadger.h - Window.cpp Window.h - Widget.h - FontUtil.h - FileDialogWindow.h FileDialogWindow.cpp - WaitingMessage.h WaitingMessage.cpp - Console.cpp Console.h - ui_renderer_gl.cpp ui_renderer_gl.h - ui_widgets.cpp ui_widgets.h -) - -set(IMAGES - shared/ui/skin/arrow_down - shared/ui/skin/arrow_left - shared/ui/skin/arrow_right - shared/ui/skin/arrow_up - shared/ui/skin/button_flat_outline - shared/ui/skin/button_flat_pressed - shared/ui/skin/button_grouped_x_first_down - shared/ui/skin/button_grouped_x_first_up - shared/ui/skin/button_grouped_x_last_down - shared/ui/skin/button_grouped_x_last_up - shared/ui/skin/button_grouped_x_middle_down - shared/ui/skin/button_grouped_x_middle_up - shared/ui/skin/button - shared/ui/skin/button_pressed - shared/ui/skin/checkbox_mark - shared/ui/skin/checkbox - shared/ui/skin/checkbox_pressed - shared/ui/skin/container - shared/ui/skin/editfield - shared/ui/skin/fadeout_x - shared/ui/skin/fadeout_y - shared/ui/skin/filedialog-dir - shared/ui/skin/filedialog-file - shared/ui/skin/focus_r4 - shared/ui/skin/focus_tabbutton_left - shared/ui/skin/focus_tabbutton_top - shared/ui/skin/item_hover - shared/ui/skin/item_selected - shared/ui/skin/item_separator_x - shared/ui/skin/item_separator_y - shared/ui/skin/progress_spinner_strip - shared/ui/skin/radio_mark - shared/ui/skin/radio - shared/ui/skin/radio_pressed - shared/ui/skin/remove - shared/ui/skin/resizer - shared/ui/skin/scroll_bg_x - shared/ui/skin/scroll_bg_y - shared/ui/skin/scroll_fg_x - shared/ui/skin/scroll_fg_y - shared/ui/skin/search - shared/ui/skin/section_container - shared/ui/skin/selection - shared/ui/skin/slider_bg_x - shared/ui/skin/slider_bg_y - shared/ui/skin/slider_handle - shared/ui/skin/tab_button_bottom_active - shared/ui/skin/tab_button_bottom_inactive - shared/ui/skin/tab_button_left_active - shared/ui/skin/tab_button_left_inactive - shared/ui/skin/tab_button_right_active - shared/ui/skin/tab_button_right_inactive - shared/ui/skin/tab_button_top_active - shared/ui/skin/tab_button_top_inactive - shared/ui/skin/toggle_section_icon_down - shared/ui/skin/toggle_section_icon_middle - shared/ui/skin/toggle_section_icon_up - shared/ui/skin/window_active - shared/ui/skin/window_close - shared/ui/skin/window_close_pressed - shared/ui/skin/window_mover_bg_tile - shared/ui/skin/window_mover_overlay - shared/ui/skin/window -) - -set(FILES - shared/ui/skin/skin.tb.txt - shared/ui/font/font_14.png - shared/ui/font/font_28.png - shared/ui/font/font.tb.txt - shared/ui/font/DejaVuSansMono.ttf - shared/ui/window/filedialog_dir.tb.txt - shared/ui/window/filedialog_file.tb.txt - shared/ui/window/filedialog.tb.txt -) - -foreach(IMAGE ${IMAGES}) - list(APPEND FILES ${IMAGE}.png) -endforeach() -set(DPIS "@288") -foreach(DPI ${DPIS}) - foreach(IMAGE ${IMAGES}) - list(APPEND FILES ${IMAGE}${DPI}.png) - endforeach() -endforeach() - -if (WINDOWS) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) - add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) - add_definitions(-DTB_USE_CURRENT_DIRECTORY) -elseif (LINUX) -elseif (DARWIN) - add_definitions(-DMACOSX) - add_definitions(-DTB_USE_CURRENT_DIRECTORY) -endif() - -engine_add_module(TARGET ${LIB} SRCS ${SRCS} FILES ${FILES} DEPENDENCIES imgui app render) -target_include_directories(${LIB} PUBLIC tb) diff --git a/src/modules/ui/turbobadger/Console.cpp b/src/modules/ui/turbobadger/Console.cpp deleted file mode 100644 index 30eb9fd74..000000000 --- a/src/modules/ui/turbobadger/Console.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @file - */ - -#include "Console.h" -#include "TurboBadger.h" -#include "FontUtil.h" -#include "core/Color.h" -#include "UIApp.h" -#include - -namespace ui { -namespace turbobadger { - -Console::Console() : - Super() { -} - -void Console::construct() { - Super::construct(); - _consoleAlpha = core::Var::get("ui_consolealpha", "0.9", "Console background alpha value between 0.0 and 1.0"); - _consoleBackground = core::Var::get("ui_consolebackground", "0.1", "Console background gray color value between 0.0 and 1.0"); - _consoleFontSize = core::Var::get("ui_consolefontsize", "14", "Console font size"); -} - -bool Console::init() { - if (!Super::init()) { - return false; - } - _font = getMonoSpaceFont(_consoleFontSize->intVal()); - return true; -} - -bool Console::toggle() { - const bool active = Super::toggle(); - if (active) { - SDL_StartTextInput(); - } else { - SDL_StopTextInput(); - } - return active; -} - -void Console::drawString(int x, int y, const glm::ivec4& color, int, const char* str, int len) { - _font->drawString(x, y, tb::TBColor{color.r, color.g, color.b}, str, len); -} - -void Console::beforeRender(const math::Rect &rect) { - _consoleFontSize->setVal(_fontSize); - if (_consoleFontSize->isDirty()) { - _font = getMonoSpaceFont(_consoleFontSize->intVal()); - _consoleFontSize->markClean(); - } - const tb::TBRect r(rect.getMinX(), rect.getMinZ(), rect.getMaxX(), rect.getMaxZ()); - const int c = glm::clamp((int)(_consoleBackground->floatVal() * core::Color::magnitudef), 0, 255); - const int a = glm::clamp((int)(_consoleAlpha->floatVal() * core::Color::magnitudef), 0, 255); - const tb::TBColor consoleBgColor(c, c, c, a); - tb::g_tb_skin->paintRectFill(r, consoleBgColor); - char buf[64]; - SDL_snprintf(buf, sizeof(buf), "FPS: %f", UIApp::fps()); - const int bufSize = SDL_strlen(buf); - const int length = _font->getStringWidth(buf, bufSize); - _font->drawString(rect.getMaxX() - length, 0, tb::TBColor{255, 255, 255}, buf, bufSize); -} - -int Console::lineHeight() { - const int lineHeight = _font->getFontDescription().getSize(); - return lineHeight; -} - -glm::ivec2 Console::stringSize(const char* s, int length) { - return glm::ivec2(_font->getStringWidth(s, length), lineHeight()); -} - -} -} diff --git a/src/modules/ui/turbobadger/Console.h b/src/modules/ui/turbobadger/Console.h deleted file mode 100644 index 552447415..000000000 --- a/src/modules/ui/turbobadger/Console.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "util/Console.h" -#include "core/Var.h" - -namespace tb { -class TBFontFace; -} - -namespace ui { -namespace turbobadger { - -class Console : public util::Console { -private: - using Super = util::Console; - tb::TBFontFace *_font = nullptr; - core::VarPtr _consoleAlpha; - core::VarPtr _consoleBackground; - core::VarPtr _consoleFontSize; - - void drawString(int x, int y, const glm::ivec4& color, int, const char* str, int len) override; - int lineHeight() override; - glm::ivec2 stringSize(const char* s, int length) override; - void beforeRender(const math::Rect &rect) override; - -public: - Console(); - void construct() override; - bool init() override; - bool toggle() override; -}; - -} -} diff --git a/src/modules/ui/turbobadger/FileDialogWindow.cpp b/src/modules/ui/turbobadger/FileDialogWindow.cpp deleted file mode 100644 index 2556612e0..000000000 --- a/src/modules/ui/turbobadger/FileDialogWindow.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/** - * @file - */ - -#include "FileDialogWindow.h" -#include "core/StringUtil.h" -#include "ui/turbobadger/UIApp.h" -#include - -namespace ui { -namespace turbobadger { - -static const char *FILELIST = "files"; -static const char *DIRLIST = "dirs"; -static const char *FILTERLIST = "filter"; -static const char *INPUT = "input"; - -FileDialogItemWidget::FileDialogItemWidget(FileDialogItem *item) : tb::TBLayout() { - setSkinBg(TBIDC("TBSelectItem")); - setLayoutDistribution(tb::LAYOUT_DISTRIBUTION_GRAVITY); - setLayoutDistributionPosition(tb::LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); - setPaintOverflowFadeout(false); - - if (item->entry().type == io::Filesystem::DirEntry::Type::dir) { - tb::g_widgets_reader->loadFile(getContentRoot(), "ui/window/filedialog_dir.tb.txt"); - } else { - tb::g_widgets_reader->loadFile(getContentRoot(), "ui/window/filedialog_file.tb.txt"); - } - if (tb::TBTextField *name = getWidgetByIDAndType(TBIDC("name"))) { - name->setText(item->entry().name.c_str()); - } -} - -bool FileDialogItemSource::filterHidden(const io::Filesystem::DirEntry& entry) const { - if (_showHidden) { - return false; - } - if (entry.name == "..") { - return false; - } - return entry.name[0] == '.'; -} - -bool FileDialogItemSource::filter(int index, const char *filter) { - const FileDialogItem* item = getItem(index); - if (item == nullptr) { - return false; - } - - const io::Filesystem::DirEntry& entry = item->entry(); - - if (filterHidden(entry)) { - return false; - } - - // never filter directories if we want to see directories - because we - // always want to be able to switch to those directories - if (entry.type == io::Filesystem::DirEntry::Type::dir && _mode != video::WindowedApp::OpenFileMode::Directory) { - return true; - } - - return core::string::fileMatchesMultiple(item->str.c_str(), filter); -} - -tb::TBWidget *FileDialogItemSource::createItemWidget(int index, tb::TBSelectItemViewer *viewer) { - return new FileDialogItemWidget(getItem(index)); -} - -FileDialogWindow::FileDialogWindow(UIApp* tool, const std::function& callback, const core::VarPtr& lastDirectory) : - Super(tool), _callback(callback), _lastDirectory(lastDirectory) { - _fs = tool->filesystem(); - loadResourceFile("ui/window/filedialog.tb.txt"); - if (tb::TBSelectList * select = getWidgetByType(FILELIST)) { - select->setSource(&_entityList); - select->getScrollContainer()->setScrollMode(tb::SCROLL_MODE_X_AUTO_Y_AUTO); - } - if (tb::TBSelectList * select = getWidgetByType(DIRLIST)) { - select->setSource(&_dirList); - const io::Paths& paths = io::filesystem()->paths(); - for (const auto& p : paths) { - _dirList.addItem(new tb::TBGenericStringItem(p.c_str())); - } - } - if (tb::TBSelectDropdown * select = getWidgetByType(FILTERLIST)) { - select->setSource(&_filterList); - } - - _filterList.setSort(tb::TB_SORT_ASCENDING); - _directory = _fs->absolutePath("."); - setMode(video::WindowedApp::OpenFileMode::Open); -} - -FileDialogWindow::~FileDialogWindow() { - if (tb::TBSelectList *select = getWidgetByType(FILELIST)) { - select->setSource(nullptr); - } - if (tb::TBSelectDropdown * select = getWidgetByType(FILTERLIST)) { - select->setSource(nullptr); - } - if (tb::TBSelectList * select = getWidgetByType(DIRLIST)) { - select->setSource(nullptr); - } -} - -void FileDialogWindow::addShortcut(const core::String& dir) { - _dirList.addItem(new tb::TBGenericStringItem(dir.c_str())); -} - -void FileDialogWindow::setMode(video::WindowedApp::OpenFileMode mode, const char *inputText) { - _mode = mode; - _entityList.setMode(mode); - if (tb::TBEditField * input = getWidgetByType(INPUT)) { - if (_mode == video::WindowedApp::OpenFileMode::Save - || _mode == video::WindowedApp::OpenFileMode::Open) { - input->setVisibility(tb::WIDGET_VISIBILITY_VISIBLE); - input->setFocus(tb::WIDGET_FOCUS_REASON_UNKNOWN); - if (inputText != nullptr) { - input->setText(inputText); - } - if (_mode == video::WindowedApp::OpenFileMode::Save) { - input->setPlaceholderText(tr("Enter filename for saving")); - if (tb::TBButton * ok = getWidgetByType("ok")) { - ok->setState(tb::WIDGET_STATE_DISABLED, input->getText().empty()); - } - } else { - input->setPlaceholderText(tr("Enter filename for loading")); - if (tb::TBButton * ok = getWidgetByType("ok")) { - ok->setState(tb::WIDGET_STATE_DISABLED, false); - } - } - } else { - input->setVisibility(tb::WIDGET_VISIBILITY_GONE); - } - } -} - -void FileDialogWindow::setFilter(const char **filter) { - _filterList.deleteAllItems(); - tb::TBSelectDropdown * select = getWidgetByType(FILTERLIST); - if (filter == nullptr) { - if (select != nullptr) { - select->setVisibility(tb::WIDGET_VISIBILITY_INVISIBLE); - } - return; - } - for (const char** f = filter; *f; ++f) { - _filterList.addItem(new tb::TBGenericStringItem(*f)); - } - _filterList.addItem(new tb::TBGenericStringItem("*")); - if (select != nullptr && _filterList.getNumItems() > 0) { - select->setValue(0); - select->setVisibility(tb::WIDGET_VISIBILITY_VISIBLE); - } -} - -bool FileDialogWindow::onEvent(const tb::TBWidgetEvent &ev) { - if (ev.type == tb::EVENT_TYPE_CHANGED) { - if (ev.target->getID() == TBIDC(FILTERLIST)) { - if (tb::TBSelectList *select = getWidgetByType(FILELIST)) { - select->setFilter(ev.target->getText()); - return true; - } - } else if (ev.target->getID() == TBIDC(DIRLIST)) { - if (tb::TBGenericStringItem* item = _dirList.getItem(ev.target->getValue())) { - changeDir(item->str.c_str()); - if (tb::TBEditField * input = getWidgetByType(INPUT)) { - input->setText(_directory.c_str()); - } - } - return true; - } else if (ev.target->getID() == TBIDC(INPUT)) { - const core::String& str = ev.target->getText(); - if (tb::TBButton * ok = getWidgetByType("ok")) { - bool disabled; - if (str.empty()) { - disabled = true; - } else { - disabled = true; - if (_filterList.getNumItems() == 1) { - disabled = false; - } else { - for (int i = 0; i < _filterList.getNumItems(); i++) { - const char *filter = _filterList.getItemString(i); - if (!SDL_strcmp(filter, "*")) { - continue; - } - if (core::string::fileMatchesMultiple(str.c_str(), filter)) { - disabled = false; - break; - } - } - } - } - ok->setState(tb::WIDGET_STATE_DISABLED, disabled); - } - // if entered manually, we want to change the directory. - if (io::Filesystem::isReadableDir(str.c_str())) { - changeDir(str.c_str()); - } - } - } - if (ev.type == tb::EVENT_TYPE_KEY_DOWN && ev.special_key == tb::TB_KEY_ESC) { - tb::TBWidgetEvent click_ev(tb::EVENT_TYPE_CLICK); - m_close_button.invokeEvent(click_ev); - return true; - } - const tb::TBID& id = ev.target->getID(); - if (ev.type == tb::EVENT_TYPE_POINTER_DOWN && ev.count >= 2) { - if (tb::TBSelectList *select = getWidgetByType(FILELIST)) { - const int index = select->getValue(); - if (index >= 0 && index < _entityList.getNumItems()) { - const FileDialogItem* item = _entityList.getItem(index); - const auto& dirEntry = item->entry(); - if (_mode != video::WindowedApp::OpenFileMode::Directory - && dirEntry.type == io::Filesystem::DirEntry::Type::dir) { - changeDir(dirEntry.name); - if (tb::TBEditField * input = getWidgetByType(INPUT)) { - input->setText(_directory.c_str()); - } - return true; - } - if (_mode == video::WindowedApp::OpenFileMode::Save) { - if (tb::TBEditField * input = getWidgetByType(INPUT)) { - input->setText(dirEntry.name.c_str()); - } - } else { - const core::String& filename = dirEntry.name; - if (io::Filesystem::isRelativePath(filename)) { - _callback(_directory + "/" + filename); - } else { - _callback(filename); - } - tb::TBWidgetEvent click_ev(tb::EVENT_TYPE_CLICK); - m_close_button.invokeEvent(click_ev); - } - return true; - } - } - } else if (ev.type == tb::EVENT_TYPE_CLICK) { - if (id == TBIDC("ok")) { - if (_mode == video::WindowedApp::OpenFileMode::Save) { - if (tb::TBEditField * input = getWidgetByType(INPUT)) { - const core::String& filename = input->getText(); - const core::String& sfilename = core::String(filename.c_str()); - if (io::Filesystem::isRelativePath(sfilename)) { - _callback(_directory + "/" + sfilename); - } else { - _callback(sfilename); - } - } else { - Log::error("Failed to get input node"); - } - } else if (tb::TBSelectList *select = getWidgetByType(FILELIST)) { - const int index = select->getValue(); - if (index >= 0 && index < _entityList.getNumItems()) { - const FileDialogItem* item = _entityList.getItem(index); - const auto& dirEntry = item->entry(); - const core::String& filename = dirEntry.name; - if (io::Filesystem::isRelativePath(filename)) { - _callback(_directory + "/" + filename); - } else { - _callback(filename); - } - } - } - tb::TBWidgetEvent click_ev(tb::EVENT_TYPE_CLICK); - m_close_button.invokeEvent(click_ev); - return true; - } else if (id == TBIDC("cancel")) { - tb::TBWidgetEvent click_ev(tb::EVENT_TYPE_CLICK); - m_close_button.invokeEvent(click_ev); - return true; - } - } - - return Super::onEvent(ev); -} - -void FileDialogWindow::init() { - if (tb::TBEditField * input = getWidgetByType(INPUT)) { - input->setText(_directory.c_str()); - } -} - -void FileDialogWindow::changeDir(const core::String& dir) { - if (!dir.empty()) { - if (io::Filesystem::isRelativePath(dir)) { - _directory = _fs->absolutePath(_directory + "/" + dir); - } else { - _directory = dir; - } - if (!io::Filesystem::isReadableDir(_directory)) { - _directory = _fs->absolutePath("."); - } - } - _lastDirectory->setVal(_directory); - - _entityList.deleteAllItems(); - _entityList.addItem(new FileDialogItem(io::Filesystem::DirEntry{"..", io::Filesystem::DirEntry::Type::dir, (uint64_t)0, (uint64_t)0})); - - core::DynamicArray entities; - getApp()->filesystem()->list(_directory, entities); - - Log::debug("Looking in %s and found %i entries", _directory.c_str(), (int)entities.size()); - for (const io::Filesystem::DirEntry& e : entities) { - _entityList.addItem(new FileDialogItem(e)); - } -} - -} -} diff --git a/src/modules/ui/turbobadger/FileDialogWindow.h b/src/modules/ui/turbobadger/FileDialogWindow.h deleted file mode 100644 index fe18d1dc1..000000000 --- a/src/modules/ui/turbobadger/FileDialogWindow.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/Window.h" -#include "ui/turbobadger/ui_widgets.h" -#include "video/WindowedApp.h" -#include "io/Filesystem.h" - -namespace ui { -namespace turbobadger { - -/** - * @brief Adds extra info to a string item. - */ -class FileDialogItem: public tb::TBGenericStringItem { -private: - const io::Filesystem::DirEntry _entry; -public: - FileDialogItem(const io::Filesystem::DirEntry& entry) : - tb::TBGenericStringItem(entry.name.c_str()), _entry(entry) { - } - - inline const io::Filesystem::DirEntry& entry() const { return _entry; } -}; - -/** - * @brief FileDialogItemWidget is the widget representing a FileDialogItem. - * On changes to the item, it calls InvokeItemChanged on the source, so that all - * viewers of the source are updated to reflect the change. - */ -class FileDialogItemWidget : public tb::TBLayout { -public: - FileDialogItemWidget(FileDialogItem *item); -}; - -/** - * @brief FileDialogItemSource provides items of type FileDialogItem and makes sure - * the viewer is populated with the customized widget for each item. - */ -class FileDialogItemSource: public tb::TBSelectItemSourceList { -private: - video::WindowedApp::OpenFileMode _mode; - bool _showHidden = false; - bool filterHidden(const io::Filesystem::DirEntry& entry) const; -public: - bool filter(int index, const char *filter) override; - tb::TBWidget *createItemWidget(int index, tb::TBSelectItemViewer *viewer) override; - - inline void setMode(video::WindowedApp::OpenFileMode mode) { _mode = mode; } - inline void setShowHidden(bool showHidden) { _showHidden = showHidden; } -}; - -class FileDialogWindow: public ui::turbobadger::Window { -private: - using Super = ui::turbobadger::Window; - core::String _directory; - video::WindowedApp::OpenFileMode _mode; - FileDialogItemSource _entityList; - tb::TBGenericStringItemSource _dirList; - tb::TBGenericStringItemSource _filterList; - std::function _callback; - io::FilesystemPtr _fs; - core::VarPtr _lastDirectory; -public: - FileDialogWindow(UIApp* app, const std::function& callback, const core::VarPtr& lastDirectory); - ~FileDialogWindow(); - void changeDir(const core::String& dir = ""); - void init(); - void addShortcut(const core::String& dir); - - void setFilter(const char **filter); - void setMode(video::WindowedApp::OpenFileMode mode, const char *inputText = nullptr); - - bool onEvent(const tb::TBWidgetEvent &ev) override; -}; - -} -} diff --git a/src/modules/ui/turbobadger/FontUtil.h b/src/modules/ui/turbobadger/FontUtil.h deleted file mode 100644 index b8036f611..000000000 --- a/src/modules/ui/turbobadger/FontUtil.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "TurboBadger.h" -#include "core/Common.h" - -extern void register_tbbf_font_renderer(); -extern void register_stb_font_renderer(); - -namespace ui { -namespace turbobadger { - -static inline void initFonts() { - register_tbbf_font_renderer(); - register_stb_font_renderer(); - - tb::g_font_manager->addFontInfo("ui/font/font.tb.txt", "Segoe"); - tb::g_font_manager->addFontInfo("ui/font/DejaVuSansMono.ttf", "monospace"); -} - -static inline tb::TBFontFace *getFontByName(const char* fontname, int dpSize = 14, bool registerAsDefault = false) { - tb::TBFontFace *_font; - tb::TBFontDescription fd; - fd.setID(TBIDC(fontname)); - fd.setSize(tb::g_tb_skin->getDimensionConverter()->dpToPx(dpSize)); - - tb::TBFontManager *fontMgr = tb::g_font_manager; - if (registerAsDefault) { - fontMgr->setDefaultFontDescription(fd); - } - - if (fontMgr->hasFontFace(fd)) { - _font = fontMgr->getFontFace(fd); - } else { - _font = fontMgr->createFontFace(fd); - } - core_assert_msg(_font != nullptr, "Could not find the default font - make sure the ui is already configured"); - _font->renderGlyphs(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"); - return _font; -} - -static inline tb::TBFontFace *getFont(int dpSize = 14, bool registerAsDefault = false) { - return getFontByName("Segoe", dpSize, registerAsDefault); -} - -static inline tb::TBFontFace *getMonoSpaceFont(int dpSize = 14, bool registerAsDefault = false) { - return getFontByName("monospace", dpSize, registerAsDefault); -} - -} -} diff --git a/src/modules/ui/turbobadger/TurboBadger.h b/src/modules/ui/turbobadger/TurboBadger.h deleted file mode 100644 index 9e30c8a81..000000000 --- a/src/modules/ui/turbobadger/TurboBadger.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ui_renderer_gl.h" - -#define UIWIDGET_SUBCLASS(clazz, baseclazz) TBOBJECT_SUBCLASS(clazz, baseclazz) -#define UIWIDGET_FACTORY(classname, sync_type, add_child_z) \ - class classname##Factory : public tb::TBWidgetFactory \ - { \ - public: \ - classname##Factory() \ - : tb::TBWidgetFactory(#classname, sync_type) { doRegister(); } \ - virtual ~classname##Factory() {} \ - virtual tb::TBWidget *create(tb::INFLATE_INFO *info) \ - { \ - classname *widget = new classname(); \ - if (widget) { \ - widget->getContentRoot()->setZInflate(add_child_z); \ - } \ - return widget; \ - } \ - }; - -namespace ui { -namespace turbobadger { -using UIRect = tb::TBRect; -using UICheckBox = tb::TBCheckBox; -using UIRadioButton = tb::TBRadioButton; -using UITextField = tb::TBTextField; -} -} diff --git a/src/modules/ui/turbobadger/UIApp.cpp b/src/modules/ui/turbobadger/UIApp.cpp deleted file mode 100644 index 41aed5f6e..000000000 --- a/src/modules/ui/turbobadger/UIApp.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/** - * @file - */ - -#include "UIApp.h" -#include "ui/turbobadger/TurboBadger.h" -#include "ui/turbobadger/FontUtil.h" -#include "FileDialogWindow.h" - -#include "io/Filesystem.h" -#include "command/Command.h" -#include "core/collection/DynamicArray.h" -#include "core/Color.h" -#include "core/UTF8.h" -#include "core/Common.h" -#include "core/Trace.h" -#include "math/Rect.h" -#include "ui_renderer_gl.h" -#include "ui_widgets.h" -#include - -namespace ui { -namespace turbobadger { - -namespace { - -static ImageWidgetFactory imageWidget_wf; -static ColorWidgetFactory colorWidget_wf; - -static inline tb::MODIFIER_KEYS mapModifier(int32_t key, int16_t modifier) { - tb::MODIFIER_KEYS code = tb::TB_MODIFIER_NONE; - switch (key) { - case SDLK_LCTRL: - case SDLK_RCTRL: - code |= tb::TB_CTRL; - break; - case SDLK_LSHIFT: - case SDLK_RSHIFT: - code |= tb::TB_SHIFT; - break; - case SDLK_LALT: - case SDLK_RALT: - code |= tb::TB_ALT; - break; - case SDLK_LGUI: - case SDLK_RGUI: - code |= tb::TB_SUPER; - break; - case SDLK_MODE: - break; - } - - if (modifier & KMOD_ALT) - code |= tb::TB_ALT; - if (modifier & KMOD_CTRL) - code |= tb::TB_CTRL; - if (modifier & KMOD_SHIFT) - code |= tb::TB_SHIFT; - if (modifier & KMOD_GUI) - code |= tb::TB_SUPER; - return code; -} - -static tb::SPECIAL_KEY mapSpecialKey(int32_t key) { - switch (key) { - case SDLK_F1: - return tb::TB_KEY_F1; - case SDLK_F2: - return tb::TB_KEY_F2; - case SDLK_F3: - return tb::TB_KEY_F3; - case SDLK_F4: - return tb::TB_KEY_F4; - case SDLK_F5: - return tb::TB_KEY_F5; - case SDLK_F6: - return tb::TB_KEY_F6; - case SDLK_F7: - return tb::TB_KEY_F7; - case SDLK_F8: - return tb::TB_KEY_F8; - case SDLK_F9: - return tb::TB_KEY_F9; - case SDLK_F10: - return tb::TB_KEY_F10; - case SDLK_F11: - return tb::TB_KEY_F11; - case SDLK_F12: - return tb::TB_KEY_F12; - case SDLK_LEFT: - return tb::TB_KEY_LEFT; - case SDLK_UP: - return tb::TB_KEY_UP; - case SDLK_RIGHT: - return tb::TB_KEY_RIGHT; - case SDLK_DOWN: - return tb::TB_KEY_DOWN; - case SDLK_PAGEUP: - return tb::TB_KEY_PAGE_UP; - case SDLK_PAGEDOWN: - return tb::TB_KEY_PAGE_DOWN; - case SDLK_HOME: - return tb::TB_KEY_HOME; - case SDLK_END: - return tb::TB_KEY_END; - case SDLK_INSERT: - return tb::TB_KEY_INSERT; - case SDLK_TAB: - return tb::TB_KEY_TAB; - case SDLK_DELETE: - return tb::TB_KEY_DELETE; - case SDLK_BACKSPACE: - return tb::TB_KEY_BACKSPACE; - case SDLK_RETURN: - case SDLK_KP_ENTER: - return tb::TB_KEY_ENTER; - case SDLK_ESCAPE: - return tb::TB_KEY_ESC; - case SDLK_LSHIFT: - case SDLK_RSHIFT: - return tb::TB_KEY_SHIFT; - case SDLK_LALT: - case SDLK_RALT: - return tb::TB_KEY_ALT; - case SDLK_RGUI: - case SDLK_LGUI: - return tb::TB_KEY_GUI; - case SDLK_LCTRL: - case SDLK_RCTRL: - return tb::TB_KEY_CTRL; - case SDLK_MODE: - return tb::TB_KEY_MODE; - } - - return tb::TB_KEY_UNDEFINED; -} - -static inline int mapKey(int32_t key) { - switch (key) { - case SDLK_LCTRL: - case SDLK_LSHIFT: - case SDLK_LALT: - case SDLK_LGUI: - case SDLK_RCTRL: - case SDLK_RSHIFT: - case SDLK_RALT: - case SDLK_RGUI: - case SDLK_MODE: - break; - default: - if (mapSpecialKey(key) == tb::TB_KEY_UNDEFINED) { - return key; - } - } - return 0; -} - -} - -tb::UIRendererGL _renderer; - -UIApp::UIApp(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider, size_t threadPoolSize) : - Super(metric, filesystem, eventBus, timeProvider, threadPoolSize) { -} - -UIApp::~UIApp() { -} - -bool UIApp::invokeKey(int key, tb::SPECIAL_KEY special, tb::MODIFIER_KEYS mod, bool down) { -#ifdef MACOSX - bool shortcutKey = (mod & tb::TB_SUPER) ? true : false; -#else - bool shortcutKey = (mod & tb::TB_CTRL) ? true : false; -#endif - Log::debug(_logId, "invoke key: %s (%i)", down ? "down" : "up", key); - if (tb::TBWidget::focused_widget && down && shortcutKey && key != 0) { - bool reverseKey = (mod & tb::TB_SHIFT) ? true : false; - if (key >= 'a' && key <= 'z') { - key += 'A' - 'a'; - } - tb::TBID id; - if (key == 'X') { - id = TBIDC("cut"); - } else if (key == 'C' || special == tb::TB_KEY_INSERT) { - id = TBIDC("copy"); - } else if (key == 'V' || (special == tb::TB_KEY_INSERT && reverseKey)) { - id = TBIDC("paste"); - } else if (key == 'A') { - id = TBIDC("selectall"); - } else if (key == 'Z' || key == 'Y') { - bool undo = key == 'Z'; - if (reverseKey) { - undo = !undo; - } - id = undo ? TBIDC("undo") : TBIDC("redo"); - } else if (key == 'N') { - id = TBIDC("new"); - } else if (key == 'O') { - id = TBIDC("open"); - } else if (key == 'S') { - id = TBIDC("save"); - } else if (key == 'W') { - id = TBIDC("close"); - } else if (special == tb::TB_KEY_PAGE_UP) { - id = TBIDC("prev_doc"); - } else if (special == tb::TB_KEY_PAGE_DOWN) { - id = TBIDC("next_doc"); - } else { - return false; - } - - tb::TBWidgetEvent ev(tb::EVENT_TYPE_SHORTCUT, 0, 0, tb::TB_UNKNOWN, mod); - ev.ref_id = id; - Log::debug(_logId, "invoke shortcut event: %i", key); - return tb::TBWidget::focused_widget->invokeEvent(ev); - } - - if (special == tb::TB_KEY_UNDEFINED && SDL_IsTextInputActive()) { - return true; - } - - if (_root->getVisibility() != tb::WIDGET_VISIBILITY_VISIBLE) { - return false; - } - return _root->invokeKey(key, special, mod, down); -} - -void UIApp::showStr(int x, int y, const glm::vec4& color, const char *fmt, ...) { - static char buf[1024]; - va_list ap; - va_start(ap, fmt); - SDL_vsnprintf(buf, sizeof(buf), fmt, ap); - buf[sizeof(buf) - 1] = '\0'; - _root->getFont()->drawString(x, y, tb::TBColor(color.r * 255.0f, color.g * 255.0f, color.b * 255.0f, color.a * 255.0f), buf, SDL_strlen(buf)); - va_end(ap); -} - -void UIApp::enqueueShowStr(int x, const glm::vec4& color, const char *fmt, ...) { - static char buf[1024]; - va_list ap; - va_start(ap, fmt); - SDL_vsnprintf(buf, sizeof(buf), fmt, ap); - buf[sizeof(buf) - 1] = '\0'; - tb::TBFontFace* font = _root->getFont(); - font->drawString(x, _lastShowTextY, tb::TBColor(color.r * 255.0f, color.g * 255.0f, color.b * 255.0f, color.a * 255.0f), buf, SDL_strlen(buf)); - _lastShowTextY += _root->getFont()->getHeight() + 5; - va_end(ap); -} - -void UIApp::fileDialog(const std::function& callback, OpenFileMode mode, const core::String& filter) { - if (isRelativeMouseMode()) { - toggleRelativeMouseMode(); - } - FileDialogWindow* dialog = new FileDialogWindow(this, callback, _lastDirectory); - dialog->setMode(mode); - const core::String& lastDir = _lastDirectory->strVal(); - if (!lastDir.empty()) { - dialog->addShortcut(lastDir); - } - if (!filter.empty()) { - core::DynamicArray tokens; - core::string::splitString(filter, tokens, ";"); - const char **filters = new const char*[tokens.size() + 1]; - int n = 0; - for (const auto& f : tokens) { - filters[n++] = f.c_str(); - } - filters[n] = nullptr; - dialog->setFilter((const char**)filters); - delete[] filters; - } - dialog->changeDir(lastDir); - dialog->init(); -} - -bool UIApp::onMouseWheel(int32_t x, int32_t y) { - if (_console.onMouseWheel(x, y)) { - return true; - } - if (Super::onMouseWheel(x, y)) { - return true; - } - return _root->invokeWheel(_mousePos.x, _mousePos.y, x, -y, getModifierKeys()); -} - -void UIApp::onMouseButtonPress(int32_t x, int32_t y, uint8_t button, uint8_t clicks) { - if (_console.onMouseButtonPress(x, y, button)) { - return; - } - const tb::MODIFIER_KEYS modKeys = getModifierKeys(); - - tb::BUTTON_TYPE type = tb::BUTTON_TYPE::TB_UNKNOWN; - if (button == SDL_BUTTON_LEFT) { - type = tb::TB_LEFT; - } else if (button == SDL_BUTTON_RIGHT) { - type = tb::TB_RIGHT; - } else if (button == SDL_BUTTON_MIDDLE) { - type = tb::TB_MIDDLE; - } - - _root->invokePointerDown(x, y, clicks, modKeys, type); - Super::onMouseButtonPress(x, y, button, clicks); -} - -tb::MODIFIER_KEYS UIApp::getModifierKeys() const { - return mapModifier(0, SDL_GetModState()); -} - -void UIApp::onMouseButtonRelease(int32_t x, int32_t y, uint8_t button) { - if (_console.isActive()) { - return; - } - const tb::MODIFIER_KEYS modKeys = getModifierKeys(); - tb::BUTTON_TYPE type = tb::BUTTON_TYPE::TB_UNKNOWN; - if (button == SDL_BUTTON_LEFT) { - type = tb::TB_LEFT; - } else if (button == SDL_BUTTON_RIGHT) { - type = tb::TB_RIGHT; - } else if (button == SDL_BUTTON_MIDDLE) { - type = tb::TB_MIDDLE; - } - if (button == SDL_BUTTON_RIGHT) { - _root->invokePointerMove(x, y, modKeys, type); - tb::TBWidget* hover = tb::TBWidget::hovered_widget; - if (hover != nullptr) { - hover->convertFromRoot(x, y); - tb::TBWidgetEvent ev(tb::EVENT_TYPE_CONTEXT_MENU, x, y, type, modKeys); - if (!hover->invokeEvent(ev)) { - _root->invokePointerUp(x, y, modKeys, type); - } - } else { - _root->invokePointerUp(x, y, modKeys, type); - } - } else { - _root->invokePointerUp(x, y, modKeys, type); - } - Super::onMouseButtonRelease(x, y, button); -} - -bool UIApp::onTextInput(const core::String& text) { - if (_console.onTextInput(text)) { - return true; - } - const char *c = text.c_str(); - for (;;) { - const int key = core::utf8::next(&c); - if (key == -1) { - return true; - } - _root->invokeKey(key, tb::TB_KEY_UNDEFINED, tb::TB_MODIFIER_NONE, true); - _root->invokeKey(key, tb::TB_KEY_UNDEFINED, tb::TB_MODIFIER_NONE, false); - } - return true; -} - -bool UIApp::onKeyPress(int32_t key, int16_t modifier) { - if (_console.onKeyPress(key, modifier)) { - return true; - } - - if (Super::onKeyPress(key, modifier)) { - return true; - } - - return invokeKey(mapKey(key), mapSpecialKey(key), mapModifier(key, modifier), true); -} - -bool UIApp::onKeyRelease(int32_t key, int16_t modifier) { - if (_console.isActive()) { - return true; - } - Super::onKeyRelease(key, modifier); - tb::MODIFIER_KEYS mod = mapModifier(0, modifier); - mod |= mapModifier(key, 0); - if (key == SDLK_MENU && tb::TBWidget::focused_widget) { - tb::TBWidgetEvent ev(tb::EVENT_TYPE_CONTEXT_MENU, 0, 0, tb::TB_UNKNOWN, mod); - if (tb::TBWidget::focused_widget->invokeEvent(ev)) { - return true; - } - } - return invokeKey(mapKey(key), mapSpecialKey(key), mod, false); -} - -void UIApp::onWindowResize(int windowWidth, int windowHeight) { - Super::onWindowResize(windowWidth, windowHeight); - _renderer.onWindowResize(frameBufferDimension(), windowDimension()); - _root->setRect(tb::TBRect(0, 0, _frameBufferDimension.x, _frameBufferDimension.y)); -} - -app::AppState UIApp::onConstruct() { - const app::AppState state = Super::onConstruct(); - command::Command::registerCommand("cl_ui_debug", [&] (const command::CmdArgs& args) { -#ifdef DEBUG - tb::ShowDebugInfoSettingsWindow(_root); -#endif - }).setHelp("Show ui debug information - only available in debug builds"); - - _console.construct(); - - return state; -} - -void UIApp::onWidgetFocusChanged(tb::TBWidget *widget, bool focused) { - if (focused && widget->isOfType()) { - SDL_StartTextInput(); - } else { - SDL_StopTextInput(); - } -} - -void UIApp::afterRootWidget() { - const math::Rect rect(0, 0, _frameBufferDimension.x, _frameBufferDimension.y); - _console.render(rect, _deltaFrameSeconds); -} - -app::AppState UIApp::onInit() { - const app::AppState state = Super::onInit(); - video::checkError(); - if (state != app::AppState::Running) { - return state; - } - if (!tb::tb_core_init(&_renderer)) { - Log::error(_logId, "failed to initialize the ui"); - return app::AppState::InitFailure; - } - - tb::TBWidgetListener::addGlobalListener(this); - _uiInitialized = true; - - if (!tb::g_tb_lng->load("ui/lang/en.tb.txt")) { - Log::warn(_logId, "could not load the translation ui/lang/en.tb.txt"); - } - - if (_applicationSkin.empty()) { - const core::String skin = "ui/skin/" + _appname + "-skin.tb.txt"; - if (filesystem()->exists(skin)) { - _applicationSkin = skin; - } - } - - tb::TBWidgetsAnimationManager::init(); - - if (!tb::g_tb_skin->load("ui/skin/skin.tb.txt", _applicationSkin.empty() ? nullptr : _applicationSkin.c_str())) { - Log::error(_logId, "could not load the skin at ui/skin/skin.tb.txt and/or %s", - _applicationSkin.empty() ? "none" : _applicationSkin.c_str()); - return app::AppState::InitFailure; - } - - if (!_renderer.init(frameBufferDimension(), windowDimension())) { - Log::error(_logId, "could not init ui renderer"); - return app::AppState::InitFailure; - } - - initFonts(); - tb::TBFontFace *font = getFont(_uiFontSize->intVal(), true); - if (font == nullptr) { - Log::error(_logId, "could not create the font face"); - return app::AppState::InitFailure; - } - - _root = new tb::TBWidget(); - _root->setRect(tb::TBRect(0, 0, _frameBufferDimension.x, _frameBufferDimension.y)); - _root->setSkinBg(TBIDC("background")); - _root->setGravity(tb::WIDGET_GRAVITY_ALL); - - _console.init(); - - return state; -} - -void UIApp::addChild(Window* window) { - _root->addChild(window); -} - -tb::TBWidget* UIApp::getWidget(const char *name) { - return _root->getWidgetByID(tb::TBID(name)); -} - -tb::TBWidget* UIApp::getWidgetAt(int x, int y, bool includeChildren) { - return _root->getWidgetAt(x, y, includeChildren); -} - -void UIApp::beforeUI() { - _console.update(_deltaFrameSeconds); - - _lastShowTextY = 5; - - if (!_console.isActive()) { - static glm::ivec2 lastMousePos = _mousePos; - if (lastMousePos != _mousePos) { - _root->invokePointerMove(_mousePos.x, _mousePos.y, getModifierKeys(), tb::TB_UNKNOWN); - lastMousePos = _mousePos; - } - } - - const bool renderUI = _renderUI->boolVal(); - if (renderUI) { - core_trace_scoped(UIAppUpdateUI); - tb::TBAnimationManager::update(); - _root->invokeProcessStates(); - _root->invokeProcess(); - - _renderer.beginPaint(_frameBufferDimension.x, _frameBufferDimension.y); - _root->invokePaint(tb::TBWidget::PaintProps()); - } - { - core_trace_scoped(UIAppAfterUI); - afterRootWidget(); - } - if (renderUI) { - core_trace_scoped(UIAppEndPaint); - _renderer.endPaint(); - // If animations are running, reinvalidate immediately - if (tb::TBAnimationManager::hasAnimationsRunning()) { - _root->invalidate(); - } - } - double next_fire_time = tb::TBMessageHandler::getNextMessageFireTime(); - double now = tb::TBSystem::getTimeMS(); - if (next_fire_time == TB_NOT_SOON || (next_fire_time - now) <= 1.0) { - tb::TBMessageHandler::processMessages(); - } -} - -app::AppState UIApp::onCleanup() { - tb::TBAnimationManager::abortAllAnimations(); - if (_uiInitialized) { - tb::TBWidgetListener::removeGlobalListener(this); - tb::TBWidgetsAnimationManager::shutdown(); - _uiInitialized = false; - } - - tb::tb_core_shutdown(); - - Log::debug("shutdown ui widgets"); - if (_root != nullptr) { - _root->die(); - } - _root = nullptr; - - _console.shutdown(); - - _renderer.shutdown(); - - return Super::onCleanup(); -} - -} -} diff --git a/src/modules/ui/turbobadger/UIApp.h b/src/modules/ui/turbobadger/UIApp.h deleted file mode 100644 index 454720f1d..000000000 --- a/src/modules/ui/turbobadger/UIApp.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/imgui/IMGUIApp.h" -#include "Window.h" -#include "Console.h" -#include - -namespace ui { -namespace turbobadger { - -/** - * @ingroup UI - */ -class UIApp: public ui::imgui::IMGUIApp, private tb::TBWidgetListener { -private: - using Super = ui::imgui::IMGUIApp; -protected: - static constexpr uint32_t _logId = Log::logid("UIAPP"); - tb::TBWidget* _root = nullptr; - Console _console; - int _lastShowTextY = -1; - core::String _applicationSkin; - bool _uiInitialized = false; - - virtual bool onKeyRelease(int32_t key, int16_t modifier) override; - virtual bool onKeyPress(int32_t key, int16_t modifier) override; - virtual bool onTextInput(const core::String& text) override; - virtual bool onMouseWheel(int32_t x, int32_t y) override; - virtual void onMouseButtonPress(int32_t x, int32_t y, uint8_t button, uint8_t clicks) override; - virtual void onMouseButtonRelease(int32_t x, int32_t y, uint8_t button) override; - - virtual void onWidgetFocusChanged(tb::TBWidget *widget, bool focused) override; - - bool invokeKey(int key, tb::SPECIAL_KEY special, tb::MODIFIER_KEYS mod, bool down); - void showStr(int x, int y, const glm::vec4& color, CORE_FORMAT_STRING const char *fmt, ...) CORE_PRINTF_VARARG_FUNC(5); - void enqueueShowStr(int x, const glm::vec4& color, CORE_FORMAT_STRING const char *fmt, ...) CORE_PRINTF_VARARG_FUNC(4); - - tb::MODIFIER_KEYS getModifierKeys() const; -public: - UIApp(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider, size_t threadPoolSize = 1); - virtual ~UIApp(); - - virtual void beforeUI() override; - - template - T* getWidgetByType(const char *name); - - tb::TBWidget* getWidget(const char *name); - tb::TBWidget* getWidgetAt(int x, int y, bool includeChildren = true); - - // hook that is called directory before the ui is rendered. Your last chance to let the app contribute - // something in the ui context and the ui drawcalls (like debug text or rendering an in-game console on - // top of the ui) - virtual void afterRootWidget(); - - void addChild(Window* window); - - /** - * @param[in] filter png,jpg;psd The default filter is for png and jpg files. A second filter is available for psd files. There is a wildcard option in a dropdown. - */ - void fileDialog(const std::function& callback, OpenFileMode mode, const core::String& filter) override; - - virtual void onRenderUI() override {} - - virtual void onWindowResize(int windowWidth, int windowHeight) override; - virtual app::AppState onConstruct() override; - virtual app::AppState onInit() override; - virtual app::AppState onCleanup() override; -}; - -template -inline T* UIApp::getWidgetByType(const char *name) { - return _root->getWidgetByIDAndType(tb::TBID(name)); -} - -} -} diff --git a/src/modules/ui/turbobadger/UIDummies.h b/src/modules/ui/turbobadger/UIDummies.h deleted file mode 100644 index 4718ff1c4..000000000 --- a/src/modules/ui/turbobadger/UIDummies.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file - */ - -#pragma once - -class DummyBitmap: public tb::TBBitmap { -public: - bool init(int width, int height, uint32_t *data) { - m_w = width; - m_h = height; - setData(data); - return true; - } - - virtual int width() override { - return m_w; - } - - virtual int height() override { - return m_h; - } - - virtual void setData(uint32_t *data) override { - } -public: - int m_w = 0, m_h = 0; -}; - -class DummyRenderer: public tb::TBRendererBatcher { -private: - tb::TBRect _clipRect; -public: - void beginPaint(int, int) override { - } - - void endPaint() override { - } - - tb::TBBitmap *createBitmap(int width, int height, uint32_t* data) override { - DummyBitmap *bitmap = new DummyBitmap(); - if (!bitmap || !bitmap->init(width, height, data)) { - delete bitmap; - return nullptr; - } - return bitmap; - } - - void renderBatch(Batch*) override { - } - - void setClipRect(const tb::TBRect&) override { - } -}; diff --git a/src/modules/ui/turbobadger/WaitingMessage.cpp b/src/modules/ui/turbobadger/WaitingMessage.cpp deleted file mode 100644 index 8707f6035..000000000 --- a/src/modules/ui/turbobadger/WaitingMessage.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "WaitingMessage.h" -#include "FontUtil.h" -#include "core/ArrayLength.h" -#include "UIApp.h" - -namespace ui { -namespace turbobadger { - -WaitingMessage::WaitingMessage(UIApp* app) : - _app(app) { -} - -WaitingMessage::~WaitingMessage() { - shutdown(); -} - -void WaitingMessage::setColor(const glm::vec4& color) { - _color = tb::TBColor(color.r, color.g, color.b, color.a); -} - -void WaitingMessage::init(int fontSize) { - _font = getFont(fontSize); -} - -void WaitingMessage::shutdown() { - _font = nullptr; -} - -void WaitingMessage::setTextId(const char *textId) { - _translatedStr = tr(textId); -} - -void WaitingMessage::reset() { - _connectingStart = 0ul; - _dotsIndex = 0; -} - -void WaitingMessage::update(uint64_t deltaFrame) { - _connectingStart += deltaFrame; -} - -void WaitingMessage::render() { - static const char *dotsArray[] = { ".", "..", "...", "....", "....." }; - if (_translatedStr == nullptr) { - return; - } - if (_font == nullptr) { - return; - } - if (_connectingStart >= 2000ul) { - _dotsIndex = (_dotsIndex + 1) % lengthof(dotsArray); - _connectingStart -= 2000ul; - } - const int y = _app->frameBufferHeight() / 2 - _font->getHeight() / 2; - const int len = SDL_strlen(_translatedStr); - const int w = _font->getStringWidth(_translatedStr, len); - const int x = _app->frameBufferWidth() / 2 - w / 2; - _font->drawString(x, y, _color, _translatedStr, len); - - const int dotX = x + w + 5; - const char *dotsString = dotsArray[_dotsIndex]; - _font->drawString(dotX, y, _color, dotsString, SDL_strlen(dotsString)); -} - -} -} diff --git a/src/modules/ui/turbobadger/WaitingMessage.h b/src/modules/ui/turbobadger/WaitingMessage.h deleted file mode 100644 index b847907a1..000000000 --- a/src/modules/ui/turbobadger/WaitingMessage.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "TurboBadger.h" -#include "core/Color.h" - -namespace ui { -namespace turbobadger { - -class UIApp; - -class WaitingMessage { -private: - UIApp* _app; - tb::TBFontFace *_font = nullptr; - tb::TBColor _color = {255, 255, 255, 255}; - const char* _translatedStr = nullptr; - uint64_t _connectingStart = 0ul; - int _dotsIndex = 0; -public: - WaitingMessage(UIApp* app); - ~WaitingMessage(); - - void init(int fontSize = 28); - void shutdown(); - - void setColor(const glm::vec4& color); - /** - * @param[in] textId The language identifier - */ - void setTextId(const char *textId); - - void reset(); - void update(uint64_t deltaFrame); - void render(); -}; - -} -} diff --git a/src/modules/ui/turbobadger/Widget.h b/src/modules/ui/turbobadger/Widget.h deleted file mode 100644 index 1b5e3b61e..000000000 --- a/src/modules/ui/turbobadger/Widget.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "TurboBadger.h" -#include - -namespace ui { -namespace turbobadger { - -class Widget : public tb::TBWidget { -protected: - inline bool isRelativeMouseMode() const { - return SDL_GetRelativeMouseMode() == SDL_TRUE ? true : false; - } - - inline bool isMiddleMouseButtonPressed() const { - return SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(SDL_BUTTON_MIDDLE); - } - - inline bool isRightMouseButtonPressed() const { - return SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(SDL_BUTTON_RIGHT); - } -public: - Widget() : - tb::TBWidget() { - } - - ~Widget() override {} -}; - -} -} diff --git a/src/modules/ui/turbobadger/Window.cpp b/src/modules/ui/turbobadger/Window.cpp deleted file mode 100644 index 24e810b39..000000000 --- a/src/modules/ui/turbobadger/Window.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/** - * @file - */ - -#include "Window.h" -#include "UIApp.h" -#include "io/Filesystem.h" -#include "core/GLM.h" -#include "core/Singleton.h" -#include "core/StringUtil.h" -#include "core/Var.h" -#include - -namespace ui { -namespace turbobadger { - -static const core::String EMPTY = ""; - -Window::Window(UIApp* app) : - Super(), _app(app) { - app->addChild(this); - core::Singleton::getInstance().registerObserver(this); -} - -Window::Window(Window* parent) : - Super(), _app(parent == nullptr ? nullptr : parent->_app) { - // if this is null, make sure to add the window on your own - if (parent != nullptr) { - parent->addChild(this); - } - core::Singleton::getInstance().registerObserver(this); -} - -Window::~Window() { - removeFromParent(); - core::Singleton::getInstance().removeObserver(this); -} - -tb::TBGenericStringItem* Window::addStringItem(tb::TBGenericStringItemSource& items, const char *text, const char *id, bool translate) { - tb::TBGenericStringItem* item; - if (id == nullptr) { - const core::String& lowerId = core::String::lower(text); - item = new tb::TBGenericStringItem(translate ? tr(text) : text, TBIDC(lowerId.c_str())); - const core::String& iconId = app::App::getInstance()->appname() + "-" + lowerId; - item->setSkinImage(TBIDC(iconId.c_str())); - } else { - item = new tb::TBGenericStringItem(translate ? tr(text) : text, TBIDC(id)); - char buf[128]; - SDL_snprintf(buf, sizeof(buf), "%s-%s", app::App::getInstance()->appname().c_str(), id); - item->setSkinImage(TBIDC(buf)); - } - items.addItem(item); - return item; -} - -void Window::onDie() { - Super::onDie(); - core::Singleton::getInstance().removeObserver(this); -} - -bool Window::onEvent(const tb::TBWidgetEvent &ev) { - return Super::onEvent(ev); -} - -float Window::getFloat(const char *nodeId) { - return core::string::toFloat(getStr(nodeId)); -} - -int Window::getSelectedId(const char *nodeId) { - if (tb::TBSelectDropdown *select = getWidgetByIDAndType(TBIDC(nodeId))) { - return select->getValue(); - } - return -1; -} - -int Window::getInt(const char *nodeId) { - return core::string::toInt(getStr(nodeId)); -} - -Window* Window::getParent() const { - return static_cast(Super::getParent()); -} - -UIApp* Window::getApp() const { - if (_app) { - return _app; - } - Window* parent = getParent(); - if (parent == nullptr) { - return nullptr; - } - return parent->getApp(); -} - -void Window::fillWidgets(const Field* fields, int fieldAmount, void* basePtr) { - for (int i = 0; i < fieldAmount; ++i) { - const Field& field = fields[i]; - const tb::TBID& name = field.name; - TBWidget *widget = getWidgetByID(name); - if (widget == nullptr) { - Log::warn("Could not find widget in window %s", getClassName()); - continue; - } - void* fieldPtr = (uint8_t*)basePtr + field.offset; - switch (field.type) { - case T_INT: { - const core::String& str = core::string::format("%i", *(int*)fieldPtr); - widget->setText(str.c_str()); - break; - } - case T_FLOAT: { - const core::String& str = core::string::format("%f", *(float*)fieldPtr); - widget->setText(str.c_str()); - break; - } - case T_IVEC2: { - glm::ivec2* vec = (glm::ivec2*)fieldPtr; - const core::String& str = core::string::format("%i:%i", vec->x, vec->y); - widget->setText(str.c_str()); - break; - } - case T_VEC2: { - glm::vec2* vec = (glm::vec2*)fieldPtr; - const core::String& str = core::string::format("%f:%f", vec->x, vec->y); - widget->setText(str.c_str()); - break; - } - } - } -} - -void Window::fillFields(const Field* fields, int fieldAmount, void* basePtr) { - for (int i = 0; i < fieldAmount; ++i) { - const Field& field = fields[i]; - const tb::TBID name(field.name); - core::String str; - - tb::TBSelectList *list = getWidgetByIDAndType(name); - if (list != nullptr) { - const int value = list->getValue(); - tb::TBGenericStringItem* item = list->getDefaultSource()->getItem(value); - if (field.type == T_INT) { - const uint32_t id = item->id; - str = core::string::format("%i", id); - } else { - str = item->str; - } - } else { - TBWidget *widget = getWidgetByID(name); - if (widget == nullptr) { - Log::warn("Could not find widget with id %s in window %s", field.name, getClassName()); - continue; - } - str = widget->getText(); - } - const char *string = str.c_str(); - void* fieldPtr = (uint8_t*)basePtr + field.offset; - switch (field.type) { - case T_INT: { - const int value = core::string::toInt(string); - Log::info("Set %i for %s (%s)", value, field.name, string); - *(int*)fieldPtr = value; - break; - } - case T_FLOAT: { - const float value = core::string::toFloat(string); - Log::info("Set %f for %s (%s)", value, field.name, string); - *(float*)fieldPtr = value; - break; - } - case T_IVEC2: { - char buf[64]; - SDL_strlcpy(buf, string, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - char *sep = SDL_strchr(buf, ':'); - if (sep == nullptr) { - break; - } - *sep++ = '\0'; - glm::ivec2* vec = (glm::ivec2*)fieldPtr; - vec->x = core::string::toInt(string); - vec->y = core::string::toInt(sep); - break; - } - case T_VEC2: { - char buf[64]; - SDL_strlcpy(buf, string, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - char *sep = SDL_strchr(buf, ':'); - if (sep == nullptr) { - break; - } - *sep++ = '\0'; - glm::vec2* vec = (glm::vec2*)fieldPtr; - vec->x = core::string::toFloat(string); - vec->y = core::string::toFloat(sep); - break; - } - } - } -} - -bool Window::loadResourceFile(const char *filename) { - _filename = filename; - tb::TBNode node; - const io::FilesystemPtr& filesystem = io::filesystem(); - const io::FilePtr& file = filesystem->open(filename); - if (!file->exists()) { - Log::error("%s doesn't exists", filename); - return false; - } - const core::String& data = file->load(); - if (!node.readData(data.c_str(), (int)data.size(), tb::TB_NODE_READ_FLAGS_NONE)) { - return false; - } - return loadResource(node); -} - -void Window::popup(const core::String& title, const core::String& str, PopupType type, const char *id) { - tb::TBMessageWindow *win = new tb::TBMessageWindow(this, TBIDC(id)); - tb::TBMessageWindowSettings settings((tb::TB_MSG)core::enumVal(type), tb::TBID(0u)); - settings.dimmer = true; - win->show(title.c_str(), str.c_str(), &settings); -} - -void Window::setStr(const char *nodeId, const core::String& text) { - tb::TBEditField *widget = getWidgetByType(nodeId); - if (widget == nullptr) { - Log::info("could not find an edit field node with the name %s", nodeId); - return; - } - widget->setText(text.c_str()); -} - -void Window::toggleViaVar(const char *checkBoxNodeId, const core::VarPtr& var) { - toggle(checkBoxNodeId, var->boolVal()); -} - -void Window::toggle(const char *checkBoxNodeId, bool state) { - tb::TBCheckBox *widget = getWidgetByIDAndType(checkBoxNodeId); - if (widget == nullptr) { - Log::info("could not find a checkbox node with the name %s", checkBoxNodeId); - return; - } - return widget->setValue(state ? 1 : 0); -} - -bool Window::isToggled(const char *checkBoxNodeId) { - tb::TBCheckBox *widget = getWidgetByIDAndType(checkBoxNodeId); - if (widget == nullptr) { - Log::info("could not find a checkbox node with the name %s", checkBoxNodeId); - return false; - } - return widget->getValue() == 1; -} - -core::String Window::getStr(const char *nodeId) { - tb::TBWidget *widget = getWidgetByID(nodeId); - if (widget == nullptr) { - Log::info("could not find a node with the name %s", nodeId); - return EMPTY; - } - const core::String& amplitude = widget->getText(); - return core::String(amplitude.c_str()); -} - -bool Window::loadResourceData(const char *data) { - tb::TBNode node; - if (!node.readData(data)) { - return false; - } - return loadResource(node); -} - -static void printNodeTree(const core::String& filename, tb::TBNode &node) { - for (tb::TBNode *child = node.getFirstChild(); child; child = child->getNext()) { - Log::trace("File: %s: node found: '%s' = '%s'", filename.c_str(), child->getName(), child->getValue().getString()); - printNodeTree(filename, *child); - } -} - -bool Window::loadResource(tb::TBNode &node) { - printNodeTree(_filename, node); - - tb::g_widgets_reader->loadNodeTree(this, &node); - - // Get title from the WindowInfo section (or use "" if not specified) - setText(node.getValueString("WindowInfo>title", "")); - - tb::TBWidget *parent = getParent(); - if (parent == nullptr) { - return false; - } - const tb::TBRect& r = parent->getRect(); - const tb::TBRect parentRect(0, 0, r.w, r.h); - const tb::TBDimensionConverter *dc = tb::g_tb_skin->getDimensionConverter(); - tb::TBRect windowRect = getResizeToFitContentRect(); - - // Use specified size or adapt to the preferred content size. - tb::TBNode *tmp = node.getNode("WindowInfo>size"); - if (tmp && tmp->getValue().getArrayLength() == 2) { - tb::TBValueArray *dimensions = tmp->getValue().getArray(); - const char *sizeW = dimensions->getValue(0)->getString(); - if (sizeW[SDL_strlen(sizeW) - 1] == '%') { - _percentWidth = SDL_atof(sizeW); - windowRect.w = _app->frameBufferWidth() * _percentWidth / 100.0f; - } else { - windowRect.w = dc->getPxFromString(sizeW, windowRect.w); - } - const char *sizeH = dimensions->getValue(1)->getString(); - if (sizeH[SDL_strlen(sizeH) - 1] == '%') { - _percentHeight = SDL_atof(sizeW); - windowRect.h = _app->frameBufferHeight() * _percentHeight / 100.0f; - } else { - windowRect.h = dc->getPxFromString(sizeH, windowRect.h); - } - } - - // Use the specified position or center in parent. - tmp = node.getNode("WindowInfo>position"); - if (tmp && tmp->getValue().getArrayLength() == 2) { - tb::TBValueArray *position = tmp->getValue().getArray(); - const char *posW = position->getValue(0)->getString(); - const char *posH = position->getValue(1)->getString(); - windowRect.x = dc->getPxFromString(posW, windowRect.x); - windowRect.y = dc->getPxFromString(posH, windowRect.y); - } else { - windowRect = windowRect.centerIn(parentRect); - } - - if (tb::TBNode *fullscreen = node.getNode("WindowInfo>fullscreen")) { - const int fullscreenVal = fullscreen->getValue().getInt(); - if (fullscreenVal != 0) { - windowRect.x = 0; - windowRect.y = 0; - if (_app != nullptr) { - windowRect.w = _app->frameBufferWidth(); - windowRect.h = _app->frameBufferHeight(); - } else { - TBWidget *parent = getParent(); - if (parent != nullptr) { - windowRect.w = parent->getPreferredSize().pref_w; - windowRect.h = parent->getPreferredSize().pref_h; - } - } - } - } - - // Make sure the window is inside the parent, and not larger. - windowRect = windowRect.moveIn(parentRect).clip(parentRect); - - setRect(windowRect); - - // Ensure we have focus - now that we've filled the window with possible focusable - // widgets. EnsureFocus was automatically called when the window was activated (by - // adding the window to the root), but then we had nothing to focus. - // Alternatively, we could add the window after setting it up properly. - ensureFocus(); - return true; -} - -void Window::onWindowResize(int, int) { - const tb::TBRect parentRect(0, 0, getParent()->getRect().w, getParent()->getRect().h); - tb::TBRect windowRect = getRect(); - windowRect = windowRect.moveIn(parentRect).clip(parentRect); - if (_percentHeight > 0.0f) { - windowRect.w = _app->frameBufferWidth() * _percentWidth; - } - if (_percentHeight > 0.0f) { - windowRect.h = _app->frameBufferHeight() * _percentHeight; - } - setRect(windowRect); -} - -tb::TBWidget* Window::getWidget(const char *name) { - return getWidgetByID(tb::TBID(name)); -} - -bool Window::setActive(const char *name, bool active) { - tb::TBWidget* widget = getWidget(name); - if (widget == nullptr) { - return false; - } - - widget->setState(tb::WIDGET_STATE_DISABLED, !active); - return true; -} - -bool Window::setVisible(const char *name, bool visible) { - tb::TBWidget* widget = getWidget(name); - if (widget == nullptr) { - return false; - } - - if (visible) { - widget->setVisibility(tb::WIDGET_VISIBILITY_GONE); - } else { - widget->setVisibility(tb::WIDGET_VISIBILITY_VISIBLE); - } - return true; -} - -void Window::requestQuit() { - app::App::getInstance()->requestQuit(); -} - -} -} diff --git a/src/modules/ui/turbobadger/Window.h b/src/modules/ui/turbobadger/Window.h deleted file mode 100644 index c44ea894c..000000000 --- a/src/modules/ui/turbobadger/Window.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "TurboBadger.h" -#include "video/IEventObserver.h" -#include -#include "core/SharedPtr.h" - -namespace core { -class Var; -typedef core::SharedPtr VarPtr; -} - -namespace ui { -namespace turbobadger { - -class UIApp; - -#define FIELD(name, type, structtarget, structmember) name, type, offsetof(structtarget, structmember) -#define INT_FIELD(name, structtarget, structmember) FIELD(name, ui::turbobadger::Window::T_INT, structtarget, structmember) -#define FLOAT_FIELD(name, structtarget, structmember) FIELD(name, ui::turbobadger::Window::T_FLOAT, structtarget, structmember) -#define IVEC2_FIELD(name, structtarget, structmember) FIELD(name, ui::turbobadger::Window::T_IVEC2, structtarget, structmember) -#define VEC2_FIELD(name, structtarget, structmember) FIELD(name, ui::turbobadger::Window::T_VEC2, structtarget, structmember) -#define tr(id) ui::turbobadger::Window::getTranslation(id) - -class Window: public tb::TBWindow, public io::IEventObserver { -private: - UIApp* _app; -protected: - using Super = tb::TBWindow; - float _percentWidth = 0.0f; - float _percentHeight = 0.0f; - core::String _filename; - - tb::TBGenericStringItem* addStringItem(tb::TBGenericStringItemSource& items, const char *text, const char *id = nullptr, bool translate = true); - -public: - static inline const char *getTranslation(const char *input) { - const char *str = tb::g_tb_lng->getString(tb::TBID(input)); - if (!SDL_strncmp(str, " - T* getWidgetByType(const char *name); - - tb::TBWidget* getWidget(const char *name); - bool setVisible(const char *name, bool visible); - bool setActive(const char *name, bool active); - void setStr(const char *nodeId, const core::String& text); - - virtual void onDie() override; - virtual bool onEvent(const tb::TBWidgetEvent &ev) override; - - virtual void onWindowResize(int windowWidth, int windowHeight) override; -}; - -template -inline T* Window::getWidgetByType(const char *name) { - return getWidgetByIDAndType(tb::TBID(name)); -} - -} -} diff --git a/src/modules/ui/turbobadger/tb/LICENSE b/src/modules/ui/turbobadger/tb/LICENSE deleted file mode 100644 index 0e5a955a0..000000000 --- a/src/modules/ui/turbobadger/tb/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -The whole source code in this directory based on the original work of -Emil Segerås. But was modified to integrate better into the engine. - -License - -Turbo Badger -Copyright (C) 2011-2014 Emil Segerås - -License: - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not -claim that you wrote the original software. If you use this software -in a product, an acknowledgment in the product documentation would be -appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not be -misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. diff --git a/src/modules/ui/turbobadger/tb/animation/tb_animation.cpp b/src/modules/ui/turbobadger/tb/animation/tb_animation.cpp deleted file mode 100644 index 76ca41f6b..000000000 --- a/src/modules/ui/turbobadger/tb/animation/tb_animation.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/** - * @file - */ - -#include "animation/tb_animation.h" -#include "core/Assert.h" -#include "core/Trace.h" -#include "tb_system.h" - -namespace tb { - -#define SMOOTHSTEP(x) ((x) * (x) * (3.0f - 2.0f * (x))) - -static float sc(float x) { - float s = x < 0 ? -1.F : 1.F; - x = Abs(x); - if (x >= 1) { - return s; - } - return s * (x < 0 ? x / 0.5F : (x / (1 + x * x)) / 0.5F); -} - -static float smoothCurve(float x, float a) { - float r = a * x / (2 * a * x - a - x + 1); - r = (r - 0.5F) * 2; - return sc(r) * 0.5F + 0.5F; -} - -void TBAnimationObject::invokeOnAnimationStart() { - TBLinkListOf::Iterator li = m_listeners.iterateForward(); - onAnimationStart(); - while (TBAnimationListener *listener = li.getAndStep()) - listener->onAnimationStart(this); -} - -void TBAnimationObject::invokeOnAnimationUpdate(float progress) { - TBLinkListOf::Iterator li = m_listeners.iterateForward(); - onAnimationUpdate(progress); - while (TBAnimationListener *listener = li.getAndStep()) - listener->onAnimationUpdate(this, progress); -} - -void TBAnimationObject::invokeOnAnimationStop(bool aborted) { - TBLinkListOf::Iterator li = m_listeners.iterateForward(); - onAnimationStop(aborted); - while (TBAnimationListener *listener = li.getAndStep()) - listener->onAnimationStop(this, aborted); -} - -TBLinkListOf TBAnimationManager::animating_objects; -static int block_animations_counter = 0; - -// static -void TBAnimationManager::abortAllAnimations() { - while (TBAnimationObject *obj = animating_objects.getFirst()) - abortAnimation(obj, true); -} - -// static -void TBAnimationManager::update() { - core_trace_scoped(AnimationManagerUpdate); - double time_now = TBSystem::getTimeMS(); - - TBLinkListOf::Iterator iter = animating_objects.iterateForward(); - while (TBAnimationObject *obj = iter.getAndStep()) { - // Adjust the start time if it's the first update time for this object. - if (obj->adjust_start_time) { - obj->animation_start_time = time_now; - obj->adjust_start_time = false; - } - - // Calculate current progress - // If animation_duration is 0, it should just complete immediately. - float progress = 1.0f; - if (obj->animation_duration != 0) { - progress = (float)(time_now - obj->animation_start_time) / (float)obj->animation_duration; - progress = Min(progress, 1.0f); - } - - // Apply animation curve - float tmp; - switch (obj->animation_curve) { - case ANIMATION_CURVE_SLOW_DOWN: - tmp = 1 - progress; - progress = 1 - tmp * tmp * tmp; - break; - case ANIMATION_CURVE_SPEED_UP: - progress = progress * progress * progress; - break; - case ANIMATION_CURVE_BEZIER: - progress = SMOOTHSTEP(progress); - break; - case ANIMATION_CURVE_SMOOTH: - progress = smoothCurve(progress, 0.6f); - break; - default: // linear (progress is already linear) - break; - } - - // Update animation - obj->invokeOnAnimationUpdate(progress); - - // Remove completed animations - if (progress == 1.0f) { - animating_objects.remove(obj); - obj->invokeOnAnimationStop(false); - delete obj; - } - } -} - -// static -bool TBAnimationManager::hasAnimationsRunning() { - return animating_objects.hasLinks(); -} - -// static -void TBAnimationManager::startAnimation(TBAnimationObject *obj, ANIMATION_CURVE animationCurve, - double animationDuration, ANIMATION_TIME animationTime) { - if (obj->isAnimating()) - abortAnimation(obj, false); - if (isAnimationsBlocked()) - animationDuration = 0; - obj->adjust_start_time = (animationTime == ANIMATION_TIME_FIRST_UPDATE ? true : false); - obj->animation_start_time = TBSystem::getTimeMS(); - obj->animation_duration = Max(animationDuration, 0.0); - obj->animation_curve = animationCurve; - animating_objects.addLast(obj); - obj->invokeOnAnimationStart(); -} - -// static -void TBAnimationManager::abortAnimation(TBAnimationObject *obj, bool deleteAnimation) { - if (obj->isAnimating()) { - animating_objects.remove(obj); - obj->invokeOnAnimationStop(true); - if (deleteAnimation) - delete obj; - } -} - -// static -bool TBAnimationManager::isAnimationsBlocked() { - return block_animations_counter > 0; -} - -// static -void TBAnimationManager::beginBlockAnimations() { - block_animations_counter++; -} - -// static -void TBAnimationManager::endBlockAnimations() { - core_assert(block_animations_counter > 0); - block_animations_counter--; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/animation/tb_animation.h b/src/modules/ui/turbobadger/tb/animation/tb_animation.h deleted file mode 100644 index e08e26bba..000000000 --- a/src/modules/ui/turbobadger/tb/animation/tb_animation.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_linklist.h" -#include "tb_object.h" - -namespace tb { - -class TBAnimationObject; - -/** Defines how the animation progress value is interpolated. */ -enum ANIMATION_CURVE { - ANIMATION_CURVE_LINEAR, ///< Linear - ANIMATION_CURVE_SLOW_DOWN, ///< Fast start, slow end - ANIMATION_CURVE_SPEED_UP, ///< Slow start, fast end - ANIMATION_CURVE_BEZIER, ///< Slow start, slow end. Almost linear. - ANIMATION_CURVE_SMOOTH ///< Slow start, slow end. Stronger than ANIMATION_CURVE_BEZIER. -}; - -/** Defines what the animation duration time is relative to. */ -enum ANIMATION_TIME { - - /** The start time begins when the animation start in TBAnimationManager::startAnimation. */ - ANIMATION_TIME_IMMEDIATELY, - - /** The animation start in StartAnimation just as with ANIMATION_TIME_IMMEDIATELY, - but the start time is adjusted to when the animations Update is about to be called - the first time since it was started. - - Using this is most often preferable since starting a animation is often accompanied - with some extra work that might eat up a considerable time of the total duration (and - chop of the beginning of it). - - F.ex: Creating a window and starting its appearance animation. During initialization - of the window, you might initiate loading of additional resources. When that is done - and you finally end up updating animations, most of the animation time might already - have passed. If the animation start time is adjusted to the first update, the whole - animation will run from 0.0 - 1.0 smoothly when the initialization is done. */ - ANIMATION_TIME_FIRST_UPDATE -}; - -#define ANIMATION_DEFAULT_CURVE ANIMATION_CURVE_SLOW_DOWN -#define ANIMATION_DEFAULT_DURATION 200 - -/** TBAnimationListener - Listens to the progress of TBAnimationObject. */ - -class TBAnimationListener : public TBLinkOf { -public: - virtual ~TBAnimationListener(){}; - - /** Called after the animation object handled its own OnAnimationStart. - See TBAnimationObject::onAnimationStart for details. */ - virtual void onAnimationStart(TBAnimationObject *obj) = 0; - - /** Called after the animation object handled its own OnAnimationStart. - See TBAnimationObject::onAnimationUpdate for details. */ - virtual void onAnimationUpdate(TBAnimationObject *obj, float progress) = 0; - - /** Called after the animation object handled its own OnAnimationStart. - See TBAnimationObject::onAnimationStop for details. */ - virtual void onAnimationStop(TBAnimationObject *obj, bool aborted) = 0; -}; - -/** TBAnimationObject - Base class for all animated object */ - -class TBAnimationObject : public TBTypedObject, public TBLinkOf { -public: - ANIMATION_CURVE animation_curve; - double animation_start_time; - double animation_duration; - bool adjust_start_time; - -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBAnimationObject, TBTypedObject); - - virtual ~TBAnimationObject() { - } - - /** Return true if the object is currently animating. */ - bool isAnimating() const { - return linklist ? true : false; - } - - /** Called on animation start */ - virtual void onAnimationStart() = 0; - - /** Called on animation update. progress is current progress from 0 to 1. - Note that it isn't called on start, so progress 0 might not happen. - It will be called with progress 1 before the animation is completed normally (not aborted) */ - virtual void onAnimationUpdate(float progress) = 0; - - /** Called on animation stop. aborted is true if it was aborted before completion. - Note that if a animation is started when it's already running, it will first - be aborted and then started again. */ - virtual void onAnimationStop(bool aborted) = 0; - - /** Add an listener to this animation object. */ - void addListener(TBAnimationListener *listener) { - m_listeners.addLast(listener); - } - - /** Remove an listener from this animation object. */ - void removeListener(TBAnimationListener *listener) { - m_listeners.remove(listener); - } - -private: - friend class TBAnimationManager; - TBLinkListOf m_listeners; - void invokeOnAnimationStart(); - void invokeOnAnimationUpdate(float progress); - void invokeOnAnimationStop(bool aborted); -}; - -/** TBAnimationManager - System class that manages all animated object */ - -class TBAnimationManager { -private: - static TBLinkListOf animating_objects; - -public: - /** Update all running animations. */ - static void update(); - - /** Return true if there are running animations. */ - static bool hasAnimationsRunning(); - - static void startAnimation(TBAnimationObject *obj, ANIMATION_CURVE animation_curve = ANIMATION_DEFAULT_CURVE, - double animation_duration = ANIMATION_DEFAULT_DURATION, - ANIMATION_TIME animation_time = ANIMATION_TIME_FIRST_UPDATE); - /** Abort the animation. If delete_animation is true, the animation will be deleted in - this call after running callbacks and listeners callbacks. In rare situations, - you might want to keep the animation around and delete it later (or start it - again). */ - static void abortAnimation(TBAnimationObject *obj, bool delete_animation); - - /** Abort and delete all animations. */ - static void abortAllAnimations(); - - /** Return true if new animations are blocked. */ - static bool isAnimationsBlocked(); - - /** Begin a period of blocking new animations. End the period with EndBlockAnimations. - If StartAnimation is called during the blocked period, the animation object will - finish the next animation update as it completed normally. */ - static void beginBlockAnimations(); - - /** End a period of blocking new animations that was started with BeginBlockAnimations. */ - static void endBlockAnimations(); -}; - -/** TBAnimationBlocker blocks new animations during its lifetime. - It's convenient to put on the stack to block new animations - within a scope of code. */ -class TBAnimationBlocker { -public: - TBAnimationBlocker() { - TBAnimationManager::beginBlockAnimations(); - } - ~TBAnimationBlocker() { - TBAnimationManager::endBlockAnimations(); - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/animation/tb_animation_utils.h b/src/modules/ui/turbobadger/tb/animation/tb_animation_utils.h deleted file mode 100644 index 4233a290f..000000000 --- a/src/modules/ui/turbobadger/tb/animation/tb_animation_utils.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "animation/tb_animation.h" - -namespace tb { - -// TBAnimatedFloat - A animated float value - -class TBAnimatedFloat : public TBAnimationObject { -public: - float src_val; - float dst_val; - float current_progress; - -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBAnimatedFloat, TBAnimationObject); - - TBAnimatedFloat(float initial_value, ANIMATION_CURVE animation_curve = ANIMATION_DEFAULT_CURVE, - double animation_duration = ANIMATION_DEFAULT_DURATION) - : src_val(initial_value), dst_val(initial_value), current_progress(0) { - TBAnimationObject::animation_curve = animation_curve; - TBAnimationObject::animation_duration = animation_duration; - } - virtual ~TBAnimatedFloat() {} - - float getValue() const { - return src_val + (dst_val - src_val) * current_progress; - } - void setValueAnimated(float value) { - src_val = getValue(); - dst_val = value; - TBAnimationManager::startAnimation(this, animation_curve, animation_duration); - } - void setValueImmediately(float value) { - TBAnimationManager::abortAnimation(this, false); - src_val = dst_val = value; - onAnimationUpdate(1.0f); - } - - virtual void onAnimationStart() { - current_progress = 0; - } - virtual void onAnimationUpdate(float progress) { - current_progress = progress; - } - virtual void onAnimationStop(bool aborted) { - } -}; - -// TBFloatAnimator - Animates a external float value, which address is given in the constructor. - -class TBFloatAnimator : public TBAnimatedFloat { -public: - float *target_value; - -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBFloatAnimator, TBAnimationObject); - - TBFloatAnimator(float *target_value, ANIMATION_CURVE animation_curve = ANIMATION_DEFAULT_CURVE, - double animation_duration = ANIMATION_DEFAULT_DURATION) - : TBAnimatedFloat(*target_value), target_value(target_value) { - } - - virtual void onAnimationStart() { - TBAnimatedFloat::onAnimationStart(); - *target_value = getValue(); - } - virtual void onAnimationUpdate(float progress) { - TBAnimatedFloat::onAnimationUpdate(progress); - *target_value = getValue(); - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/animation/tb_widget_animation.cpp b/src/modules/ui/turbobadger/tb/animation/tb_widget_animation.cpp deleted file mode 100644 index 4f512055e..000000000 --- a/src/modules/ui/turbobadger/tb/animation/tb_widget_animation.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/** - * @file - */ - -#include "animation/tb_widget_animation.h" -#include "tb_list.h" -#include "tb_message_window.h" -#include "tb_widgets.h" -#include "tb_widgets_common.h" -#include "tb_window.h" - -namespace tb { - -TBLinkListOf widget_animations; - -#define LERP(src, dst, progress) (src + (dst - src) * progress) - -TBWidgetAnimationObject::TBWidgetAnimationObject(TBWidget *widget) : m_widget(widget) { - widget_animations.addLast(this); -} - -TBWidgetAnimationObject::~TBWidgetAnimationObject() { - widget_animations.remove(this); -} - -// == TBWidgetAnimationOpacity ============================================================ - -TBWidgetAnimationOpacity::TBWidgetAnimationOpacity(TBWidget *widget, float srcOpacity, float dstOpacity, bool die) - : TBWidgetAnimationObject(widget), m_src_opacity(srcOpacity), m_dst_opacity(dstOpacity), m_die(die) { -} - -void TBWidgetAnimationOpacity::onAnimationStart() { - // Make sure we don't stay idle if nothing is scheduled (hack). - // FIX: fix this properly - m_widget->invalidate(); - - m_widget->setOpacity(m_src_opacity); -} - -void TBWidgetAnimationOpacity::onAnimationUpdate(float progress) { - m_widget->setOpacity(LERP(m_src_opacity, m_dst_opacity, progress)); -} - -void TBWidgetAnimationOpacity::onAnimationStop(bool aborted) { - // If we're aborted, it may be because the widget is being deleted - if (m_die && !aborted) { - TBWidgetSafePointer the_widget(m_widget); - m_widget->removeFromParent(); - if (the_widget.get()) - delete the_widget.get(); - } else - m_widget->setOpacity(m_dst_opacity); -} - -TBWidgetAnimationRect::TBWidgetAnimationRect(TBWidget *widget, const TBRect &srcRect, const TBRect &dstRect) - : TBWidgetAnimationObject(widget), m_src_rect(srcRect), m_dst_rect(dstRect), m_mode(MODE_SRC_TO_DST) { -} - -TBWidgetAnimationRect::TBWidgetAnimationRect(TBWidget *widget, const TBRect &deltaRect, MODE mode) - : TBWidgetAnimationObject(widget), m_delta_rect(deltaRect), m_mode(mode) { - core_assert(mode == MODE_DELTA_IN || mode == MODE_DELTA_OUT); -} - -void TBWidgetAnimationRect::onAnimationStart() { - // Make sure we don't stay idle if nothing is scheduled (hack). - // FIX: fix this properly - m_widget->invalidate(); - - if (m_mode == MODE_SRC_TO_DST) - m_widget->setRect(m_src_rect); -} - -void TBWidgetAnimationRect::onAnimationUpdate(float progress) { - if (m_mode == MODE_DELTA_IN || m_mode == MODE_DELTA_OUT) { - m_dst_rect = m_src_rect = m_widget->getRect(); - if (m_dst_rect.equals(TBRect())) { - // Widget hasn't been laid out yet, - // the animation was started too soon. - //! \TODO this is certainly a BUG because it can be called from within the - // TBAnimationManager::update() loop which ALSO deletes the animation objevt. - TBAnimationManager::abortAnimation(this, true); - return; - } - if (m_mode == MODE_DELTA_IN) { - m_dst_rect.x += m_delta_rect.x; - m_dst_rect.y += m_delta_rect.y; - m_dst_rect.w += m_delta_rect.w; - m_dst_rect.h += m_delta_rect.h; - } else { - m_src_rect.x += m_delta_rect.x; - m_src_rect.y += m_delta_rect.y; - m_src_rect.w += m_delta_rect.w; - m_src_rect.h += m_delta_rect.h; - } - m_mode = MODE_SRC_TO_DST; - } - TBRect rect; - rect.x = (int)LERP(m_src_rect.x, m_dst_rect.x, progress); - rect.y = (int)LERP(m_src_rect.y, m_dst_rect.y, progress); - rect.w = (int)LERP(m_src_rect.w, m_dst_rect.w, progress); - rect.h = (int)LERP(m_src_rect.h, m_dst_rect.h, progress); - m_widget->setRect(rect); -} - -void TBWidgetAnimationRect::onAnimationStop(bool aborted) { - if (m_mode == MODE_SRC_TO_DST) // m_dst_rect may still be unset if aborted. - m_widget->setRect(m_dst_rect); -} - -// == TBWidgetsAnimationManager ===================================================== - -TBWidgetsAnimationManager widgets_animation_manager; - -void TBWidgetsAnimationManager::init() { - TBWidgetListener::addGlobalListener(&widgets_animation_manager); -} - -void TBWidgetsAnimationManager::shutdown() { - TBWidgetListener::removeGlobalListener(&widgets_animation_manager); -} - -void TBWidgetsAnimationManager::abortAnimations(TBWidget *widget) { - abortAnimations(widget, nullptr); -} - -void TBWidgetsAnimationManager::abortAnimations(TBWidget *widget, TB_TYPE_ID typeId) { - TBLinkListOf::Iterator iter = widget_animations.iterateForward(); - while (TBWidgetAnimationObject *wao = iter.getAndStep()) { - if (wao->m_widget == widget) { - // Skip this animation if we asked for a specific (and - // different) animation type. - if (typeId != nullptr && !wao->isOfTypeId(typeId)) - continue; - - // Abort the animation. This will both autoremove itself - // and delete it, so no need to do it here. - TBAnimationManager::abortAnimation(wao, true); - } - } -} - -void TBWidgetsAnimationManager::onWidgetDelete(TBWidget *widget) { - // Kill and delete all animations running for the widget being deleted. - abortAnimations(widget); -} - -bool TBWidgetsAnimationManager::onWidgetDying(TBWidget *widget) { - bool handled = false; - if (TBWindow *window = TBSafeCast(widget)) { - // Fade out dying windows - if (TBAnimationObject *anim = new TBWidgetAnimationOpacity(window, 1.f, TB_ALMOST_ZERO_OPACITY, true)) - TBAnimationManager::startAnimation(anim, ANIMATION_CURVE_BEZIER); - handled = true; - } - if (TBMessageWindow *window = TBSafeCast(widget)) { - // Move out dying message windows - if (TBAnimationObject *anim = - new TBWidgetAnimationRect(window, TBRect(0, 50, 0, 0), TBWidgetAnimationRect::MODE_DELTA_IN)) - TBAnimationManager::startAnimation(anim, ANIMATION_CURVE_SPEED_UP); - handled = true; - } - if (TBDimmer *dimmer = TBSafeCast(widget)) { - // Fade out dying dim layers - if (TBAnimationObject *anim = new TBWidgetAnimationOpacity(dimmer, 1.f, TB_ALMOST_ZERO_OPACITY, true)) - TBAnimationManager::startAnimation(anim, ANIMATION_CURVE_BEZIER); - handled = true; - } - return handled; -} - -void TBWidgetsAnimationManager::onWidgetAdded(TBWidget *parent, TBWidget *widget) { - if (TBWindow *window = TBSafeCast(widget)) { - // Fade in new windows - if (TBAnimationObject *anim = new TBWidgetAnimationOpacity(window, TB_ALMOST_ZERO_OPACITY, 1.f, false)) - TBAnimationManager::startAnimation(anim, ANIMATION_CURVE_BEZIER); - } - if (TBMessageWindow *window = TBSafeCast(widget)) { - // Move in new message windows - if (TBAnimationObject *anim = - new TBWidgetAnimationRect(window, TBRect(0, -50, 0, 0), TBWidgetAnimationRect::MODE_DELTA_OUT)) - TBAnimationManager::startAnimation(anim); - } - if (TBDimmer *dimmer = TBSafeCast(widget)) { - // Fade in dim layer - if (TBAnimationObject *anim = new TBWidgetAnimationOpacity(dimmer, TB_ALMOST_ZERO_OPACITY, 1.f, false)) - TBAnimationManager::startAnimation(anim, ANIMATION_CURVE_BEZIER); - } -} - -void TBWidgetsAnimationManager::onWidgetRemove(TBWidget *parent, TBWidget *widget) { -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/animation/tb_widget_animation.h b/src/modules/ui/turbobadger/tb/animation/tb_widget_animation.h deleted file mode 100644 index e7c9e54ee..000000000 --- a/src/modules/ui/turbobadger/tb/animation/tb_widget_animation.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "animation/tb_animation.h" -#include "tb_widgets_listener.h" - -namespace tb { - -/** Don't use 0.0 for opacity animations since that may break focus code. - At the moment a window should appear and start fading in from opacity 0, - it would also attempt setting the focus to it, but if opacity is 0 it will - think focus should not be set in that window and fail. */ -#define TB_ALMOST_ZERO_OPACITY 0.001f - -/** Base class for widget animations. This animation object will - be deleted automatically if the widget is deleted. */ -class TBWidgetAnimationObject : public TBAnimationObject, public TBLinkOf { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBWidgetAnimationObject, TBAnimationObject); - - TBWidgetAnimationObject(TBWidget *widget); - virtual ~TBWidgetAnimationObject(); - -public: - TBWidget *m_widget; -}; - -/** Animate the opacity of the target widget. */ -class TBWidgetAnimationOpacity : public TBWidgetAnimationObject { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBWidgetAnimationOpacity, TBWidgetAnimationObject); - - TBWidgetAnimationOpacity(TBWidget *widget, float src_opacity, float dst_opacity, bool die); - virtual void onAnimationStart() override; - virtual void onAnimationUpdate(float progress) override; - virtual void onAnimationStop(bool aborted) override; - -private: - float m_src_opacity; - float m_dst_opacity; - bool m_die; -}; - -/** Animate the rectangle of the target widget. */ -class TBWidgetAnimationRect : public TBWidgetAnimationObject { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBWidgetAnimationRect, TBWidgetAnimationObject); - - enum MODE { - /** Animate from source to dest. */ - MODE_SRC_TO_DST, - /** Animate from current + delta to current. */ - MODE_DELTA_IN, - /** Animate from current to current + delta. */ - MODE_DELTA_OUT - }; - /** Animate the widget between the given source and dest rectangle. */ - TBWidgetAnimationRect(TBWidget *widget, const TBRect &src_rect, const TBRect &dst_rect); - /** Animate the widget between rectangles based on the current widget - rectangle and a delta. The reference rectangle will be taken from - the target widget on the first OnAnimationUpdate. */ - TBWidgetAnimationRect(TBWidget *widget, const TBRect &delta_rect, MODE mode); - virtual void onAnimationStart() override; - virtual void onAnimationUpdate(float progress) override; - virtual void onAnimationStop(bool aborted) override; - -private: - TBRect m_src_rect; - TBRect m_dst_rect; - TBRect m_delta_rect; - MODE m_mode; -}; - -class TBWidgetsAnimationManager : public TBWidgetListener { -public: - virtual ~TBWidgetsAnimationManager() { - } - /** Init the widgets animation manager. */ - static void init(); - - /** Shutdown the widgets animation manager. */ - static void shutdown(); - - /** Abort all animations that are running for the given widget. */ - static void abortAnimations(TBWidget *widget); - - /** Abort all animations matching the given type that are running for the given widget. - This example will abort all opacity animations: - AbortAnimations(widget, TBTypedObject::getTypeId()) */ - static void abortAnimations(TBWidget *widget, TB_TYPE_ID type_id); - -private: - // == TBWidgetListener ================== - virtual void onWidgetDelete(TBWidget *widget) override; - virtual bool onWidgetDying(TBWidget *widget) override; - virtual void onWidgetAdded(TBWidget *parent, TBWidget *child) override; - virtual void onWidgetRemove(TBWidget *parent, TBWidget *child) override; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/image/tb_image_manager.cpp b/src/modules/ui/turbobadger/tb/image/tb_image_manager.cpp deleted file mode 100644 index af9bbb26c..000000000 --- a/src/modules/ui/turbobadger/tb/image/tb_image_manager.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/** - * @file - */ - -#include "tb_image_manager.h" -#include "tb_skin.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" - -namespace tb { - -TBImageRep::TBImageRep(TBImageManager *imageManager, TBBitmapFragment *fragment, uint32_t hashKey) - : ref_count(0), hash_key(hashKey), image_manager(imageManager), fragment(fragment) { -} - -void TBImageRep::incRef() { - ref_count++; -} - -void TBImageRep::decRef() { - ref_count--; - if (ref_count == 0) { - if (image_manager != nullptr) { - image_manager->removeImageRep(this); - } - delete this; - } -} - -TBImage::TBImage(TBImageRep *rep) : m_image_rep(rep) { - if (m_image_rep != nullptr) { - m_image_rep->incRef(); - } -} - -TBImage::TBImage(const TBImage &image) : m_image_rep(image.m_image_rep) { - if (m_image_rep != nullptr) { - m_image_rep->incRef(); - } -} - -TBImage::~TBImage() { - if (m_image_rep != nullptr) { - m_image_rep->decRef(); - } -} - -bool TBImage::isEmpty() const { - return !(m_image_rep && m_image_rep->fragment); -} - -int TBImage::width() const { - if (m_image_rep && m_image_rep->fragment) - return m_image_rep->fragment->width(); - return 0; -} - -int TBImage::height() const { - if (m_image_rep && m_image_rep->fragment) - return m_image_rep->fragment->height(); - return 0; -} - -TBBitmapFragment *TBImage::getBitmap() const { - return m_image_rep ? m_image_rep->fragment : nullptr; -} - -void TBImage::setImageRep(TBImageRep *imageRep) { - if (m_image_rep == imageRep) { - return; - } - - if (m_image_rep != nullptr) { - m_image_rep->decRef(); - } - - m_image_rep = imageRep; - - if (m_image_rep != nullptr) { - m_image_rep->incRef(); - } -} - -TBImageManager *g_image_manager = nullptr; - -TBImageManager::TBImageManager() { - g_renderer->addListener(this); -} - -TBImageManager::~TBImageManager() { - g_renderer->removeListener(this); - - // If there is TBImageRep objects live, we must unset the fragment pointer - // since the m_frag_manager is going to be destroyed very soon. - TBHashTableIteratorOf it(&m_image_rep_hash); - while (TBImageRep *image_rep = it.getNextContent()) { - image_rep->fragment = nullptr; - image_rep->image_manager = nullptr; - } -} - -TBImage TBImageManager::getImage(const char *filename) { - uint32_t hash_key = TBGetHash(filename); - TBImageRep *image_rep = m_image_rep_hash.get(hash_key); - if (image_rep == nullptr) { - // Load a fragment. Load a destination DPI bitmap if available. - TBBitmapFragment *fragment = nullptr; - if (g_tb_skin->getDimensionConverter()->needConversion()) { - TBTempBuffer filename_dst_DPI; - g_tb_skin->getDimensionConverter()->getDstDPIFilename(filename, &filename_dst_DPI); - fragment = m_frag_manager.getFragmentFromFile(filename_dst_DPI.getData(), false); - } - if (!fragment) - fragment = m_frag_manager.getFragmentFromFile(filename, false); - - image_rep = new TBImageRep(this, fragment, hash_key); - if (!image_rep || !fragment || !m_image_rep_hash.add(hash_key, image_rep)) { - delete image_rep; - m_frag_manager.freeFragment(fragment); - image_rep = nullptr; - } - Log::debug(image_rep ? "TBImageManager - Loaded new image.\n" : "TBImageManager - Loading image failed."); - } - return TBImage(image_rep); -} - -TBImage TBImageManager::getImage(const char *name, uint32_t *buffer, int width, int height) { - uint32_t hash_key = TBGetHash(name); - TBImageRep *image_rep = m_image_rep_hash.get(hash_key); - if (image_rep == nullptr) { - TBID id(name); - TBBitmapFragment *fragment = m_frag_manager.createNewFragment(id, false, width, height, width, buffer); - - image_rep = new TBImageRep(this, fragment, hash_key); - if (!image_rep || !fragment || !m_image_rep_hash.add(hash_key, image_rep)) { - delete image_rep; - m_frag_manager.freeFragment(fragment); - image_rep = nullptr; - } - Log::debug(image_rep ? "TBImageManager - Loaded new image.\n" : "TBImageManager - Loading image failed."); - } - return TBImage(image_rep); -} - -void TBImageManager::removeImageRep(TBImageRep *imageRep) { - core_assert(imageRep->ref_count == 0); - if (imageRep->fragment) { - m_frag_manager.freeFragment(imageRep->fragment); - imageRep->fragment = nullptr; - } - m_image_rep_hash.remove(imageRep->hash_key); - imageRep->image_manager = nullptr; - Log::debug("TBImageManager - Removed image."); -} - -void TBImageManager::onContextLost() { - m_frag_manager.deleteBitmaps(); -} - -void TBImageManager::onContextRestored() { - // No need to do anything. The bitmaps will be created when drawing. -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/image/tb_image_manager.h b/src/modules/ui/turbobadger/tb/image/tb_image_manager.h deleted file mode 100644 index e98f92c26..000000000 --- a/src/modules/ui/turbobadger/tb/image/tb_image_manager.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_bitmap_fragment.h" -#include "tb_core.h" -#include "tb_hashtable.h" -#include "tb_linklist.h" -#include "tb_renderer.h" - -namespace tb { - -class TBImageManager; - -/** TBImageRep is the internal contents of a TBImage. Owned by reference counting from TBImage. */ -class TBImageRep { - friend class TBImageManager; - friend class TBImage; - - TBImageRep(TBImageManager *imageManager, TBBitmapFragment *fragment, uint32_t hashKey); - - void incRef(); - void decRef(); - - int ref_count; - uint32_t hash_key; - TBImageManager *image_manager; - TBBitmapFragment *fragment; -}; - -/** TBImage is a reference counting object representing a image loaded by TBImageManager. - As long as there are TBImage objects for a certain image, it will be kept loaded in memory. - It may be empty if the image has not yet been set, or if the TBImageManager is destroyed - when the image is still alive. -*/ -class TBImage { -public: - TBImage() : m_image_rep(nullptr) { - } - TBImage(TBImageRep *rep); - TBImage(const TBImage &image); - ~TBImage(); - - /** Return true if this image is empty. */ - bool isEmpty() const; - - /** Return the width of this image, or 0 if empty. */ - int width() const; - - /** Return the height of this image, or 0 if empty. */ - int height() const; - - /** Return the bitmap fragment for this image, or nullptr if empty. */ - TBBitmapFragment *getBitmap() const; - - const TBImage &operator=(const TBImage &image) { - setImageRep(image.m_image_rep); - return *this; - } - bool operator==(const TBImage &image) const { - return m_image_rep == image.m_image_rep; - } - bool operator!=(const TBImage &image) const { - return m_image_rep != image.m_image_rep; - } - -private: - void setImageRep(TBImageRep *image_rep); - TBImageRep *m_image_rep; -}; - -/** TBImageManager loads images returned as TBImage objects. - - It internally use a TBBitmapFragmentManager that create fragment maps for loaded images, - and keeping track of which images are loaded so they are not loaded several times. - - Images are forgotten when there are no longer any TBImage objects for a given file. -*/ - -class TBImageManager : private TBRendererListener { -public: - TBImageManager(); - ~TBImageManager(); - - /** Return a image object for the given filename. - If it fails, the returned TBImage object will be empty. */ - TBImage getImage(const char *filename); - TBImage getImage(const char *name, uint32_t *buffer, int width, int height); - -#ifdef TB_RUNTIME_DEBUG_INFO - /** Render the skin bitmaps on screen, to analyze fragment positioning. */ - void debug() { - m_frag_manager.debug(); - } -#endif - - // Implementing TBRendererListener - virtual void onContextLost(); - virtual void onContextRestored(); - -private: - TBBitmapFragmentManager m_frag_manager; - TBHashTableOf m_image_rep_hash; - - friend class TBImageRep; - void removeImageRep(TBImageRep *image_rep); -}; - -/** The global TBImageManager. */ -extern TBImageManager *g_image_manager; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/image/tb_image_widget.cpp b/src/modules/ui/turbobadger/tb/image/tb_image_widget.cpp deleted file mode 100644 index ee30c6540..000000000 --- a/src/modules/ui/turbobadger/tb/image/tb_image_widget.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file - */ - -#include "image/tb_image_widget.h" -#include "tb_node_tree.h" -#include "tb_widgets_reader.h" - -namespace tb { - -PreferredSize TBImageWidget::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - return PreferredSize(m_image.width(), m_image.height()); -} - -void TBImageWidget::onPaint(const PaintProps &paintProps) { - if (TBBitmapFragment *fragment = m_image.getBitmap()) - g_renderer->drawBitmap(getPaddingRect(), TBRect(0, 0, m_image.width(), m_image.height()), fragment); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/image/tb_image_widget.h b/src/modules/ui/turbobadger/tb/image/tb_image_widget.h deleted file mode 100644 index 7533bcb38..000000000 --- a/src/modules/ui/turbobadger/tb/image/tb_image_widget.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_image_manager.h" -#include "tb_object.h" -#include "tb_widgets.h" - -namespace tb { - -/** TBImageWidget is a widget showing a image loaded by TBImageManager, - constrained in size to its skin. - If you need to show a image from the skin, you can use TBSkinImage. */ -class TBImageWidget : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBImageWidget, TBWidget); - - TBImageWidget() { - } - - void setImage(const TBImage &image) { - m_image = image; - } - void setImage(const char *filename) { - m_image = g_image_manager->getImage(filename); - } - - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override; - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual void onPaint(const PaintProps &paintProps) override; - -private: - TBImage m_image; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/parser/tb_parser.cpp b/src/modules/ui/turbobadger/tb/parser/tb_parser.cpp deleted file mode 100644 index 38b4de4bc..000000000 --- a/src/modules/ui/turbobadger/tb/parser/tb_parser.cpp +++ /dev/null @@ -1,422 +0,0 @@ -/** - * @file - */ - -#include "parser/tb_parser.h" -#include "core/Assert.h" -#include "tb_tempbuffer.h" -#include "utf8/utf8.h" -#include - -namespace tb { - -static bool is_hex(char c) { - return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); -} - -static uint32_t parse_hex(char *&src, int maxCount) { - uint32_t hex = 0; - for (int i = 0; i < maxCount; i++) { - char c = *src; - if (!is_hex(c)) { - break; - } - hex <<= 4; - hex |= SDL_isdigit(c) != 0 ? c - '0' : SDL_tolower(c) - 'a' + 10; - src++; - } - return hex; -} - -void unescapeString(char *str) { - // fast forward to any escape sequence - while ((*str != 0) && *str != '\\') { - str++; - } - - char *dst = str; - char *src = str; - while (*src != 0) { - if (*src == '\\') { - bool code_found = true; - switch (src[1]) { - case 'a': - *dst = '\a'; - break; - case 'b': - *dst = '\b'; - break; - case 'f': - *dst = '\f'; - break; - case 'n': - *dst = '\n'; - break; - case 'r': - *dst = '\r'; - break; - case 't': - *dst = '\t'; - break; - case 'v': - *dst = '\v'; - break; - case '0': - *dst = '\0'; - break; - case '\"': - *dst = '\"'; - break; - case '\'': - *dst = '\''; - break; - case '\\': - *dst = '\\'; - break; - case 'x': // \xXX - case 'u': // \uXXXX - { - // This should be safe. A utf-8 character can be at most 4 bytes, - // and we have 4 bytes to use for \xXX and 6 for \uXXXX. - src += 2; - if (UCS4 hex = parse_hex(src, src[1] == 'x' ? 2 : 4)) - dst += utf8::encode(hex, dst); - continue; - } - default: - code_found = false; - } - if (code_found) { - src += 2; - dst++; - continue; - } - } - *dst = *src; - dst++; - src++; - } - *dst = 0; -} - -bool is_white_space(const char *str) { - switch (*str) { - case ' ': - case '\t': - return true; - default: - return false; - } -} - -/** Return true if the given string starts with a color. - Ex: #ffdd00, #fd0 */ -bool is_start_of_color(const char *str) { - if (*str++ != '#') { - return false; - } - int digit_count = 0; - while (is_hex(*str)) { - str++; - digit_count++; - } - return digit_count == 8 || digit_count == 6 || digit_count == 4 || digit_count == 3; -} - -/** Return true if the given string may be a node reference, such - as language strings or TBNodeRefTree references. */ -bool is_start_of_reference(const char *str) { - if (*str++ != '@') { - return false; - } - while ((*str != 0) && *str != ' ') { - // If the token ends with colon, it's not a value but a key. - if (*str == ':') { - return false; - } - str++; - } - return true; -} - -/** Check if the line is a comment or empty space. If it is, consume the leading - whitespace from line. */ -bool is_space_or_comment(char *&line) { - char *tmp = line; - while (is_white_space(tmp)) { - tmp++; - } - if (*tmp == '#' || *tmp == 0) { - line = tmp; - return true; - } - return false; -} - -bool is_pending_multiline(const char *str) { - while (is_white_space(str)) { - str++; - } - return str[0] == '\\' && str[1] == 0; -} - -bool isEndQuote(const char *bufStart, const char *buf, const char quoteType) { - if (*buf != quoteType) { - return false; - } - int num_backslashes = 0; - while (bufStart < buf && *(buf-- - 1) == '\\') { - num_backslashes++; - } - return (num_backslashes & 1) == 0; -} - -TBParser::STATUS TBParser::read(TBParserStream *stream, TBParserTarget *target) { - TBTempBuffer line, work; - if (!line.reserve(1024) || !work.reserve(1024)) - return STATUS_OUT_OF_MEMORY; - - current_indent = 0; - current_line_nr = 1; - pending_multiline = false; - multi_line_sub_level = 0; - - while (int read_len = stream->getMoreData((char *)work.getData(), work.getCapacity())) { - char *buf = work.getData(); - - // Skip BOM (BYTE ORDER MARK) character, often in the beginning of UTF-8 documents. - if (current_line_nr == 1 && read_len > 3 && (uint8_t)buf[0] == 239 && (uint8_t)buf[1] == 187 && - (uint8_t)buf[2] == 191) { - read_len -= 3; - buf += 3; - } - - int line_pos = 0; - while (true) { - // Find line end - int line_start = line_pos; - while (line_pos < read_len && buf[line_pos] != '\n') - line_pos++; - - if (line_pos < read_len) { - // We have a line - // Skip preceding \r (if we have one) - int line_len = line_pos - line_start; - if (!line.append(buf + line_start, line_len)) - return STATUS_OUT_OF_MEMORY; - - // Strip away trailing '\r' if the line has it - char *linebuf = line.getData(); - int linebuf_len = line.getAppendPos(); - if (linebuf_len > 0 && linebuf[linebuf_len - 1] == '\r') - linebuf[linebuf_len - 1] = 0; - - // Terminate the line string - if (!line.append("", 1)) - return STATUS_OUT_OF_MEMORY; - - // Handle line - onLine(line.getData(), target); - current_line_nr++; - - line.resetAppendPos(); - line_pos++; // Skip this \n - // Find next line - continue; - } - // No more lines here so push the rest and break for more data - if (!line.append(buf + line_start, read_len - line_start)) - return STATUS_OUT_OF_MEMORY; - break; - } - } - if (line.getAppendPos()) { - if (!line.append("", 1)) - return STATUS_OUT_OF_MEMORY; - onLine(line.getData(), target); - current_line_nr++; - } - return STATUS_OK; -} - -void TBParser::onLine(char *line, TBParserTarget *target) { - if (is_space_or_comment(line)) { - if (*line == '#') - target->onComment(current_line_nr, line + 1); - return; - } - if (pending_multiline) { - onMultiline(line, target); - return; - } - - // Check indent - int indent = 0; - while (line[indent] == '\t' && line[indent] != 0) - indent++; - line += indent; - - if (indent - current_indent > 1) { - target->onError(current_line_nr, "Indentation error. (Line skipped)"); - return; - } - - if (indent > current_indent) { - // FIX: Report indentation error if more than 1 higher! - core_assert(indent - current_indent == 1); - target->enter(); - current_indent++; - } else if (indent < current_indent) { - while (indent < current_indent) { - target->leave(); - current_indent--; - } - } - - if (*line == 0) - return; - else { - char *token = line; - // Read line while consuming it and copy over to token buf - while (!is_white_space(line) && *line != 0) - line++; - int token_len = line - token; - // Consume any white space after the token - while (is_white_space(line)) - line++; - - bool is_compact_line = token_len && token[token_len - 1] == ':'; - - TBValue value; - if (is_compact_line) { - token_len--; - token[token_len] = 0; - - // Check if the first argument is not a child but the value for this token - if (*line == '[' || *line == '\"' || *line == '\'' || is_start_of_number(line) || is_start_of_color(line) || - is_start_of_reference(line)) { - consumeValue(value, line); - - if (pending_multiline) { - // The value wrapped to the next line, so we should remember the token and continue. - multi_line_token = token; - return; - } - } - } else if (token[token_len]) { - token[token_len] = 0; - unescapeString(line); - value.setFromStringAuto(line, TBValue::SET_AS_STATIC); - } - target->onToken(current_line_nr, token, value); - - if (is_compact_line) - onCompactLine(line, target); - } -} - -void TBParser::onCompactLine(char *line, TBParserTarget *target) { - target->enter(); - while (*line) { - // consume any whitespace - while (is_white_space(line)) - line++; - - // Find token - char *token = line; - while (*line != ':' && *line != 0) - line++; - if (!*line) - break; // Syntax error, expected token - *line++ = 0; - - // consume any whitespace - while (is_white_space(line)) - line++; - - TBValue v; - consumeValue(v, line); - - if (pending_multiline) { - // The value wrapped to the next line, so we should remember the token and continue. - multi_line_token = token; - // Since we need to call target->Leave when the multiline is ready, set multi_line_sub_level. - multi_line_sub_level = 1; - return; - } - - // Ready - target->onToken(current_line_nr, token, v); - } - - target->leave(); -} - -void TBParser::onMultiline(char *line, TBParserTarget *target) { - // consume any whitespace - while (is_white_space(line)) - line++; - - TBValue value; - consumeValue(value, line); - - if (!pending_multiline) { - // Ready with all lines - value.setString(multi_line_value.getData(), TBValue::SET_AS_STATIC); - target->onToken(current_line_nr, multi_line_token.c_str(), value); - - if (multi_line_sub_level) - target->leave(); - - // Reset - multi_line_value.setAppendPos(0); - multi_line_sub_level = 0; - } -} - -void TBParser::consumeValue(TBValue &dstValue, char *&line) { - // Find value (As quoted string, or as auto) - char *value = line; - if (*line == '\"' || *line == '\'') { - const char quote_type = *line; - // Consume starting quote - line++; - value++; - // Find ending quote or end - while (!isEndQuote(value, line, quote_type) && *line != 0) - line++; - // Terminate away the quote - if (*line == quote_type) - *line++ = 0; - - // consume any whitespace - while (is_white_space(line)) - line++; - // consume any comma - if (*line == ',') - line++; - - unescapeString(value); - dstValue.setString(value, TBValue::SET_AS_STATIC); - } else { - // Find next comma or end - while (*line != ',' && *line != 0) - line++; - // Terminate away the comma - if (*line == ',') - *line++ = 0; - - unescapeString(value); - dstValue.setFromStringAuto(value, TBValue::SET_AS_STATIC); - } - - // Check if we still have pending value data on the following line and set pending_multiline. - bool continuing_multiline = pending_multiline; - pending_multiline = is_pending_multiline(line); - - // Append the multi line value to the buffer. - if (continuing_multiline || pending_multiline) - multi_line_value.appendString(dstValue.getString()); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/parser/tb_parser.h b/src/modules/ui/turbobadger/tb/parser/tb_parser.h deleted file mode 100644 index 358e67013..000000000 --- a/src/modules/ui/turbobadger/tb/parser/tb_parser.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_str.h" -#include "tb_tempbuffer.h" -#include "tb_value.h" - -namespace tb { - -/** Unescape backslash codes. This is done in place using the string both as source - and destination. */ -void unescapeString(char *str); - -/** Check if buf is pointing at an end quote. It may need to iterate - buf backwards toward buf_start to check if any preceding backslashes - make it a escaped quote (which should not be the end quote) */ -bool isEndQuote(const char *buf_start, const char *buf, const char quote_type); - -class TBParserTarget { -public: - virtual ~TBParserTarget() { - } - virtual void onError(int line_nr, const char *error) = 0; - virtual void onComment(int line_nr, const char *comment) = 0; - virtual void onToken(int line_nr, const char *name, TBValue &value) = 0; - virtual void enter() = 0; - virtual void leave() = 0; -}; - -class TBParserStream { -public: - virtual ~TBParserStream() { - } - virtual int getMoreData(char *buf, int buf_len) = 0; -}; - -class TBParser { -public: - enum STATUS { STATUS_OK, STATUS_OUT_OF_MEMORY, STATUS_PARSE_ERROR }; - TBParser() { - } - STATUS read(TBParserStream *stream, TBParserTarget *target); - -private: - int current_indent = 0; - int current_line_nr = 0; - core::String multi_line_token; - TBTempBuffer multi_line_value; - int multi_line_sub_level = 0; - bool pending_multiline = false; - void onLine(char *line, TBParserTarget *target); - void onCompactLine(char *line, TBParserTarget *target); - void onMultiline(char *line, TBParserTarget *target); - void consumeValue(TBValue &dst_value, char *&line); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/renderers/tb_renderer_batcher.cpp b/src/modules/ui/turbobadger/tb/renderers/tb_renderer_batcher.cpp deleted file mode 100644 index 1338aaa56..000000000 --- a/src/modules/ui/turbobadger/tb/renderers/tb_renderer_batcher.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/** - * @file - */ - -#include "renderers/tb_renderer_batcher.h" -#include "tb_bitmap_fragment.h" -#include "tb_system.h" - -namespace tb { - -// == TBRendererBatcher::Batch ========================================================== - -#ifdef TB_RUNTIME_DEBUG_INFO -uint32_t dbg_begin_paint_batch_id = 0; -uint32_t dbg_frame_triangle_count = 0; -#endif // TB_RUNTIME_DEBUG_INFO - -#define VER_COL(r, g, b, a) (((a) << 24) + ((b) << 16) + ((g) << 8) + r) -#define VER_COL_OPACITY(a) (0x00ffffff + (((uint32_t)a) << 24)) - -void TBRendererBatcher::Batch::flush(TBRendererBatcher *batchRenderer) { - if (!vertex_count || is_flushing) { - return; - } - - // Prevent re-entrancy. Calling fragment->getBitmap may end up calling TBBitmap::setData - // which will end up flushing any existing batch with that bitmap. - is_flushing = true; - - if (fragment) { - // Now it's time to ensure the bitmap data is up to date. A call to GetBitmap - // with TB_VALIDATE_ALWAYS should guarantee that its data is validated. - TBBitmap *frag_bitmap = fragment->getBitmap(TB_VALIDATE_ALWAYS); - ((void)frag_bitmap); // silence warning about unused variable - core_assert(frag_bitmap == bitmap); - } - - batchRenderer->renderBatch(this); - -#ifdef TB_RUNTIME_DEBUG_INFO - if (TB_DEBUG_SETTING(RENDER_BATCHES)) { - // This assumes we're drawing triangles. Need to modify this - // if we start using strips, fans or whatever. - dbg_frame_triangle_count += vertex_count / 3; - - // Draw the triangles again using a random color based on the batch - // id. This indicates which triangles belong to the same batch. - uint32_t id = batch_id - dbg_begin_paint_batch_id; - uint32_t hash = id * (2166136261U ^ id); - uint32_t color = 0xAA000000 + (hash & 0x00FFFFFF); - for (int i = 0; i < vertex_count; i++) - vertex[i].col = color; - bitmap = nullptr; - batchRenderer->renderBatch(this); - } -#endif // TB_RUNTIME_DEBUG_INFO - - vertex_count = 0; - - ++batch_id; // Will overflow eventually, but that doesn't really matter. - - is_flushing = false; -} - -TBRendererBatcher::Vertex *TBRendererBatcher::Batch::reserve(TBRendererBatcher *batchRenderer, int count) { - core_assert(count < VERTEX_BATCH_SIZE); - if (vertex_count + count > VERTEX_BATCH_SIZE) { - flush(batchRenderer); - } - Vertex *ret = &vertex[vertex_count]; - vertex_count += count; - return ret; -} - -// == TBRendererBatcher =================================================================== - -TBRendererBatcher::TBRendererBatcher() - : m_opacity(255), m_translation_x(0), m_translation_y(0) { -} - -TBRendererBatcher::~TBRendererBatcher() { -} - -void TBRendererBatcher::beginPaint(int renderTargetW, int renderTargetH) { -#ifdef TB_RUNTIME_DEBUG_INFO - dbg_begin_paint_batch_id = batch.batch_id; - dbg_frame_triangle_count = 0; -#endif // TB_RUNTIME_DEBUG_INFO - - m_screen_rect.set(0, 0, renderTargetW, renderTargetH); - m_clip_rect = m_screen_rect; -} - -void TBRendererBatcher::endPaint() { - flushAllInternal(); - -#ifdef TB_RUNTIME_DEBUG_INFO - if (TB_DEBUG_SETTING(RENDER_BATCHES)) - Log::debug("Frame rendered using %d batches and a total of %d triangles.", - batch.batch_id - dbg_begin_paint_batch_id, dbg_frame_triangle_count); -#endif // TB_RUNTIME_DEBUG_INFO -} - -void TBRendererBatcher::translate(int dx, int dy) { - m_translation_x += dx; - m_translation_y += dy; -} - -void TBRendererBatcher::setOpacity(float opacity) { - int8_t opacity8 = (uint8_t)(opacity * 255); - if (opacity8 == m_opacity) - return; - m_opacity = opacity8; -} - -float TBRendererBatcher::getOpacity() { - return m_opacity / 255.f; -} - -TBRect TBRendererBatcher::setClipRect(const TBRect &rect, bool addToCurrent) { - TBRect old_clip_rect = m_clip_rect; - m_clip_rect = rect; - m_clip_rect.x += m_translation_x; - m_clip_rect.y += m_translation_y; - - if (addToCurrent) { - m_clip_rect = m_clip_rect.clip(old_clip_rect); - } - - flushAllInternal(); - setClipRect(m_clip_rect); - - old_clip_rect.x -= m_translation_x; - old_clip_rect.y -= m_translation_y; - return old_clip_rect; -} - -TBRect TBRendererBatcher::getClipRect() { - TBRect curr_clip_rect = m_clip_rect; - curr_clip_rect.x -= m_translation_x; - curr_clip_rect.y -= m_translation_y; - return curr_clip_rect; -} - -void TBRendererBatcher::drawBitmap(const TBRect &dstRect, const TBRect &srcRect, TBBitmapFragment *bitmapFragment) { - if (TBBitmap *bitmap = bitmapFragment->getBitmap(TB_VALIDATE_FIRST_TIME)) - addQuadInternal(dstRect.offset(m_translation_x, m_translation_y), - srcRect.offset(bitmapFragment->m_rect.x, bitmapFragment->m_rect.y), VER_COL_OPACITY(m_opacity), - bitmap, bitmapFragment); -} - -void TBRendererBatcher::drawBitmap(const TBRect &dstRect, const TBRect &srcRect, TBBitmap *bitmap) { - addQuadInternal(dstRect.offset(m_translation_x, m_translation_y), srcRect, VER_COL_OPACITY(m_opacity), bitmap, - nullptr); -} - -void TBRendererBatcher::drawBitmapColored(const TBRect &dstRect, const TBRect &srcRect, const TBColor &color, - TBBitmapFragment *bitmapFragment) { - if (TBBitmap *bitmap = bitmapFragment->getBitmap(TB_VALIDATE_FIRST_TIME)) { - uint32_t a = (color.a * m_opacity) / 255; - addQuadInternal(dstRect.offset(m_translation_x, m_translation_y), - srcRect.offset(bitmapFragment->m_rect.x, bitmapFragment->m_rect.y), - VER_COL(color.r, color.g, color.b, a), bitmap, bitmapFragment); - } -} - -void TBRendererBatcher::drawBitmapColored(const TBRect &dstRect, const TBRect &srcRect, const TBColor &color, - TBBitmap *bitmap) { - uint32_t a = (color.a * m_opacity) / 255; - addQuadInternal(dstRect.offset(m_translation_x, m_translation_y), srcRect, VER_COL(color.r, color.g, color.b, a), - bitmap, nullptr); -} - -void TBRendererBatcher::drawBitmapTile(const TBRect &dstRect, TBBitmap *bitmap) { - addQuadInternal(dstRect.offset(m_translation_x, m_translation_y), TBRect(0, 0, dstRect.w, dstRect.h), - VER_COL_OPACITY(m_opacity), bitmap, nullptr); -} - -void TBRendererBatcher::addQuadInternal(const TBRect &dstRect, const TBRect &srcRect, uint32_t color, TBBitmap *bitmap, - TBBitmapFragment *fragment) { - if (batch.bitmap != bitmap) { - batch.flush(this); - batch.bitmap = bitmap; - } - batch.fragment = fragment; - - const int bitmap_w = bitmap->width(); - const int bitmap_h = bitmap->height(); - const float m_u = (float)srcRect.x / bitmap_w; - const float m_v = (float)srcRect.y / bitmap_h; - const float xw = (float)(srcRect.x + srcRect.w); - const float yh = (float)(srcRect.y + srcRect.h); - const float m_uu = xw / bitmap_w; - const float m_vv = yh / bitmap_h; - - Vertex *ver = batch.reserve(this, 6); - ver[0].x = (float)dstRect.x; - ver[0].y = (float)(dstRect.y + dstRect.h); - ver[0].u = m_u; - ver[0].v = m_vv; - ver[0].col = color; - ver[1].x = (float)(dstRect.x + dstRect.w); - ver[1].y = ver[0].y; - ver[1].u = m_uu; - ver[1].v = m_vv; - ver[1].col = color; - ver[2].x = ver[0].x; - ver[2].y = (float)dstRect.y; - ver[2].u = m_u; - ver[2].v = m_v; - ver[2].col = color; - - ver[3].x = ver[0].x; - ver[3].y = ver[2].y; - ver[3].u = m_u; - ver[3].v = m_v; - ver[3].col = color; - ver[4].x = ver[1].x; - ver[4].y = ver[0].y; - ver[4].u = m_uu; - ver[4].v = m_vv; - ver[4].col = color; - ver[5].x = ver[1].x; - ver[5].y = ver[2].y; - ver[5].u = m_uu; - ver[5].v = m_v; - ver[5].col = color; - - // Update fragments batch id (See FlushBitmapFragment) - if (fragment) - fragment->m_batch_id = batch.batch_id; -} - -void TBRendererBatcher::flushAllInternal() { - batch.flush(this); -} - -void TBRendererBatcher::flushBitmap(TBBitmap *bitmap) { - // Flush the batch if it's using this bitmap (that is about to change or be deleted) - if (batch.vertex_count && bitmap == batch.bitmap) { - batch.flush(this); - } -} - -void TBRendererBatcher::flushBitmapFragment(TBBitmapFragment *bitmapFragment) { - // Flush the batch if it is using this fragment (that is about to change or be deleted) - // We know if it is in use in the current batch if its batch_id matches the current - // batch_id in our (one and only) batch. - // If we switch to a more advance batching system with multiple batches, we need to - // solve this a bit differently. - if (batch.vertex_count && bitmapFragment->m_batch_id == batch.batch_id) { - batch.flush(this); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/renderers/tb_renderer_batcher.h b/src/modules/ui/turbobadger/tb/renderers/tb_renderer_batcher.h deleted file mode 100644 index 31ad7ebb9..000000000 --- a/src/modules/ui/turbobadger/tb/renderers/tb_renderer_batcher.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_renderer.h" - -namespace tb { - -#define VERTEX_BATCH_SIZE 65536 - -/** TBRendererBatcher is a helper class that implements batching of draw operations for a TBRenderer. - If you do not want to do your own batching you can subclass this class instead of TBRenderer. - If overriding any function in this class, make sure to call the base class too. */ -class TBRendererBatcher : public TBRenderer { -public: - /** Vertex stored in a Batch */ - struct Vertex { - float x, y; - float u, v; - union { - struct { - unsigned char r, g, b, a; - }; - uint32_t col; - }; - }; - /** A batch which should be rendered. */ - class Batch { - public: - Batch() : vertex_count(0), bitmap(nullptr), fragment(nullptr), batch_id(0), is_flushing(false) { - } - void flush(TBRendererBatcher *batch_renderer); - Vertex *reserve(TBRendererBatcher *batch_renderer, int count); - - alignas(16) Vertex vertex[VERTEX_BATCH_SIZE]; - int vertex_count; - - TBBitmap *bitmap; - TBBitmapFragment *fragment; - - uint32_t batch_id; - bool is_flushing; - }; - - TBRendererBatcher(); - virtual ~TBRendererBatcher(); - - virtual void beginPaint(int render_target_w, int render_target_h); - virtual void endPaint(); - - virtual void translate(int dx, int dy); - - virtual void setOpacity(float opacity); - virtual float getOpacity(); - - virtual TBRect setClipRect(const TBRect &rect, bool add_to_current); - virtual TBRect getClipRect(); - - virtual void drawBitmap(const TBRect &dst_rect, const TBRect &src_rect, TBBitmapFragment *bitmap_fragment); - virtual void drawBitmap(const TBRect &dst_rect, const TBRect &src_rect, TBBitmap *bitmap); - virtual void drawBitmapColored(const TBRect &dst_rect, const TBRect &src_rect, const TBColor &color, - TBBitmapFragment *bitmap_fragment); - virtual void drawBitmapColored(const TBRect &dst_rect, const TBRect &src_rect, const TBColor &color, - TBBitmap *bitmap); - virtual void drawBitmapTile(const TBRect &dst_rect, TBBitmap *bitmap); - virtual void flushBitmap(TBBitmap *bitmap); - virtual void flushBitmapFragment(TBBitmapFragment *bitmap_fragment); - - virtual void beginBatchHint(TBRenderer::BATCH_HINT hint) { - } - virtual void endBatchHint() { - } - - // == Methods that need implementation in subclasses ================================ - virtual TBBitmap *createBitmap(int width, int height, uint32_t *data) = 0; - virtual void renderBatch(Batch *batch) = 0; - virtual void setClipRect(const TBRect &rect) = 0; - -protected: - uint8_t m_opacity; - TBRect m_screen_rect; - TBRect m_clip_rect; - int m_translation_x; - int m_translation_y; - - Batch batch; ///< The one and only batch. this should be improved. - - void addQuadInternal(const TBRect &dst_rect, const TBRect &src_rect, uint32_t color, TBBitmap *bitmap, - TBBitmapFragment *fragment); - void flushAllInternal(); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_bitmap_fragment.cpp b/src/modules/ui/turbobadger/tb/tb_bitmap_fragment.cpp deleted file mode 100644 index 0c828a475..000000000 --- a/src/modules/ui/turbobadger/tb/tb_bitmap_fragment.cpp +++ /dev/null @@ -1,506 +0,0 @@ -/** - * @file - */ - -#include "tb_bitmap_fragment.h" -#include "image/Image.h" -#include "tb_renderer.h" -#include "tb_system.h" - -namespace tb { - -int TBGetNearestPowerOfTwo(int val) { - int i; - for (i = 31; i >= 0; i--) { - if (((val - 1) & (1 << i)) != 0) { - break; - } - } - return (1 << (i + 1)); -} - -// == TBSpaceAllocator ====================================================================================== - -bool TBSpaceAllocator::hasSpace(int neededW) const { - if (neededW > m_available_space) { - return false; - } - if (isAllAvailable()) { - return true; - } - for (Space *fs = m_free_space_list.getFirst(); fs != nullptr; fs = fs->getNext()) { - if (neededW <= fs->width) { - return true; - } - } - return false; -} - -TBSpaceAllocator::Space *TBSpaceAllocator::allocSpace(int neededW) { - if (Space *available_space = getSmallestAvailableSpace(neededW)) { - if (Space *new_space = new Space) { - new_space->x = available_space->x; - new_space->width = neededW; - m_used_space_list.addLast(new_space); - - // Consume the used space from the available space - available_space->x += neededW; - available_space->width -= neededW; - m_available_space -= neededW; - - // Remove it if empty - if (available_space->width == 0) { - m_free_space_list.doDelete(available_space); - } - return new_space; - } - } - return nullptr; -} - -TBSpaceAllocator::Space *TBSpaceAllocator::getSmallestAvailableSpace(int neededW) { - core_assert(neededW > 0); - - // Add free space covering all available space if empty. - if (!m_free_space_list.hasLinks() && isAllAvailable()) { - if (Space *fs = new Space) { - fs->x = 0; - fs->width = m_available_space; - m_free_space_list.addLast(fs); - } - } - - // Check for the smallest space where we fit - Space *best_fs = nullptr; - for (Space *fs = m_free_space_list.getFirst(); fs != nullptr; fs = fs->getNext()) { - if (neededW == fs->width) { - return fs; // It can't be better than a perfect match! - } - if (neededW < fs->width) { - if ((best_fs == nullptr) || fs->width < best_fs->width) { - best_fs = fs; - } - } - } - return best_fs; -} - -void TBSpaceAllocator::freeSpace(Space *space) { - m_used_space_list.remove(space); - m_available_space += space->width; - - // Find where in m_free_space_list we should insert the space, - // or which existing space we can extend. - Space *preceeding = nullptr; - Space *succeeding = nullptr; - for (Space *fs = m_free_space_list.getFirst(); fs != nullptr; fs = fs->getNext()) { - if (fs->x < space->x) { - preceeding = fs; - } - if (fs->x > space->x) { - succeeding = fs; - break; - } - } - if ((preceeding != nullptr) && preceeding->x + preceeding->width == space->x) { - preceeding->width += space->width; - delete space; - } else if ((succeeding != nullptr) && succeeding->x == space->x + space->width) { - succeeding->x -= space->width; - succeeding->width += space->width; - delete space; - } else { - if (preceeding != nullptr) { - m_free_space_list.addAfter(space, preceeding); - } else if (succeeding != nullptr) { - m_free_space_list.addBefore(space, succeeding); - } else { - core_assert(!m_free_space_list.hasLinks()); - m_free_space_list.addLast(space); - } - } - // Merge free spaces - Space *fs = m_free_space_list.getFirst(); - while (fs != nullptr) { - Space *next_fs = fs->getNext(); - if (next_fs == nullptr) { - break; - } - if (fs->x + fs->width == next_fs->x) { - fs->width += next_fs->width; - m_free_space_list.doDelete(next_fs); - continue; - } - fs = next_fs; - } - -#ifdef TB_RUNTIME_DEBUG_INFO - // Check that free space is in order - Space *tmp = m_free_space_list.getFirst(); - int x = 0; - while (tmp != nullptr) { - core_assert(tmp->x >= x); - x = tmp->x + tmp->width; - tmp = tmp->getNext(); - } -#endif // TB_RUNTIME_DEBUG_INFO -} - -// == TBBitmapFragmentMap =================================================================================== - -TBBitmapFragmentMap::TBBitmapFragmentMap() - : m_bitmap_w(0), m_bitmap_h(0), m_bitmap_data(nullptr), m_bitmap(nullptr), m_need_update(false), - m_allocated_pixels(0) { -} - -bool TBBitmapFragmentMap::init(int bitmapW, int bitmapH) { - m_bitmap_data = new uint32_t[bitmapW * bitmapH]; - m_bitmap_w = bitmapW; - m_bitmap_h = bitmapH; -#ifdef TB_RUNTIME_DEBUG_INFO - if (m_bitmap_data != nullptr) { - memset(m_bitmap_data, 0x88, bitmapW * bitmapH * sizeof(uint32_t)); - } -#endif - return m_bitmap_data != nullptr; -} - -TBBitmapFragmentMap::~TBBitmapFragmentMap() { - delete m_bitmap; - delete[] m_bitmap_data; -} - -TBBitmapFragment *TBBitmapFragmentMap::createNewFragment(int fragW, int fragH, int dataStride, uint32_t *fragData, - bool addBorder) { - // Finding available space works like this: - // The map size is sliced up horizontally in rows (initially just one row covering - // the entire map). When adding a new fragment, put it in the row with smallest height. - // If the smallest row is empty, it may slice the row to make a even smaller row. - - // When a image is stretched up to a larger size, the filtering will read - // pixels closest (but outside) of the src_rect. When we pack images together - // those pixels would be read from neighbour images, so we must add border space - // around each image to avoid artifacts. We must also fill in that border with - // the "clamp" of the image itself so we don't get any filtering artifacts at all. - // Allways add border except when we're using the entire map for one fragment. - int border = 0; - int needed_w = fragW; - int needed_h = fragH; - if (addBorder) { - if (needed_w != m_bitmap_w || needed_h != m_bitmap_h) { - border = 1; - needed_w += 2; - needed_h += 2; - } - } - - // Snap the fragments to a certain granularity. This could maybe ease the stress - // on the space allocator when allocating & deallocating lots of small fragments. - // I'm not sure there is any performance issue though and it would be better to - // optimize the algorithm instead (so disabled it for now). - // const int granularity = 8; - // needed_w = (needed_w + granularity - 1) / granularity * granularity; - // needed_h = (needed_h + granularity - 1) / granularity * granularity; - - if (m_rows.getNumItems() == 0) { - // Create a row covering the entire bitmap. - TBFragmentSpaceAllocator *row; - if (!m_rows.growIfNeeded() || ((row = new TBFragmentSpaceAllocator(0, m_bitmap_w, m_bitmap_h)) == nullptr)) { - return nullptr; - } - m_rows.add(row); - } - // Get the smallest row where we fit - int best_row_index = -1; - TBFragmentSpaceAllocator *best_row = nullptr; - for (int i = 0; i < m_rows.getNumItems(); i++) { - TBFragmentSpaceAllocator *row = m_rows[i]; - if ((best_row == nullptr) || row->height < best_row->height) { - // This is the best row so far, if we fit - if (needed_h <= row->height && row->hasSpace(needed_w)) { - best_row = row; - best_row_index = i; - if (needed_h == row->height) { - break; // We can't find a smaller line, so we're done - } - } - } - } - // Return if we're full - if (best_row == nullptr) { - return nullptr; - } - // If the row is unused, create a smaller row to only consume needed height for fragment - if (best_row->isAllAvailable() && needed_h < best_row->height) { - TBFragmentSpaceAllocator *row; - if (!m_rows.growIfNeeded() || ((row = new TBFragmentSpaceAllocator(best_row->y + needed_h, m_bitmap_w, - best_row->height - needed_h)) == nullptr)) { - return nullptr; - } - // Keep the rows sorted from top to bottom - m_rows.add(row, best_row_index + 1); - best_row->height = needed_h; - } - // Allocate the fragment and copy the fragment data into the map data. - if (TBFragmentSpaceAllocator::Space *space = best_row->allocSpace(needed_w)) { - if (TBBitmapFragment *frag = new TBBitmapFragment) { - frag->m_map = this; - frag->m_row = best_row; - frag->m_space = space; - frag->m_rect.set(space->x + border, best_row->y + border, fragW, fragH); - frag->m_row_height = best_row->height; - frag->m_batch_id = 0xffffffff; - copyData(frag, dataStride, fragData, border); - m_need_update = true; - m_allocated_pixels += frag->m_space->width * frag->m_row->height; - return frag; - } - { best_row->freeSpace(space); } - } - return nullptr; -} - -void TBBitmapFragmentMap::freeFragmentSpace(TBBitmapFragment *frag) { - if (frag == nullptr) { - return; - } - core_assert(frag->m_map == this); - -#ifdef TB_RUNTIME_DEBUG_INFO - // Debug code to clear the area in debug builds so it's easier to - // see & debug the allocation & deallocation of fragments in maps. - if (uint32_t *data32 = new uint32_t[frag->m_space->width * frag->m_row->height]) { - static int c = 0; - memset(data32, (c++) * 32, sizeof(uint32_t) * frag->m_space->width * frag->m_row->height); - copyData(frag, frag->m_space->width, data32, 0); - m_need_update = true; - delete[] data32; - } -#endif // TB_RUNTIME_DEBUG_INFO - - m_allocated_pixels -= frag->m_space->width * frag->m_row->height; - frag->m_row->freeSpace(frag->m_space); - frag->m_space = nullptr; - frag->m_row_height = 0; - - // If the row is now empty, merge empty rows so larger fragments - // have a chance of allocating the space. - if (frag->m_row->isAllAvailable()) { - for (int i = 0; i < m_rows.getNumItems() - 1; i++) { - core_assert(i >= 0); - core_assert(i < m_rows.getNumItems() - 1); - TBFragmentSpaceAllocator *row = m_rows.get(i); - TBFragmentSpaceAllocator *next_row = m_rows.get(i + 1); - if (row->isAllAvailable() && next_row->isAllAvailable()) { - row->height += next_row->height; - m_rows.doDelete(i + 1); - i--; - } - } - } -} - -void TBBitmapFragmentMap::copyData(TBBitmapFragment *frag, int dataStride, uint32_t *fragData, int border) { - // Copy the bitmap data - uint32_t *dst = m_bitmap_data + frag->m_rect.x + frag->m_rect.y * m_bitmap_w; - uint32_t *src = fragData; - for (int i = 0; i < frag->m_rect.h; i++) { - memcpy(dst, src, frag->m_rect.w * sizeof(uint32_t)); - dst += m_bitmap_w; - src += dataStride; - } - // Copy the bitmap data to the border around the fragment - if (border != 0) { - TBRect rect = frag->m_rect.expand(border, border); - // Copy vertical edges - dst = m_bitmap_data + rect.x + (rect.y + 1) * m_bitmap_w; - src = fragData; - for (int i = 0; i < frag->m_rect.h; i++) { - dst[0] = src[0] & 0x00ffffff; - dst[rect.w - 1] = src[frag->m_rect.w - 1] & 0x00ffffff; - dst += m_bitmap_w; - src += dataStride; - } - // Copy horizontal edges - dst = m_bitmap_data + rect.x + 1 + rect.y * m_bitmap_w; - src = fragData; - for (int i = 0; i < frag->m_rect.w; i++) { - dst[i] = src[i] & 0x00ffffff; - } - dst = m_bitmap_data + rect.x + 1 + (rect.y + rect.h - 1) * m_bitmap_w; - src = fragData + (frag->m_rect.h - 1) * dataStride; - for (int i = 0; i < frag->m_rect.w; i++) { - dst[i] = src[i] & 0x00ffffff; - } - } -} - -TBBitmap *TBBitmapFragmentMap::getBitmap(TB_VALIDATE_TYPE validateType) { - if ((m_bitmap != nullptr) && validateType == TB_VALIDATE_FIRST_TIME) { - return m_bitmap; - } - validateBitmap(); - return m_bitmap; -} - -bool TBBitmapFragmentMap::validateBitmap() { - if (m_need_update) { - if (m_bitmap != nullptr) { - m_bitmap->setData(m_bitmap_data); - } else { - m_bitmap = g_renderer->createBitmap(m_bitmap_w, m_bitmap_h, m_bitmap_data); - } - m_need_update = false; - } - return m_bitmap != nullptr; -} - -void TBBitmapFragmentMap::deleteBitmap() { - delete m_bitmap; - m_bitmap = nullptr; - m_need_update = true; -} - -// == TBBitmapFragmentManager ============================================================================= - -TBBitmapFragmentManager::TBBitmapFragmentManager() - : m_num_maps_limit(0), m_add_border(false), m_default_map_w(2048), m_default_map_h(2048) { -} - -TBBitmapFragmentManager::~TBBitmapFragmentManager() { - clear(); -} - -TBBitmapFragment *TBBitmapFragmentManager::getFragmentFromFile(const char *filename, bool dedicatedMap) { - TBID id(filename); - - // If we already have a fragment for this filename, return that - TBBitmapFragment *frag = m_fragments.get(id); - if (frag != nullptr) { - return frag; - } - - // Load the file - const image::ImagePtr &img = image::loadImage(filename, false); - return createNewFragment(id, dedicatedMap, img->width(), img->height(), img->width(), (uint32_t *)img->data()); -} - -TBBitmapFragment *TBBitmapFragmentManager::createNewFragment(const TBID &id, bool dedicatedMap, int dataW, int dataH, - int dataStride, uint32_t *data) { - core_assert(!getFragment(id)); - - TBBitmapFragment *frag = nullptr; - - // Create a fragment in any of the fragment maps. Doing it in the reverse order - // would be faster since it's most likely to succeed, but we want to maximize - // the amount of fragments per map, so do it in the creation order. - if (!dedicatedMap) { - for (int i = 0; i < m_fragment_maps.getNumItems(); i++) { - if ((frag = m_fragment_maps[i]->createNewFragment(dataW, dataH, dataStride, data, m_add_border)) != - nullptr) { - break; - } - } - } - // If we couldn't create the fragment in any map, create a new map where we know it will fit. - bool allow_another_map = (m_num_maps_limit == 0 || m_fragment_maps.getNumItems() < m_num_maps_limit); - if ((frag == nullptr) && allow_another_map && m_fragment_maps.growIfNeeded()) { - int po2w = TBGetNearestPowerOfTwo(Max(dataW, m_default_map_w)); - int po2h = TBGetNearestPowerOfTwo(Max(dataH, m_default_map_h)); - if (dedicatedMap) { - po2w = TBGetNearestPowerOfTwo(dataW); - po2h = TBGetNearestPowerOfTwo(dataH); - } - TBBitmapFragmentMap *fm = new TBBitmapFragmentMap(); - if ((fm != nullptr) && fm->init(po2w, po2h)) { - m_fragment_maps.add(fm); - frag = fm->createNewFragment(dataW, dataH, dataStride, data, m_add_border); - } else { - delete fm; - } - } - // Finally, add the new fragment to the hash. - if ((frag != nullptr) && m_fragments.add(id, frag)) { - frag->m_id = id; - return frag; - } - delete frag; - return nullptr; -} - -void TBBitmapFragmentManager::freeFragment(TBBitmapFragment *frag) { - if (frag != nullptr) { - g_renderer->flushBitmapFragment(frag); - - TBBitmapFragmentMap *map = frag->m_map; - frag->m_map->freeFragmentSpace(frag); - m_fragments.deleteKey(frag->m_id); - - // If the map is now empty, delete it. - if (map->m_allocated_pixels == 0) { - m_fragment_maps.doDelete(m_fragment_maps.find(map)); - } - } -} - -TBBitmapFragment *TBBitmapFragmentManager::getFragment(const TBID &id) const { - return m_fragments.get(id); -} - -void TBBitmapFragmentManager::clear() { - m_fragment_maps.deleteAll(); - m_fragments.deleteAll(); -} - -bool TBBitmapFragmentManager::validateBitmaps() { - bool success = true; - for (int i = 0; i < m_fragment_maps.getNumItems(); i++) { - if (!m_fragment_maps[i]->validateBitmap()) { - success = false; - } - } - return success; -} - -void TBBitmapFragmentManager::deleteBitmaps() { - for (int i = 0; i < m_fragment_maps.getNumItems(); i++) { - m_fragment_maps[i]->deleteBitmap(); - } -} - -void TBBitmapFragmentManager::setNumMapsLimit(int numMapsLimit) { - m_num_maps_limit = numMapsLimit; -} - -void TBBitmapFragmentManager::setDefaultMapSize(int w, int h) { - core_assert(TBGetNearestPowerOfTwo(w) == w); - core_assert(TBGetNearestPowerOfTwo(h) == h); - m_default_map_w = w; - m_default_map_h = h; -} - -int TBBitmapFragmentManager::getUseRatio() const { - int used = 0; - int total = 0; - for (int i = 0; i < m_fragment_maps.getNumItems(); i++) { - used += m_fragment_maps[i]->m_allocated_pixels; - total += m_fragment_maps[i]->m_bitmap_w * m_fragment_maps[i]->m_bitmap_h; - } - return total != 0 ? (used * 100) / total : 0; -} - -#ifdef TB_RUNTIME_DEBUG_INFO -void TBBitmapFragmentManager::debug() { - int x = 0; - for (int i = 0; i < m_fragment_maps.getNumItems(); i++) { - TBBitmapFragmentMap *fm = m_fragment_maps[i]; - if (TBBitmap *bitmap = fm->getBitmap(TB_VALIDATE_ALWAYS)) { - g_renderer->drawBitmap(TBRect(x, 0, fm->m_bitmap_w, fm->m_bitmap_h), - TBRect(0, 0, fm->m_bitmap_w, fm->m_bitmap_h), bitmap); - } - x += fm->m_bitmap_w + 5; - } -} -#endif // TB_RUNTIME_DEBUG_INFO - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_bitmap_fragment.h b/src/modules/ui/turbobadger/tb/tb_bitmap_fragment.h deleted file mode 100644 index d494c6f8d..000000000 --- a/src/modules/ui/turbobadger/tb/tb_bitmap_fragment.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_geometry.h" -#include "tb_hashtable.h" -#include "tb_id.h" -#include "tb_linklist.h" -#include "tb_list.h" - -namespace tb { - -class TBBitmapFragment; -class TBBitmap; - -/** Return the nearest power of two from val. - F.ex 110 -> 128, 256->256, 257->512 etc. */ -int TBGetNearestPowerOfTwo(int val); - -/** Allocator of space out of a given available space. */ -class TBSpaceAllocator { -public: - /** A chunk of space */ - class Space : public TBLinkOf { - public: - int x, width; - }; - - TBSpaceAllocator(int availableSpace) : m_available_space(availableSpace) { - } - - /** Return true if no allocations are currently live using this allocator. */ - bool isAllAvailable() const { - return !m_used_space_list.hasLinks(); - } - - /** Return true if the given width is currently available. */ - bool hasSpace(int needed_w) const; - - /** Allocate the given space and return the Space, or nullptr on error. */ - Space *allocSpace(int needed_w); - - /** Free the given space so it is available for new allocations. */ - void freeSpace(Space *space); - -private: - Space *getSmallestAvailableSpace(int needed_w); - int m_available_space; - TBLinkListAutoDeleteOf m_free_space_list; - TBLinkListAutoDeleteOf m_used_space_list; -}; - -/** Allocates space for TBBitmapFragment in a row (used in TBBitmapFragmentMap). */ -class TBFragmentSpaceAllocator : public TBSpaceAllocator { -public: - TBFragmentSpaceAllocator(int y, int width, int height) : TBSpaceAllocator(width), y(y), height(height) { - } - - int y, height; -}; - -/** Specify when the bitmap should be validated when calling TBBitmapFragmentMap::getBitmap. */ -enum TB_VALIDATE_TYPE { - - /** Always validate the bitmap (The bitmap is updated if needed) */ - TB_VALIDATE_ALWAYS, - - /** Only validate if the bitmap does not yet exist (Make sure there is - a valid bitmap pointer, but the data is not necessarily updated) */ - TB_VALIDATE_FIRST_TIME -}; - -/** TBBitmapFragmentMap is used to pack multiple bitmaps into a single TBBitmap. - When initialized (in a size suitable for a TBBitmap) it also creates a software buffer - that will make up the TBBitmap when all fragments have been added. */ -class TBBitmapFragmentMap { -public: - TBBitmapFragmentMap(); - ~TBBitmapFragmentMap(); - - /** Initialize the map with the given size. The size should be a power of two since - it will be used to create a TBBitmap (texture memory). */ - bool init(int bitmapW, int bitmapH); - - /** Create a new fragment with the given size and data in this map. - Returns nullptr if there is not enough room in this map or on any other fail. */ - TBBitmapFragment *createNewFragment(int fragW, int fragH, int dataStride, uint32_t *fragData, bool addBorder); - - /** Free up the space used by the given fragment, so that other fragments can take its place. */ - void freeFragmentSpace(TBBitmapFragment *frag); - - /** Return the bitmap for this map. - By default, the bitmap is validated if needed before returning (See TB_VALIDATE_TYPE) */ - TBBitmap *getBitmap(TB_VALIDATE_TYPE validate_type); - -private: - friend class TBBitmapFragmentManager; - bool validateBitmap(); - void deleteBitmap(); - void copyData(TBBitmapFragment *frag, int dataStride, uint32_t *fragData, int border); - TBListAutoDeleteOf m_rows; - int m_bitmap_w, m_bitmap_h; - uint32_t *m_bitmap_data; - TBBitmap *m_bitmap; - bool m_need_update; - int m_allocated_pixels; -}; - -/** TBBitmapFragment represents a sub part of a TBBitmap. - It's owned by TBBitmapFragmentManager which pack multiple - TBBitmapFragment within TBBitmaps to reduce texture switching. */ -class TBBitmapFragment { -public: - /** Return the width of the bitmap fragment. */ - int width() const { - return m_rect.w; - } - - /** Return the height of the bitmap fragment. */ - int height() const { - return m_rect.h; - } - - /** Return the bitmap for this fragment. - By default, the bitmap is validated if needed before returning (See TB_VALIDATE_TYPE) */ - TBBitmap *getBitmap(TB_VALIDATE_TYPE validateType) { - return m_map->getBitmap(validateType); - } - - /** Return the height allocated to this fragment. This may be larger than Height() depending - of the internal allocation of fragments in a map. It should rarely be used. */ - int getAllocatedHeight() const { - return m_row_height; - } - -public: - TBBitmapFragmentMap *m_map; - TBRect m_rect; - TBFragmentSpaceAllocator *m_row; - TBFragmentSpaceAllocator::Space *m_space; - TBID m_id; - int m_row_height; - - /** This uint32 is reserved for batching renderer backends. It's not used - internally, but always initialized to 0xffffffff for all new fragments. */ - uint32_t m_batch_id; -}; - -/** TBBitmapFragmentManager manages loading bitmaps of arbitrary size, - pack as many of them into as few TBBitmap as possible. - - It also makes sure that only one instance of each file is loaded, - so f.ex loading "foo.png" many times still load and allocate one - TBBitmapFragment. */ -class TBBitmapFragmentManager { -public: - TBBitmapFragmentManager(); - ~TBBitmapFragmentManager(); - - /** Set to true if a 1px border should be added to new fragments so stretched - drawing won't get filtering artifacts at the edges (default is disabled). */ - void setAddBorder(bool addBorder) { - m_add_border = addBorder; - } - bool getAddBorder() const { - return m_add_border; - } - - /** Get the fragment with the given image filename. If it's not already loaded, - it will be loaded into a new fragment with the filename as id. - returns nullptr on fail. */ - TBBitmapFragment *getFragmentFromFile(const char *filename, bool dedicated_map); - - /** Get the fragment with the given id, or nullptr if it doesn't exist. */ - TBBitmapFragment *getFragment(const TBID &id) const; - - /** Create a new fragment from the given data. - @param id The id that should be used to identify the fragment. - @param dedicatedMap if true, it will get a dedicated map. - @param dataW the width of the data. - @param dataH the height of the data. - @param dataStride the number of pixels in a row of the input data. - @param data pointer to the data in BGRA32 format. */ - TBBitmapFragment *createNewFragment(const TBID &id, bool dedicatedMap, int dataW, int dataH, int dataStride, - uint32_t *data); - - /** Delete the given fragment and free the space it used in its map, - so that other fragments can take its place. */ - void freeFragment(TBBitmapFragment *frag); - - /** Clear all loaded bitmaps and all created bitmap fragments and maps. - After this call, do not keep any pointers to any TBBitmapFragment created - by this fragment manager. */ - void clear(); - - /** Validate bitmaps on fragment maps that has changed. */ - bool validateBitmaps(); - - /** Delete all bitmaps in all fragment maps in this manager. - The bitmaps will be recreated automatically when needed, or when - calling ValidateBitmaps. You do not need to call this, except when - the context is lost and all bitmaps must be forgotten. */ - void deleteBitmaps(); - - /** Get number of fragment maps that is currently used. */ - int getNumMaps() const { - return m_fragment_maps.getNumItems(); - } - - /** Set the number of maps (TBBitmaps) this manager should be allowed to create. - If a new fragment can't fit into any existing bitmap and the limit is reached, - the fragment creation will fail. Set to 0 for unlimited (default). */ - void setNumMapsLimit(int num_maps_limit); - - /** Set the default size of new fragment maps. These must be power of two. */ - void setDefaultMapSize(int w, int h); - - /** Get the amount (in percent) of space that is currently occupied by all maps - in this fragment manager. */ - int getUseRatio() const; -#ifdef TB_RUNTIME_DEBUG_INFO - /** Render the maps on screen, to analyze fragment positioning. */ - void debug(); -#endif -private: - TBListOf m_fragment_maps; - TBHashTableOf m_fragments; - int m_num_maps_limit; - bool m_add_border; - int m_default_map_w; - int m_default_map_h; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_color.cpp b/src/modules/ui/turbobadger/tb/tb_color.cpp deleted file mode 100644 index d5f7ef2c0..000000000 --- a/src/modules/ui/turbobadger/tb/tb_color.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file - */ - -#include "tb_color.h" -#include -#include -#include - -namespace tb { - -TBColor TBColor::fromVec4(const glm::vec4& c) { - return TBColor((int)(c.r * 255.0f), (int)(c.g * 255.0f), (int)(c.b * 255.0f), (int)(c.a * 255.0f)); -} - -TBColor TBColor::fromVec3(const glm::vec3& c) { - return TBColor((int)(c.r * 255.0f), (int)(c.g * 255.0f), (int)(c.b * 255.0f)); -} - -void TBColor::setFromString(const char *str, int len) { - int r; - int g; - int b; - int a; - if (len == 9 && sscanf(str, "#%2x%2x%2x%2x", &r, &g, &b, &a) == 4) { // rrggbbaa - set(TBColor(r, g, b, a)); - } else if (len == 7 && sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3) { // rrggbb - set(TBColor(r, g, b)); - } else if (len == 5 && sscanf(str, "#%1x%1x%1x%1x", &r, &g, &b, &a) == 4) { // rgba - set(TBColor(r + (r << 4), g + (g << 4), b + (b << 4), a + (a << 4))); - } else if (len == 4 && sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3) { // rgb - set(TBColor(r + (r << 4), g + (g << 4), b + (b << 4))); - } else { - set(TBColor()); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_color.h b/src/modules/ui/turbobadger/tb/tb_color.h deleted file mode 100644 index 9f4dbc3ac..000000000 --- a/src/modules/ui/turbobadger/tb/tb_color.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_types.h" -#include "core/GLM.h" - -namespace tb { - -/** TBColor contains a 32bit color. */ - -// TODO: replace with glm::u8vec4 -class TBColor { -public: - constexpr TBColor() : b(0), g(0), r(0), a(255) { - } - constexpr TBColor(int r, int g, int b, int a = 255) : b(b), g(g), r(r), a(a) { - } - - static TBColor fromVec4(const glm::vec4& c); - - static TBColor fromVec3(const glm::vec3& c); - - uint8_t b, g, r, a; - - void set(const TBColor &color) { - *this = color; - } - - /** Set the color from string in any of the following formats: - "#rrggbbaa", "#rrggbb", "#rgba", "#rgb" */ - void setFromString(const char *str, int len); - - inline operator uint32_t() const { - return *((uint32_t *)this); - } - inline bool operator==(const TBColor &c) const { - return *this == (uint32_t)c; - } - inline bool operator!=(const TBColor &c) const { - return !(*this == c); - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_config.h b/src/modules/ui/turbobadger/tb/tb_config.h deleted file mode 100644 index 705fcc3f0..000000000 --- a/src/modules/ui/turbobadger/tb/tb_config.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @file - */ - -#pragma once - -#ifdef DEBUG -#define TB_RUNTIME_DEBUG_INFO -#endif -#define TB_GLYPH_CACHE_WIDTH 512 -#define TB_GLYPH_CACHE_HEIGHT 512 diff --git a/src/modules/ui/turbobadger/tb/tb_core.cpp b/src/modules/ui/turbobadger/tb/tb_core.cpp deleted file mode 100644 index 24b5cddb6..000000000 --- a/src/modules/ui/turbobadger/tb/tb_core.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file - */ - -#include "tb_core.h" -#include "animation/tb_animation.h" -#include "image/tb_image_manager.h" -#include "tb_font_renderer.h" -#include "tb_language.h" -#include "tb_skin.h" -#include "tb_system.h" -#include "tb_widgets_reader.h" - -namespace tb { - -TBRenderer *g_renderer = nullptr; -TBSkin *g_tb_skin = nullptr; -TBWidgetsReader *g_widgets_reader = nullptr; -TBLanguage *g_tb_lng = nullptr; -TBFontManager *g_font_manager = nullptr; - -bool tb_core_init(TBRenderer *renderer) { - Log::debug("Initiating Turbo Badger - version " TB_VERSION_STR); - g_renderer = renderer; - g_tb_lng = new TBLanguage; - g_font_manager = new TBFontManager(); - g_tb_skin = new TBSkin(); - g_widgets_reader = TBWidgetsReader::create(); - g_image_manager = new TBImageManager(); - return true; -} - -void tb_core_shutdown() { - TBAnimationManager::abortAllAnimations(); - delete g_image_manager; - g_image_manager = nullptr; - delete g_widgets_reader; - g_widgets_reader = nullptr; - delete g_tb_skin; - g_tb_skin = nullptr; - delete g_font_manager; - g_font_manager = nullptr; - delete g_tb_lng; - g_tb_lng = nullptr; -} - -bool tb_core_is_initialized() { - return g_widgets_reader != nullptr; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_core.h b/src/modules/ui/turbobadger/tb/tb_core.h deleted file mode 100644 index 313a81285..000000000 --- a/src/modules/ui/turbobadger/tb/tb_core.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_debug.h" -#include "tb_hash.h" -#include "tb_types.h" - -#define TB_VERSION_MAJOR 0 -#define TB_VERSION_MINOR 1 -#define TB_VERSION_REVISION 1 -#define TB_VERSION_STR "0.1.1" - -namespace tb { - -class TBRenderer; -class TBSkin; -class TBWidgetsReader; -class TBLanguage; -class TBFontManager; - -extern TBRenderer *g_renderer; -extern TBSkin *g_tb_skin; -extern TBWidgetsReader *g_widgets_reader; -extern TBLanguage *g_tb_lng; -extern TBFontManager *g_font_manager; - -/** Initialize turbo badger. Call this before using any turbo badger API. */ -bool tb_core_init(TBRenderer *renderer); - -/** Shutdown turbo badger. Call this after deleting the last widget, to free turbo badger internals. */ -void tb_core_shutdown(); - -/** Returns true if turbo badger is initialized. */ -bool tb_core_is_initialized(); - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_debug.cpp b/src/modules/ui/turbobadger/tb/tb_debug.cpp deleted file mode 100644 index b8f84b68e..000000000 --- a/src/modules/ui/turbobadger/tb/tb_debug.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @file - */ - -#include "tb_core.h" -#include "tb_editfield.h" -#include "tb_font_renderer.h" -#include "tb_tempbuffer.h" -#include "tb_widgets_reader.h" -#include "tb_window.h" -#include "core/StringUtil.h" -#include - -namespace tb { - -#ifdef TB_RUNTIME_DEBUG_INFO - -TBDebugInfo g_tb_debug; - -TBDebugInfo::TBDebugInfo() { - SDL_memset(settings, 0, sizeof(int) * NUM_SETTINGS); -} - -/** Window showing runtime debug settings. */ -class DebugSettingsWindow : public TBWindow, public TBWidgetListener { -public: - TBEditField *output; - - TBOBJECT_SUBCLASS(DebugSettingsWindow, TBWindow); - - DebugSettingsWindow(TBWidget *root) { - setText("Debug settings"); - g_widgets_reader->loadData(this, "TBLayout: axis: y, distribution: available, position: left\n" - " TBLayout: id: 'container', axis: y, size: available\n" - " TBTextField: text: 'Event output:'\n" - " TBEditField: id: 'output', gravity: all, multiline: 1, wrap: 0\n" - " lp: pref-height: 100dp"); - - addCheckbox(TBDebugInfo::LAYOUT_BOUNDS, "Layout bounds"); - addCheckbox(TBDebugInfo::LAYOUT_CLIPPING, "Layout clipping"); - addCheckbox(TBDebugInfo::LAYOUT_PS_DEBUGGING, "Layout size calculation"); - addCheckbox(TBDebugInfo::RENDER_BATCHES, "Render batches"); - addCheckbox(TBDebugInfo::RENDER_SKIN_BITMAP_FRAGMENTS, "Render skin bitmap fragments"); - addCheckbox(TBDebugInfo::RENDER_FONT_BITMAP_FRAGMENTS, "Render font bitmap fragments"); - - output = getWidgetByIDAndType(TBIDC("output")); - - TBRect bounds(0, 0, root->getRect().w, root->getRect().h); - setRect(getResizeToFitContentRect().centerIn(bounds).moveIn(bounds).clip(bounds)); - - root->addChild(this); - - TBWidgetListener::addGlobalListener(this); - } - - ~DebugSettingsWindow() { - TBWidgetListener::removeGlobalListener(this); - } - - void addCheckbox(TBDebugInfo::SETTING setting, const char *str) { - TBCheckBox *check = new TBCheckBox(); - check->setValue(g_tb_debug.settings[setting]); - check->data.setInt(setting); - check->setID(TBIDC("check")); - - TBClickLabel *label = new TBClickLabel(); - label->setText(str); - label->getContentRoot()->addChild(check, WIDGET_Z_BOTTOM); - - getWidgetByID(TBIDC("container"))->addChild(label); - } - - virtual bool onEvent(const TBWidgetEvent &ev) override { - if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("check")) { - // Update setting and invalidate - g_tb_debug.settings[ev.target->data.getInt()] = ev.target->getValue(); - getParentRoot()->invalidate(); - return true; - } - return TBWindow::onEvent(ev); - } - - virtual void onPaint(const PaintProps &paintProps) override { - // Draw stuff to the right of the debug window - g_renderer->translate(getRect().w, 0); - - // Draw skin bitmap fragments - if (TB_DEBUG_SETTING(RENDER_SKIN_BITMAP_FRAGMENTS)) { - g_tb_skin->debug(); - } - - // Draw font glyph fragments (the font of the hovered widget) - if (TB_DEBUG_SETTING(RENDER_FONT_BITMAP_FRAGMENTS)) { - TBWidget *widget = - TBWidget::hovered_widget != nullptr ? TBWidget::hovered_widget : TBWidget::focused_widget; - g_font_manager - ->getFontFace(widget != nullptr ? widget->getCalculatedFontDescription() - : g_font_manager->getDefaultFontDescription()) - ->debug(); - } - - g_renderer->translate(-getRect().w, 0); - } - - core::String getIdString(const TBID &id) { - core::String str; - str = core::string::format("%u", (uint32_t)id); - return str; - } - - // TBWidgetListener - virtual bool onWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) override { - // Skip these events for now - if (ev.isPointerEvent()) { - return false; - } - - // Always ignore activity in this window (or we might get endless recursion) - if (TBWindow *window = widget->getParentWindow()) { - if (TBSafeCast(window) != nullptr) { - return false; - } - } - - TBTempBuffer buf; - buf.appendString(getEventTypeStr(ev.type)); - buf.appendString(" ("); - buf.appendString(widget->getClassName()); - buf.appendString(")"); - - buf.appendString(" id: "); - buf.appendString(getIdString(ev.target->getID())); - - if (ev.ref_id != 0U) { - buf.appendString(", ref_id: "); - buf.appendString(getIdString(ev.ref_id)); - } - - if (ev.type == EVENT_TYPE_CHANGED) { - core::String extra; - core::String text; - if (ev.target->getText(text) && text.size() > 24) { - sprintf(text.c_str() + 20, "..."); - } - extra = core::string::format(", value: %.2f (\"%s\")", ev.target->getValueDouble(), text.c_str()); - buf.appendString(extra.c_str()); - } - buf.appendString("\n"); - - // Append the line to the output textfield - TBStyleEdit *se = output->getStyleEdit(); - se->selection.selectNothing(); - se->appendText(buf.getData(), true); - se->scrollIfNeeded(false, true); - - // Remove lines from the top if we exceed the height limit. - const int height_limit = 2000; - int current_height = se->getContentHeight(); - if (current_height > height_limit) { - se->caret.place(TBPoint(0, current_height - height_limit)); - se->selection.selectToCaret(se->blocks.getFirst(), 0); - se->del(); - } - return false; - } - - const char *getEventTypeStr(EVENT_TYPE type) const { - switch (type) { - case EVENT_TYPE_CLICK: - return "CLICK"; - case EVENT_TYPE_LONG_CLICK: - return "LONG_CLICK"; - case EVENT_TYPE_POINTER_DOWN: - return "POINTER_DOWN"; - case EVENT_TYPE_POINTER_UP: - return "POINTER_UP"; - case EVENT_TYPE_POINTER_MOVE: - return "POINTER_MOVE"; - case EVENT_TYPE_TOUCH_DOWN: - return "TOUCH_DOWN"; - case EVENT_TYPE_TOUCH_UP: - return "TOUCH_UP"; - case EVENT_TYPE_TOUCH_MOVE: - return "TOUCH_MOVE"; - case EVENT_TYPE_TOUCH_CANCEL: - return "TOUCH_CANCEL"; - case EVENT_TYPE_WHEEL: - return "WHEEL"; - case EVENT_TYPE_CHANGED: - return "CHANGED"; - case EVENT_TYPE_KEY_DOWN: - return "KEY_DOWN"; - case EVENT_TYPE_KEY_UP: - return "KEY_UP"; - case EVENT_TYPE_SHORTCUT: - return "SHORT_CUT"; - case EVENT_TYPE_CONTEXT_MENU: - return "CONTEXT_MENU"; - default: - return "[UNKNOWN]"; - } - } -}; - -void ShowDebugInfoSettingsWindow(TBWidget *root) { - new DebugSettingsWindow(root); -} - -#endif // TB_RUNTIME_DEBUG_INFO - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_debug.h b/src/modules/ui/turbobadger/tb/tb_debug.h deleted file mode 100644 index 45abf13c8..000000000 --- a/src/modules/ui/turbobadger/tb/tb_debug.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_types.h" - -namespace tb { - -#ifdef TB_RUNTIME_DEBUG_INFO - -class TBDebugInfo { -public: - TBDebugInfo(); - - enum SETTING { - /** Show widgets bounds */ - LAYOUT_BOUNDS, - /** Show child widget clipping set by some widgets. */ - LAYOUT_CLIPPING, - /** Show highlights on widgets that recalculate their preferred - size, and those who recalculate their layout. */ - LAYOUT_PS_DEBUGGING, - /** Show render batch info and log batch info in the debug - output. It depends on the renderer backend if this is available. */ - RENDER_BATCHES, - /** Render the bitmap fragments of the skin. */ - RENDER_SKIN_BITMAP_FRAGMENTS, - /** Render the bitmap fragments of the font that's set on the hovered - or focused widget. */ - RENDER_FONT_BITMAP_FRAGMENTS, - - NUM_SETTINGS - }; - int settings[NUM_SETTINGS]; -}; - -extern TBDebugInfo g_tb_debug; - -/** Show a window containing runtime debugging settings. */ -void ShowDebugInfoSettingsWindow(class TBWidget *root); - -#define TB_DEBUG_SETTING(setting) g_tb_debug.settings[TBDebugInfo::setting] -#define TB_IF_DEBUG_SETTING(setting, code) \ - if (TB_DEBUG_SETTING(setting)) { \ - code; \ - } - -#else // TB_RUNTIME_DEBUG_INFO - -/** Show a window containing runtime debugging settings. */ -#define ShowDebugInfoSettingsWindow(root) ((void)0) - -#define TB_DEBUG_SETTING(setting) false -#define TB_IF_DEBUG_SETTING(setting, code) - -#endif // TB_RUNTIME_DEBUG_INFO - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_dimension.cpp b/src/modules/ui/turbobadger/tb/tb_dimension.cpp deleted file mode 100644 index 0168c39ff..000000000 --- a/src/modules/ui/turbobadger/tb/tb_dimension.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @file - */ - -#include "tb_dimension.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" -#include "tb_types.h" -#include "tb_value.h" -#include "core/StringUtil.h" -#include - -namespace tb { - -// == TBDimensionConverter ================================================================================== - -void TBDimensionConverter::setDPI(int srcDpi, int dstDpi) { - m_src_dpi = srcDpi; - m_dst_dpi = dstDpi; - m_dst_dpi_str.clear(); - if (needConversion()) { - m_dst_dpi_str = core::string::format("@%d", m_dst_dpi); - } -} - -void TBDimensionConverter::getDstDPIFilename(const char *filename, TBTempBuffer *tempbuf) const { - int dot_pos = 0; - for (dot_pos = (int)SDL_strlen(filename) - 1; dot_pos > 0; dot_pos--) { - if (filename[dot_pos] == '.') { - break; - } - } - tempbuf->resetAppendPos(); - tempbuf->append(filename, dot_pos); - tempbuf->appendString(getDstDPIStr()); - tempbuf->appendString(filename + dot_pos); -} - -int TBDimensionConverter::dpToPx(int dp) const { - return (int)roundf(dpToPxF((float)dp)); -} - -float TBDimensionConverter::dpToPxF(float dp) const { - if (dp <= TB_INVALID_DIMENSION || dp == 0 || !needConversion()) { - return dp; - } - return dp * (float)m_dst_dpi / (float)m_src_dpi; -} - -int TBDimensionConverter::mmToPx(int mm) const { - return (int)roundf(mmToPxF((float)mm)); -} - -float TBDimensionConverter::mmToPxF(float mm) const { - if (mm <= TB_INVALID_DIMENSION || mm == 0) { - return mm; - } - return mm * (float)TBSystem::getDPI() / 25.4F; -} - -int TBDimensionConverter::getPxFromString(const char *str, int defValue) const { - if ((str == nullptr) || !is_start_of_number(str)) { - return defValue; - } - const int len = SDL_strlen(str); - const int val = SDL_atoi(str); - if (len > 2 && SDL_strcmp(str + len - 2, "px") == 0) { - return val; - } - if (len > 2 && SDL_strcmp(str + len - 2, "mm") == 0) { - return mmToPx(val); - } - // "dp", unspecified or unknown unit is treated as dp. - return dpToPx(val); -} - -float TBDimensionConverter::getPxFromStringF(const char *str, float defValue) const { - if ((str == nullptr) || !is_start_of_number(str)) { - return defValue; - } - const int len = SDL_strlen(str); - const float val = (float)SDL_atof(str); - if (len > 2 && SDL_strcmp(str + len - 2, "px") == 0) { - return val; - } - if (len > 2 && SDL_strcmp(str + len - 2, "mm") == 0) { - return mmToPxF(val); - } - // "dp", unspecified or unknown unit is treated as dp. - return dpToPxF(val); -} - -int TBDimensionConverter::getPxFromValue(TBValue *value, int defValue) const { - if (value == nullptr) { - return defValue; - } - if (value->getType() == TBValue::TYPE_INT) { - return dpToPx(value->getInt()); - } - if (value->getType() == TBValue::TYPE_FLOAT) { - return (int)roundf(dpToPxF(value->getFloat())); - } - return getPxFromString(value->getString(), defValue); -} - -float TBDimensionConverter::getPxFromValueF(TBValue *value, float defValue) const { - if (value == nullptr) { - return defValue; - } - if (value->getType() == TBValue::TYPE_INT) { - return dpToPxF((float)value->getInt()); - } - if (value->getType() == TBValue::TYPE_FLOAT) { - return dpToPxF(value->getFloat()); - } - return getPxFromStringF(value->getString(), defValue); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_dimension.h b/src/modules/ui/turbobadger/tb/tb_dimension.h deleted file mode 100644 index 57925c327..000000000 --- a/src/modules/ui/turbobadger/tb/tb_dimension.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_debug.h" -#include "tb_str.h" -#include "tb_types.h" - -namespace tb { - -/** Dimensions <= this value will be untouched by conversion in TBDimensionConverter. - To preserve special constants, those must be <= this value. */ -#define TB_INVALID_DIMENSION -5555 - -class TBTempBuffer; -class TBValue; - -/** TBDimensionConverter converts device independant points - to pixels, based on two DPI values. - Dimensions in Turbo Badger are normally in pixels (if not specified differently) - and conversion normally take place when loading skin. */ -class TBDimensionConverter { - int m_src_dpi; ///< The source DPI (Normally the base_dpi from skin). - int m_dst_dpi; ///< The destination DPI (Normally the supported skin DPI nearest to TBSystem::getDPI). - core::String m_dst_dpi_str; ///< The file suffix that should be used to load bitmaps in destinatin DPI. -public: - TBDimensionConverter() : m_src_dpi(100), m_dst_dpi(100) { - } - - /** Set the source and destination DPI that will affect the conversion. */ - void setDPI(int srcDpi, int dstDpi); - - /** Get the source DPI. */ - int getSrcDPI() const { - return m_src_dpi; - } - - /** Get the destination DPI. */ - int getDstDPI() const { - return m_dst_dpi; - } - - /** Get the file name suffix that should be used to load bitmaps in the destination DPI. - Examples: "@96", "@196" */ - const char *getDstDPIStr() const { - return m_dst_dpi_str.c_str(); - } - - /** Get the file name with destination DPI suffix (F.ex "foo.png" becomes "foo@192.png"). - The temp buffer will contain the resulting file name. */ - void getDstDPIFilename(const char *filename, TBTempBuffer *tempbuf) const; - - /** Return true if the source and destinatin DPI are different. */ - bool needConversion() const { - return m_src_dpi != m_dst_dpi; - } - - /** Convert device independant point to pixel. */ - int dpToPx(int dp) const; - float dpToPxF(float dp) const; - - /** Convert millimeter to pixel. */ - int mmToPx(int mm) const; - float mmToPxF(float mm) const; - - /** Get a pixel value from string in any of the following formats: - str may be nullptr. def_value is returned on fail. - - Device independent point: "1", "1dp" - Pixel value: "1px" - */ - int getPxFromString(const char *str, int def_value) const; - float getPxFromStringF(const char *str, float def_value) const; - - /** Get a pixel value from TBValue. - value may be nullptr. def_value is returned on fail. - - Number formats are treated as dp. - String format is treated like for GetPxFromString. - */ - int getPxFromValue(TBValue *value, int def_value) const; - float getPxFromValueF(TBValue *value, float def_value) const; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_editfield.cpp b/src/modules/ui/turbobadger/tb/tb_editfield.cpp deleted file mode 100644 index 22296189b..000000000 --- a/src/modules/ui/turbobadger/tb/tb_editfield.cpp +++ /dev/null @@ -1,615 +0,0 @@ -/** - * @file - */ - -#include "tb_editfield.h" -#include "tb_font_renderer.h" -#include "tb_language.h" -#include "tb_menu_window.h" -#include "tb_select.h" -#include "tb_skin_util.h" -#include "tb_style_edit_content.h" -#include "tb_system.h" -#include "tb_widget_skin_condition_context.h" -#include "tb_widgets_reader.h" - -namespace tb { - -const int CARET_BLINK_TIME = 500; -const int SELECTION_SCROLL_DELAY = 1000 / 30; - -/** Get the delta that should be scrolled if dragging the pointer outside the range min-max */ -int getSelectionScrollSpeed(int pointerpos, int min, int max) { - int d = 0; - if (pointerpos < min) { - d = pointerpos - min; - } else if (pointerpos > max) { - d = pointerpos - max; - } - d *= d; - d /= 40; - return (pointerpos < min) ? -d : d; -} - -TBEditField::TBEditField() : m_edit_type(EDIT_TYPE_TEXT), m_adapt_to_content_size(false), m_virtual_width(250) { - setIsFocusable(true); - setWantLongClick(true); - addChild(&m_scrollbar_x); - addChild(&m_scrollbar_y); - addChild(&m_root); - m_root.setGravity(WIDGET_GRAVITY_ALL); - m_scrollbar_x.setGravity(WIDGET_GRAVITY_BOTTOM | WIDGET_GRAVITY_LEFT_RIGHT); - m_scrollbar_y.setGravity(WIDGET_GRAVITY_RIGHT | WIDGET_GRAVITY_TOP_BOTTOM); - m_scrollbar_y.setAxis(AXIS_Y); - int scrollbar_y_w = m_scrollbar_y.getPreferredSize().pref_w; - int scrollbar_x_h = m_scrollbar_x.getPreferredSize().pref_h; - m_scrollbar_x.setRect(TBRect(0, -scrollbar_x_h, -scrollbar_y_w, scrollbar_x_h)); - m_scrollbar_y.setRect(TBRect(-scrollbar_y_w, 0, scrollbar_y_w, 0)); - m_scrollbar_x.setOpacity(0); - m_scrollbar_y.setOpacity(0); - - setSkinBg(TBIDC("TBEditField"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_style_edit.setListener(this); - - m_root.setRect(getVisibleRect()); - - m_placeholder.setTextAlign(TB_TEXT_ALIGN_LEFT); - - m_content_factory.editfield = this; - m_style_edit.setContentFactory(&m_content_factory); -} - -TBEditField::~TBEditField() { - removeChild(&m_root); - removeChild(&m_scrollbar_y); - removeChild(&m_scrollbar_x); -} - -TBRect TBEditField::getVisibleRect() { - TBRect rect = getPaddingRect(); - if (m_scrollbar_y.getOpacity() != 0.0F) { - rect.w -= m_scrollbar_y.getRect().w; - } - if (m_scrollbar_x.getOpacity() != 0.0F) { - rect.h -= m_scrollbar_x.getRect().h; - } - return rect; -} - -void TBEditField::updateScrollbarVisibility(bool multiline) { - bool enable_vertical = multiline && !m_adapt_to_content_size; - m_scrollbar_y.setOpacity(enable_vertical ? 1.F : 0.F); - m_root.setRect(getVisibleRect()); -} - -void TBEditField::setAdaptToContentSize(bool adapt) { - if (m_adapt_to_content_size == adapt) { - return; - } - m_adapt_to_content_size = adapt; - updateScrollbarVisibility(getMultiline()); -} - -void TBEditField::setVirtualWidth(int virtualWidth) { - if (m_virtual_width == virtualWidth) { - return; - } - m_virtual_width = virtualWidth; - - if (m_adapt_to_content_size && m_style_edit.packed.wrapping) { - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } -} - -void TBEditField::setMultiline(bool multiline) { - if (multiline == getMultiline()) { - return; - } - updateScrollbarVisibility(multiline); - m_style_edit.setMultiline(multiline); - setWrapping(multiline); - invalidateSkinStates(); - TBWidget::invalidate(); -} - -void TBEditField::setStyling(bool styling) { - m_style_edit.setStyling(styling); -} - -void TBEditField::setReadOnly(bool readonly) { - if (readonly == getReadOnly()) { - return; - } - m_style_edit.setReadOnly(readonly); - invalidateSkinStates(); - TBWidget::invalidate(); -} - -void TBEditField::setWrapping(bool wrapping) { - if (wrapping == getWrapping()) { - return; - } - - m_style_edit.setWrapping(wrapping); - - // Invalidate the layout when the wrap mode change and we should adapt our size to it - if (m_adapt_to_content_size) { - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } -} - -void TBEditField::setEditType(EDIT_TYPE type) { - if (m_edit_type == type) { - return; - } - m_edit_type = type; - m_style_edit.setPassword(type == EDIT_TYPE_PASSWORD); - invalidateSkinStates(); - TBWidget::invalidate(); -} - -bool TBEditField::getCustomSkinCondition(const TBSkinCondition::CONDITION_INFO &info) { - if (info.custom_prop == TBIDC("edit-type")) { - switch (m_edit_type) { - case EDIT_TYPE_TEXT: - return info.value == TBIDC("text"); - case EDIT_TYPE_SEARCH: - return info.value == TBIDC("search"); - case EDIT_TYPE_PASSWORD: - return info.value == TBIDC("password"); - case EDIT_TYPE_EMAIL: - return info.value == TBIDC("email"); - case EDIT_TYPE_PHONE: - return info.value == TBIDC("phone"); - case EDIT_TYPE_URL: - return info.value == TBIDC("url"); - case EDIT_TYPE_NUMBER: - return info.value == TBIDC("number"); - } - } else if (info.custom_prop == TBIDC("multiline")) { - const bool ml = ((uint32_t)info.value) == 0U; - return ml == !getMultiline(); - } else if (info.custom_prop == TBIDC("readonly")) { - const bool ro = ((uint32_t)info.value) == 0U; - return ro == !getReadOnly(); - } - return false; -} - -void TBEditField::scrollTo(int x, int y) { - int old_x = m_scrollbar_x.getValue(); - int old_y = m_scrollbar_y.getValue(); - m_style_edit.setScrollPos(x, y); - if (old_x != m_scrollbar_x.getValue() || old_y != m_scrollbar_y.getValue()) { - TBWidget::invalidate(); - } -} - -TBWidget::ScrollInfo TBEditField::getScrollInfo() { - ScrollInfo info; - info.min_x = static_cast(m_scrollbar_x.getMinValue()); - info.min_y = static_cast(m_scrollbar_y.getMinValue()); - info.max_x = static_cast(m_scrollbar_x.getMaxValue()); - info.max_y = static_cast(m_scrollbar_y.getMaxValue()); - info.x = m_scrollbar_x.getValue(); - info.y = m_scrollbar_y.getValue(); - return info; -} - -bool TBEditField::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_scrollbar_x) { - m_style_edit.setScrollPos(m_scrollbar_x.getValue(), m_style_edit.scroll_y); - onScroll(m_scrollbar_x.getValue(), m_style_edit.scroll_y); - return true; - } - if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_scrollbar_y) { - m_style_edit.setScrollPos(m_style_edit.scroll_x, m_scrollbar_y.getValue()); - onScroll(m_style_edit.scroll_x, m_scrollbar_y.getValue()); - return true; - } - if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE) { - int old_val = m_scrollbar_y.getValue(); - m_scrollbar_y.setValue(old_val + ev.delta_y * TBSystem::getPixelsPerLine()); - return m_scrollbar_y.getValue() != old_val; - } - if (ev.type == EVENT_TYPE_POINTER_DOWN && ev.target == this) { - TBRect padding_rect = getPaddingRect(); - if (m_style_edit.mouseDown(TBPoint(ev.target_x - padding_rect.x, ev.target_y - padding_rect.y), 1, ev.count, - TB_MODIFIER_NONE, ev.button_type == TB_TOUCH)) { - // Post a message to start selection scroll - postMessageDelayed(TBIDC("selscroll"), nullptr, SELECTION_SCROLL_DELAY); - return true; - } - } else if (ev.type == EVENT_TYPE_POINTER_MOVE && ev.target == this) { - TBRect padding_rect = getPaddingRect(); - return m_style_edit.mouseMove(TBPoint(ev.target_x - padding_rect.x, ev.target_y - padding_rect.y)); - } else if (ev.type == EVENT_TYPE_POINTER_UP && ev.target == this) { - TBRect padding_rect = getPaddingRect(); - return m_style_edit.mouseUp(TBPoint(ev.target_x - padding_rect.x, ev.target_y - padding_rect.y), 1, - TB_MODIFIER_NONE, ev.button_type == TB_TOUCH); - } else if (ev.type == EVENT_TYPE_KEY_DOWN) { - if (m_style_edit.keyDown(ev.key, ev.special_key, ev.modifierkeys)) { - if (_var) { - _var->setVal(getText().c_str()); - _var->markClean(); - } - return true; - } - return false; - } else if (ev.type == EVENT_TYPE_KEY_UP) { - return true; - } else if ((ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("popupmenu")) || - (ev.type == EVENT_TYPE_SHORTCUT)) { - if (ev.ref_id == TBIDC("cut") && !m_style_edit.packed.read_only) { - m_style_edit.cut(); - if (_var) { - _var->setVal(getText().c_str()); - _var->markClean(); - } - } else if (ev.ref_id == TBIDC("copy")) { - m_style_edit.copy(); - } else if (ev.ref_id == TBIDC("paste") && !m_style_edit.packed.read_only) { - m_style_edit.paste(); - if (_var) { - _var->setVal(getText().c_str()); - _var->markClean(); - } - } else if (ev.ref_id == TBIDC("delete") && !m_style_edit.packed.read_only) { - m_style_edit.del(); - if (_var) { - _var->setVal(getText().c_str()); - _var->markClean(); - } - } else if (ev.ref_id == TBIDC("undo") && !m_style_edit.packed.read_only) { - m_style_edit.undo(); - if (_var) { - _var->setVal(getText().c_str()); - _var->markClean(); - } - } else if (ev.ref_id == TBIDC("redo") && !m_style_edit.packed.read_only) { - m_style_edit.redo(); - if (_var) { - _var->setVal(getText().c_str()); - _var->markClean(); - } - } else if (ev.ref_id == TBIDC("selectall")) { - m_style_edit.selection.selectAll(); - } else { - return false; - } - return true; - } else if (ev.type == EVENT_TYPE_CONTEXT_MENU && ev.target == this) { - TBPoint pos_in_root(ev.target_x, ev.target_y); - ev.target->convertToRoot(pos_in_root.x, pos_in_root.y); - - if (TBMenuWindow *menu = new TBMenuWindow(ev.target, TBIDC("popupmenu"))) { - TBGenericStringItemSource *source = menu->getList()->getDefaultSource(); - source->addItem(new TBGenericStringItem(g_tb_lng->getString(TBIDC("cut")), TBIDC("cut"))); - source->addItem(new TBGenericStringItem(g_tb_lng->getString(TBIDC("copy")), TBIDC("copy"))); - source->addItem(new TBGenericStringItem(g_tb_lng->getString(TBIDC("paste")), TBIDC("paste"))); - source->addItem(new TBGenericStringItem(g_tb_lng->getString(TBIDC("delete")), TBIDC("delete"))); - source->addItem(new TBGenericStringItem("-")); - source->addItem(new TBGenericStringItem(g_tb_lng->getString(TBIDC("selectall")), TBIDC("selectall"))); - menu->show(source, TBPopupAlignment(pos_in_root), -1); - } - return true; - } - return false; -} - -void TBEditField::onPaint(const PaintProps &paintProps) { - TBRect visible_rect = getVisibleRect(); - - bool clip = m_scrollbar_x.canScroll() || m_scrollbar_y.canScroll(); - TBRect old_clip; - if (clip) { - old_clip = g_renderer->setClipRect(visible_rect, true); - } - - int trans_x = visible_rect.x; - int trans_y = visible_rect.y; - g_renderer->translate(trans_x, trans_y); - - // Draw text content, caret etc. - visible_rect.x = visible_rect.y = 0; - m_style_edit.paint(visible_rect, getCalculatedFontDescription(), paintProps.text_color); - - // If empty, draw placeholder text with some opacity. - if (m_style_edit.isEmpty()) { - float old_opacity = g_renderer->getOpacity(); - g_renderer->setOpacity(old_opacity * g_tb_skin->getDefaultPlaceholderOpacity()); - TBRect placeholder_rect(visible_rect.x, visible_rect.y, visible_rect.w, getFont()->getHeight()); - m_placeholder.paint(this, placeholder_rect, paintProps.text_color); - g_renderer->setOpacity(old_opacity); - } - g_renderer->translate(-trans_x, -trans_y); - - if (clip) { - g_renderer->setClipRect(old_clip, false); - } -} - -void TBEditField::setTextFormatted(const char *format, ...) { - va_list args; - va_start(args, format); - char buf[1024]; - SDL_vsnprintf(buf, sizeof(buf), format, args); - buf[sizeof(buf) - 1] = '\0'; - setText(buf); - va_end(args); -} - -void TBEditField::onPaintChildren(const PaintProps &paintProps) { - TBWidget::onPaintChildren(paintProps); - - // Draw fadeout skin at the needed edges. - drawEdgeFadeout(getVisibleRect(), TBIDC("TBEditField.fadeout_x"), TBIDC("TBEditField.fadeout_y"), - m_scrollbar_x.getValue(), m_scrollbar_y.getValue(), - (int)(m_scrollbar_x.getMaxValue() - m_scrollbar_x.getValueDouble()), - (int)(m_scrollbar_y.getMaxValue() - m_scrollbar_y.getValueDouble())); -} - -void TBEditField::onAdded() { - m_style_edit.setFont(getCalculatedFontDescription()); -} - -void TBEditField::onFontChanged() { - m_style_edit.setFont(getCalculatedFontDescription()); -} - -void TBEditField::onFocusChanged(bool focused) { - m_style_edit.focus(focused); -} - -void TBEditField::onResized(int oldW, int oldH) { - // Make the scrollbars move - TBWidget::onResized(oldW, oldH); - - TBRect visible_rect = getVisibleRect(); - m_style_edit.setLayoutSize(visible_rect.w, visible_rect.h, false); - - updateScrollbars(); -} - -PreferredSize TBEditField::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - int font_height = getFont()->getHeight(); - PreferredSize ps; - if (m_adapt_to_content_size) { - int old_layout_width = m_style_edit.layout_width; - int old_layout_height = m_style_edit.layout_height; - if (m_style_edit.packed.wrapping) { - // If we have wrapping enabled, we have to set a virtual width and format the text - // so we can get the actual content width with a constant result every time. - // If the layouter does not respect our size constraints in the end, we may - // get a completly different content height due to different wrapping. - // To fix that, we need to layout in 2 passes. - - // A hacky fix is to do something we probably shouldn't: use the old layout width - // as virtual width for the new. - // int layout_width = old_layout_width > 0 ? Max(old_layout_width, m_virtual_width) : m_virtual_width; - int layout_width = m_virtual_width; - if (constraints.available_w != SizeConstraints::NO_RESTRICTION) { - layout_width = constraints.available_w; - if (TBSkinElement *bg_skin = getSkinBgElement()) { - layout_width -= bg_skin->padding_left + bg_skin->padding_right; - } - } - - m_style_edit.setLayoutSize(layout_width, old_layout_height, true); - ps.size_dependency = SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH; - } - int width = m_style_edit.getContentWidth(); - int height = m_style_edit.getContentHeight(); - if (m_style_edit.packed.wrapping) { - m_style_edit.setLayoutSize(old_layout_width, old_layout_height, true); - } - height = Max(height, font_height); - - ps.min_w = ps.pref_w /*= ps.max_w*/ = width; // should go with the hack above. - // ps.min_w = ps.pref_w = ps.max_w = width; - ps.min_h = ps.pref_h = ps.max_h = height; - } else { - ps.pref_h = ps.min_h = font_height; - if (m_style_edit.packed.multiline_on) { - ps.pref_w = font_height * 10; - ps.pref_h = font_height * 5; - } else { - ps.max_h = ps.pref_h; - } - } - return ps; -} - -void TBEditField::onMessageReceived(TBMessage *msg) { - if (msg->message == TBIDC("blink")) { - m_style_edit.caret.on = !m_style_edit.caret.on; - m_style_edit.caret.invalidate(); - - // Post another blink message so we blink again. - postMessageDelayed(TBIDC("blink"), nullptr, CARET_BLINK_TIME); - } else if (msg->message == TBIDC("selscroll") && captured_widget == this) { - // Get scroll speed from where mouse is relative to the padding rect. - TBRect padding_rect = getVisibleRect().shrink(2, 2); - int dx = getSelectionScrollSpeed(pointer_move_widget_x, padding_rect.x, padding_rect.x + padding_rect.w); - int dy = getSelectionScrollSpeed(pointer_move_widget_y, padding_rect.y, padding_rect.y + padding_rect.h); - m_scrollbar_x.setValue(m_scrollbar_x.getValue() + dx); - m_scrollbar_y.setValue(m_scrollbar_y.getValue() + dy); - - // Handle mouse move at the new scroll position, so selection is updated - if ((dx != 0) || (dy != 0)) { - m_style_edit.mouseMove(TBPoint(pointer_move_widget_x, pointer_move_widget_y)); - } - - // Post another setscroll message so we continue scrolling if we still should. - if (m_style_edit.select_state != 0) { - postMessageDelayed(TBIDC("selscroll"), nullptr, SELECTION_SCROLL_DELAY); - } - } -} - -void TBEditField::onChange() { - // Invalidate the layout when the content change and we should adapt our size to it - if (m_adapt_to_content_size) { - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } - - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); -} - -bool TBEditField::onEnter() { - return false; -} - -void TBEditField::invalidate(const TBRect &rect) { - TBWidget::invalidate(); -} - -void TBEditField::drawString(int32_t x, int32_t y, TBFontFace *font, const TBColor &color, const char *str, - int32_t len) { - font->drawString(x, y, color, str, len); -} - -void TBEditField::drawRect(const TBRect &rect, const TBColor &color) { - g_tb_skin->paintRect(rect, color, 1); -} - -void TBEditField::drawRectFill(const TBRect &rect, const TBColor &color) { - g_tb_skin->paintRectFill(rect, color); -} - -void TBEditField::drawTextSelectionBg(const TBRect &rect) { - TBWidgetSkinConditionContext context(this); - g_tb_skin->paintSkin(rect, TBIDC("TBEditField.selection"), static_cast(getAutoState()), context); -} - -void TBEditField::drawContentSelectionFg(const TBRect &rect) { - TBWidgetSkinConditionContext context(this); - g_tb_skin->paintSkin(rect, TBIDC("TBEditField.selection"), static_cast(getAutoState()), context); -} - -void TBEditField::drawCaret(const TBRect &rect) { - if (getIsFocused() && !m_style_edit.packed.read_only) { - drawTextSelectionBg(rect); - } -} - -void TBEditField::scroll(int32_t dx, int32_t dy) { - TBWidget::invalidate(); - m_scrollbar_x.setValue(m_style_edit.scroll_x); - m_scrollbar_y.setValue(m_style_edit.scroll_y); -} - -void TBEditField::updateScrollbars() { - int32_t w = m_style_edit.layout_width; - int32_t h = m_style_edit.layout_height; - m_scrollbar_x.setLimits(0, m_style_edit.getContentWidth() - w, w); - m_scrollbar_y.setLimits(0, m_style_edit.getContentHeight() - h, h); -} - -void TBEditField::caretBlinkStart() { - // Post the delayed blink message if we don't already have one - if (getMessageByID(TBIDC("blink")) == nullptr) { - postMessageDelayed(TBIDC("blink"), nullptr, CARET_BLINK_TIME); - } -} - -void TBEditField::caretBlinkStop() { - // Remove the blink message if we have one - if (TBMessage *msg = getMessageByID(TBIDC("blink"))) { - deleteMessage(msg); - } -} - -// == TBEditFieldScrollRoot ======================================================================= - -void TBEditFieldScrollRoot::onPaintChildren(const PaintProps &paintProps) { - // Avoid setting clipping (can be expensive) if we have no children to paint anyway. - if (getFirstChild() == nullptr) { - return; - } - // Clip children - TBRect old_clip_rect = g_renderer->setClipRect(getPaddingRect(), true); - TBWidget::onPaintChildren(paintProps); - g_renderer->setClipRect(old_clip_rect, false); -} - -void TBEditFieldScrollRoot::getChildTranslation(int &x, int &y) const { - TBEditField *edit_field = static_cast(getParent()); - x = (int)-edit_field->getStyleEdit()->scroll_x; - y = (int)-edit_field->getStyleEdit()->scroll_y; -} - -WIDGET_HIT_STATUS TBEditFieldScrollRoot::getHitStatus(int x, int y) { - // Return no hit on this widget, but maybe on any of the children. - if ((TBWidget::getHitStatus(x, y) != 0U) && (getWidgetAt(x, y, false) != nullptr)) { - return WIDGET_HIT_STATUS_HIT; - } - return WIDGET_HIT_STATUS_NO_HIT; -} - -// == TBTextFragmentContentWidget ================================================================= - -class TBTextFragmentContentWidget : public TBTextFragmentContent { -public: - TBTextFragmentContentWidget(TBWidget *parent, TBWidget *widget); - virtual ~TBTextFragmentContentWidget(); - - virtual void updatePos(const TBBlock *block, int x, int y); - virtual int32_t getWidth(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment); - virtual int32_t getHeight(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment); - virtual int32_t getBaseline(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment); - -private: - TBWidget *m_widget; -}; - -TBTextFragmentContentWidget::TBTextFragmentContentWidget(TBWidget *parent, TBWidget *widget) : m_widget(widget) { - parent->getContentRoot()->addChild(widget); -} - -TBTextFragmentContentWidget::~TBTextFragmentContentWidget() { - m_widget->removeFromParent(); - delete m_widget; -} - -void TBTextFragmentContentWidget::updatePos(const TBBlock *block, int x, int y) { - m_widget->setRect(TBRect(x, y, getWidth(block, nullptr, nullptr), getHeight(block, nullptr, nullptr))); -} - -int32_t TBTextFragmentContentWidget::getWidth(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return m_widget->getRect().w != 0 ? m_widget->getRect().w : m_widget->getPreferredSize().pref_w; -} - -int32_t TBTextFragmentContentWidget::getHeight(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return m_widget->getRect().h != 0 ? m_widget->getRect().h : m_widget->getPreferredSize().pref_h; -} - -int32_t TBTextFragmentContentWidget::getBaseline(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - const int height = getHeight(block, font, fragment); - return (height + block->calculateBaseline(font)) / 2; -} - -// == TBEditFieldContentFactory =================================================================== - -int TBEditFieldContentFactory::getContent(const char *text) { - return TBTextFragmentContentFactory::getContent(text); -} - -TBTextFragmentContent *TBEditFieldContentFactory::createFragmentContent(const char *text, int textLen) { - if (SDL_strncmp(text, "loadData(widget, text + 8, textLen - 9); - return cw; - } - delete widget; - } - } - - return TBTextFragmentContentFactory::createFragmentContent(text, textLen); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_editfield.h b/src/modules/ui/turbobadger/tb/tb_editfield.h deleted file mode 100644 index ee5d6fd3e..000000000 --- a/src/modules/ui/turbobadger/tb/tb_editfield.h +++ /dev/null @@ -1,261 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_msg.h" -#include "tb_style_edit.h" -#include "tb_widgets_common.h" -#include "core/Log.h" - -namespace tb { - -/** EDIT_TYPE - These types does not restrict input (may change in the future). - They are just hints for virtual keyboard, so it can show special keys. */ -enum EDIT_TYPE { - EDIT_TYPE_TEXT, - EDIT_TYPE_SEARCH, - EDIT_TYPE_PASSWORD, - EDIT_TYPE_EMAIL, - EDIT_TYPE_PHONE, - EDIT_TYPE_URL, - EDIT_TYPE_NUMBER -}; - -/** The default content factory for embedded content in TBEditField with styling enabled. - - Creates all that TBTextFragmentContentFactory creates by default, - and any type of widget from a inline resource string. - - Syntax: Where xxx is parsed by TBWidgetsReader. - - Example - Create a button with id "hello": - - - - Example - Create a image from skin element "Icon48": - - -*/ - -class TBEditFieldContentFactory : public TBTextFragmentContentFactory { -public: - class TBEditField *editfield; - virtual int getContent(const char *text); - virtual TBTextFragmentContent *createFragmentContent(const char *text, int text_len); -}; - -/** TBEditFieldScrollRoot - Internal for TBEditField. - Acts as a scrollable container for any widget created as embedded content. */ - -class TBEditFieldScrollRoot : public TBWidget { -private: // May only be used by TBEditField. - friend class TBEditField; - TBEditFieldScrollRoot() { - } - -public: - virtual void onPaintChildren(const PaintProps &paint_props); - virtual void getChildTranslation(int &x, int &y) const; - virtual WIDGET_HIT_STATUS getHitStatus(int x, int y); -}; - -/** TBEditField is a one line or multi line textfield that is editable or - read-only. It can also be a passwordfield by calling - SetEditType(EDIT_TYPE_PASSWORD). - - It may perform styling of text and contain custom embedded content, - if enabled by SetStyling(true). Disabled by default. -*/ - -class TBEditField : public TBWidget, private TBStyleEditListener, public TBMessageHandler { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBEditField, TBWidget); - - TBEditField(); - ~TBEditField(); - - /** Get the visible rect (the GetPaddingRect() minus any scrollbars) */ - TBRect getVisibleRect(); - - /** Set if multiple lines should be allowed or not. - Will also set wrapping (to true if multiline, and false if not). */ - void setMultiline(bool multiline); - bool getMultiline() const { - return m_style_edit.packed.multiline_on; - } - - /** Set if styling should be enabled or not. Default is disabled. */ - void setStyling(bool styling); - bool getStyling() const { - return m_style_edit.packed.styling_on; - } - - /** Set if read only mode should be enabled. Default is disabled. - In read only mode, editing is disabled and caret is hidden. - The user is still able to focus, select and copy text. */ - void setReadOnly(bool readonly); - bool getReadOnly() const { - return m_style_edit.packed.read_only; - } - - /** Set to true if the text should wrap if multi line is enabled (See SetMultiline). */ - void setWrapping(bool wrapping); - bool getWrapping() const { - return m_style_edit.packed.wrapping; - } - - /** Set to true if the preferred size of this editfield should adapt to the - size of the content (disabled by default). - If wrapping is enabled, the result is partly dependant on the virtual - width (See SetVirtualWidth). */ - void setAdaptToContentSize(bool adapt); - bool getAdaptToContentSize() const { - return m_adapt_to_content_size; - } - - /** The virtual width is only used if the size is adapting to content size - (See SetAdaptToContentSize) and wrapping is enabled. - The virtual width will be used to layout the text and see which resulting - width and height it takes up. The width that is actually used depends on - the content. It is also up to the the layouter to decide if the size - should be respected or not. The default is 250. */ - void setVirtualWidth(int virtual_width); - int getVirtualWidth() const { - return m_virtual_width; - } - - /** Get the TBStyleEdit object that contains more functions and settings. */ - TBStyleEdit *getStyleEdit() { - return &m_style_edit; - } - - /** Set the edit type that is a hint for virtual keyboards about what the - content should be. */ - void setEditType(EDIT_TYPE type); - EDIT_TYPE getEditType() const { - return m_edit_type; - } - - /** Support custom skin condition properties. Currently supported properties are: - "edit-type", matching those of EDIT_TYPE. - "multiline", matching 1 if multiline mode is enabled. - "readonly", matching 1 if readonly mode is enabled. */ - virtual bool getCustomSkinCondition(const TBSkinCondition::CONDITION_INFO &info) override; - - /** Set which alignment the text should have if the space - given when painting is larger than the text. - This changes the default for new blocks, as wel as the currently selected blocks or the block - of the current caret position if nothing is selected. */ - void setTextAlign(TB_TEXT_ALIGN align) { - m_style_edit.setAlign(align); - } - TB_TEXT_ALIGN getTextAlign() { - return m_style_edit.align; - } - - void setTextFormatted(CORE_FORMAT_STRING const char *format, ...) CORE_PRINTF_VARARG_FUNC(2); - - virtual bool setText(const char *text) override { - return setText(text, TB_CARET_POS_BEGINNING); - } - virtual bool getText(core::String &text) override { - return m_style_edit.getText(text); - } - virtual void setValue(int value) override { - setTextFormatted("%i", value); - } - - void onProcess() override { - TBWidget::onProcess(); - if (!_var || !_var->isDirty()) { - return; - } - setText(_var->strVal().c_str()); - } - - using TBWidget::getText; ///< Make all versions in base class available. - - using TBWidget::invalidate; ///< Make Invalidate in base class available. - - /** Set the text and also specify if the caret should be positioned at the beginning - or end of the text. */ - bool setText(const char *text, TB_CARET_POS pos) { - if (_var) { - _var->setVal(text); - _var->markClean(); - } - return m_style_edit.setText(text, pos); - } - /** Set the text of the given length and also specify if the caret should be positioned - at the beginning or end of the text. */ - bool setText(const char *text, int textLen, TB_CARET_POS pos = TB_CARET_POS_BEGINNING) { - return m_style_edit.setText(text, textLen, pos); - } - bool setText(const core::String &text, TB_CARET_POS pos = TB_CARET_POS_BEGINNING) { - return m_style_edit.setText(text.c_str(), text.size(), pos); - } - - /** Set the placeholder text. It will be visible only when the textfield is empty. */ - virtual bool setPlaceholderText(const char *text) { - return m_placeholder.setText(text); - } - virtual bool getPlaceholderText(core::String &text) { - return m_placeholder.getText(text); - } - - virtual void scrollTo(int x, int y) override; - virtual TBWidget::ScrollInfo getScrollInfo() override; - virtual TBWidget *getScrollRoot() override { - return &m_root; - } - - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onPaint(const PaintProps &paint_props) override; - virtual void onPaintChildren(const PaintProps &paint_props) override; - virtual void onInflate(const INFLATE_INFO &info) override; - virtual void onAdded() override; - virtual void onFontChanged() override; - virtual void onFocusChanged(bool focused) override; - virtual void onResized(int oldW, int oldH) override; - virtual TBWidget *getContentRoot() override { - return &m_root; - } - - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override; - - virtual void onMessageReceived(TBMessage *msg) override; - -private: - TBScrollBar m_scrollbar_x; - TBScrollBar m_scrollbar_y; - TBWidgetString m_placeholder; - EDIT_TYPE m_edit_type; - TBEditFieldScrollRoot m_root; - TBEditFieldContentFactory m_content_factory; - TBStyleEdit m_style_edit; - core::VarPtr _var; - bool m_adapt_to_content_size; - int m_virtual_width; - void updateScrollbarVisibility(bool multiline); - - // == TBStyleEditListener ======================= - virtual void onChange() override; - virtual bool onEnter() override; - virtual void invalidate(const TBRect &rect) override; - virtual void drawString(int32_t x, int32_t y, TBFontFace *font, const TBColor &color, const char *str, - int32_t len) override; - virtual void drawRect(const TBRect &rect, const TBColor &color) override; - virtual void drawRectFill(const TBRect &rect, const TBColor &color) override; - virtual void drawTextSelectionBg(const TBRect &rect) override; - virtual void drawContentSelectionFg(const TBRect &rect) override; - virtual void drawCaret(const TBRect &rect) override; - virtual void scroll(int32_t dx, int32_t dy) override; - virtual void updateScrollbars() override; - virtual void caretBlinkStart() override; - virtual void caretBlinkStop() override; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_font_desc.h b/src/modules/ui/turbobadger/tb/tb_font_desc.h deleted file mode 100644 index 6b8c0b4e5..000000000 --- a/src/modules/ui/turbobadger/tb/tb_font_desc.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_id.h" -#include "tb_types.h" - -namespace tb { - -/** TBFontDescription describes a font. - By default when nothing is set, the font is unspecified and means it should be inherited - from a parent widget that specifies a font, or use the default font if no parent does. */ - -class TBFontDescription { -public: - /** Set the font ID of the font to use. - This ID maps to the font names in TBFontInfo, which is managed from - TBFontManager::AddFontInfo, TBFontManager::getFontInfo. - - Example: - If a font was added to the font manager with the name "Vera", you can - do font_description.setID(TBIDC("Vera")). - */ - void setID(const TBID &id) { - m_id = id; - } - - /** Get the TBID for the font name (See SetID). */ - TBID getID() const { - return m_id; - } - - /** Get the TBID for the TBFontFace that matches this font description. - This is a ID combining both the font file, and variation (such as size and style), - and should be used to identify a certain font face. - - If this is 0, the font description is unspecified. For a widget, that means that the font - should be inherited from the parent widget. */ - TBID getFontFaceID() const { - return m_id + m_packed_init; - } - - void setSize(uint32_t size) { - m_packed.size = Min(size, 0x8000U); - } - uint32_t getSize() const { - return m_packed.size; - } - - // not connected to anything yet - // void setBold(bool bold) { m_packed.bold = bold; } - // bool getBold() const { return m_packed.bold; } - - // not connected to anything yet - // void setItalic(bool italic) { m_packed.italic = italic; } - // bool getItalic() const { return m_packed.italic; } - - TBFontDescription() : m_packed_init(0) { - } - TBFontDescription(const TBFontDescription &src) { - m_packed_init = src.m_packed_init; - m_id = src.m_id; - } - const TBFontDescription &operator=(const TBFontDescription &src) { - m_packed_init = src.m_packed_init; - m_id = src.m_id; - return *this; - } - bool operator==(const TBFontDescription &fd) const { - return m_packed_init == fd.m_packed_init && m_id == fd.m_id; - } - bool operator!=(const TBFontDescription &fd) const { - return !(*this == fd); - } - -private: - TBID m_id; - union { - struct { - uint32_t size : 15; - uint32_t italic : 1; - uint32_t bold : 1; - } m_packed; - uint32_t m_packed_init; - }; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_font_renderer.cpp b/src/modules/ui/turbobadger/tb/tb_font_renderer.cpp deleted file mode 100644 index 2fc179916..000000000 --- a/src/modules/ui/turbobadger/tb/tb_font_renderer.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/** - * @file - */ - -#include "tb_font_renderer.h" -#include "tb_renderer.h" -#include "tb_skin.h" -#include "tb_system.h" -#include -#include -#if SDL_VERSION_ATLEAST(2, 0, 9) -#else -#include -#define SDL_expf expf -#endif - -namespace tb { - -static void blurGlyph(const unsigned char *src, int srcw, int srch, int srcStride, unsigned char *dst, int dstw, - int dsth, int dstStride, float *temp, const float *kernel, int kernelRadius) { - for (int y = 0; y < srch; y++) { - for (int x = 0; x < dstw; x++) { - float val = 0; - for (int k_ofs = -kernelRadius; k_ofs <= kernelRadius; k_ofs++) { - if (x - kernelRadius + k_ofs >= 0 && x - kernelRadius + k_ofs < srcw) { - val += src[y * srcStride + x - kernelRadius + k_ofs] * kernel[k_ofs + kernelRadius]; - } - } - temp[y * dstw + x] = val; - } - } - for (int y = 0; y < dsth; y++) { - for (int x = 0; x < dstw; x++) { - float val = 0; - for (int k_ofs = -kernelRadius; k_ofs <= kernelRadius; k_ofs++) { - if (y - kernelRadius + k_ofs >= 0 && y - kernelRadius + k_ofs < srch) { - val += temp[(y - kernelRadius + k_ofs) * dstw + x] * kernel[k_ofs + kernelRadius]; - } - } - dst[y * dstStride + x] = (unsigned char)(val + 0.5F); - } - } -} - -void TBFontEffect::setBlurRadius(int blurRadius) { - core_assert(blurRadius >= 0); - if (m_blur_radius == blurRadius) { - return; - } - m_blur_radius = blurRadius; - if (m_blur_radius > 0) { - if (!m_kernel.reserve(sizeof(float) * (m_blur_radius * 2 + 1))) { - m_blur_radius = 0; - return; - } - float *kernel = (float *)m_kernel.getData(); - float stdDevSq2 = (float)m_blur_radius / 2.F; - stdDevSq2 = 2.F * stdDevSq2 * stdDevSq2; - float scale = 1.F / SDL_sqrtf(3.1415F * stdDevSq2); - float sum = 0; - for (int k = 0; k < 2 * m_blur_radius + 1; k++) { - float x = (float)(k - m_blur_radius); - float kval = scale * SDL_expf(-(x * x / stdDevSq2)); - kernel[k] = kval; - sum += kval; - } - for (int k = 0; k < 2 * m_blur_radius + 1; k++) { - kernel[k] /= sum; - } - } -} - -TBFontGlyphData *TBFontEffect::render(TBGlyphMetrics *metrics, const TBFontGlyphData *src) { - TBFontGlyphData *effect_glyph_data = nullptr; - if (m_blur_radius > 0 && (src->data8 != nullptr)) { - // Create a new TBFontGlyphData for the blurred glyph - effect_glyph_data = new TBFontGlyphData; - if (effect_glyph_data == nullptr) { - return nullptr; - } - effect_glyph_data->w = src->w + m_blur_radius * 2; - effect_glyph_data->h = src->h + m_blur_radius * 2; - effect_glyph_data->stride = effect_glyph_data->w; - - // Reserve memory needed for blurring. - if (!m_data_dst.reserve(effect_glyph_data->w * effect_glyph_data->h) || - !m_blur_temp.reserve(effect_glyph_data->w * effect_glyph_data->h * sizeof(float))) { - delete effect_glyph_data; - return nullptr; - } - effect_glyph_data->data8 = (uint8_t *)m_data_dst.getData(); - - // Blur! - blurGlyph(src->data8, src->w, src->h, src->stride, effect_glyph_data->data8, effect_glyph_data->w, - effect_glyph_data->h, effect_glyph_data->w, (float *)m_blur_temp.getData(), - (float *)m_kernel.getData(), m_blur_radius); - - // Adjust glyph position to compensate for larger size. - metrics->x -= m_blur_radius; - metrics->y -= m_blur_radius; - } - return effect_glyph_data; -} - -// == TBFontGlyph ================================================================================= - -TBFontGlyph::TBFontGlyph(const TBID &hashId, UCS4 cp) : hash_id(hashId), cp(cp), frag(nullptr), has_rgb(false) { -} - -// == TBFontGlyphCache ============================================================================ - -TBFontGlyphCache::TBFontGlyphCache() { - // Only use one map for the font face. The glyph cache will start forgetting - // glyphs that haven't been used for a while if the map gets full. - m_frag_manager.setNumMapsLimit(1); - m_frag_manager.setDefaultMapSize(TB_GLYPH_CACHE_WIDTH, TB_GLYPH_CACHE_HEIGHT); - - g_renderer->addListener(this); -} - -TBFontGlyphCache::~TBFontGlyphCache() { - g_renderer->removeListener(this); -} - -TBFontGlyph *TBFontGlyphCache::getGlyph(const TBID &hashId, UCS4 cp) { - if (TBFontGlyph *glyph = m_glyphs.get(hashId)) { - // Move the glyph to the end of m_all_rendered_glyphs so we maintain LRU (oldest first) - if (m_all_rendered_glyphs.containsLink(glyph)) { - m_all_rendered_glyphs.remove(glyph); - m_all_rendered_glyphs.addLast(glyph); - } - return glyph; - } - return nullptr; -} - -TBFontGlyph *TBFontGlyphCache::createAndCacheGlyph(const TBID &hashId, UCS4 cp) { - core_assert(!getGlyph(hashId, cp)); - TBFontGlyph *glyph = new TBFontGlyph(hashId, cp); - if ((glyph != nullptr) && m_glyphs.add(glyph->hash_id, glyph)) { - return glyph; - } - delete glyph; - return nullptr; -} - -TBBitmapFragment *TBFontGlyphCache::createFragment(TBFontGlyph *glyph, int w, int h, int stride, uint32_t *data) { - core_assert(getGlyph(glyph->hash_id, glyph->cp)); - // Don't bother if the requested glyph is too large. - if (w > TB_GLYPH_CACHE_WIDTH || h > TB_GLYPH_CACHE_HEIGHT) { - return nullptr; - } - - bool try_drop_largest = true; - bool dropped_large_enough_glyph = false; - do { - // Attempt creating a fragment for the rendered glyph data - if (TBBitmapFragment *frag = m_frag_manager.createNewFragment(glyph->hash_id, false, w, h, stride, data)) { - glyph->frag = frag; - m_all_rendered_glyphs.addLast(glyph); - return frag; - } - // Drop the oldest glyph that's large enough to free up the space we need. - if (try_drop_largest) { - const int check_limit = 20; - int check_count = 0; - for (TBFontGlyph *oldest = m_all_rendered_glyphs.getFirst(); - (oldest != nullptr) && check_count < check_limit; oldest = oldest->getNext()) { - if (oldest->frag->width() >= w && oldest->frag->getAllocatedHeight() >= h) { - dropGlyphFragment(oldest); - dropped_large_enough_glyph = true; - break; - } - check_count++; - } - try_drop_largest = false; - } - // We had no large enough glyph so just drop the oldest one. We will likely - // spin around the loop, fail and drop again a few times before we succeed. - if (!dropped_large_enough_glyph) { - if (TBFontGlyph *oldest = m_all_rendered_glyphs.getFirst()) { - dropGlyphFragment(oldest); - } else { - break; - } - } - } while (true); - return nullptr; -} - -void TBFontGlyphCache::dropGlyphFragment(TBFontGlyph *glyph) { - core_assert(glyph->frag); - m_frag_manager.freeFragment(glyph->frag); - glyph->frag = nullptr; - m_all_rendered_glyphs.remove(glyph); -} - -#ifdef TB_RUNTIME_DEBUG_INFO -void TBFontGlyphCache::debug() { - m_frag_manager.debug(); -} -#endif // TB_RUNTIME_DEBUG_INFO - -void TBFontGlyphCache::onContextLost() { - m_frag_manager.deleteBitmaps(); -} - -void TBFontGlyphCache::onContextRestored() { - // No need to do anything. The bitmaps will be created when drawing. -} - -// ================================================================================================ - -TBFontFace::TBFontFace(TBFontGlyphCache *glyphCache, TBFontRenderer *renderer, const TBFontDescription &fontDesc) - : m_glyph_cache(glyphCache), m_font_renderer(renderer), m_font_desc(fontDesc), m_bgFont(nullptr), m_bgX(0), - m_bgY(0) { - if (m_font_renderer != nullptr) { - m_metrics = m_font_renderer->getMetrics(); - } else { - // Invent some metrics for the test font - int size = m_font_desc.getSize(); - m_metrics.ascent = size - size / 4; - m_metrics.descent = size / 4; - m_metrics.height = size; - } -} - -TBFontFace::~TBFontFace() { - // It would be nice to drop all glyphs we have live for this font face. - // Now they only die when they get old and kicked out of the cache. - // We currently don't drop any font faces either though (except on shutdown) - delete m_font_renderer; -} - -void TBFontFace::setBackgroundFont(TBFontFace *font, const TBColor &col, int xofs, int yofs) { - m_bgFont = font; - m_bgX = xofs; - m_bgY = yofs; - m_bgColor = col; -} - -bool TBFontFace::renderGlyphs(const char *glyphStr) { - if (m_font_renderer == nullptr) { - return true; // This is the test font - } - - const int glyphStrLen = SDL_strlen(glyphStr); - bool has_all_glyphs = true; - int i = 0; - while (i < glyphStrLen && glyphStr[i] != '\0') { - UCS4 cp = utf8::decode_next(glyphStr, &i, glyphStrLen); - if (getGlyph(cp, true) == nullptr) { - has_all_glyphs = false; - } - } - return has_all_glyphs; -} - -TBFontGlyph *TBFontFace::createAndCacheGlyph(const TBID &hashId, UCS4 cp) { - if (m_font_renderer == nullptr) { - return nullptr; // This is the test font - } - - // Create the new glyph - TBFontGlyph *glyph = m_glyph_cache->createAndCacheGlyph(hashId, cp); - if (glyph != nullptr) { - m_font_renderer->getGlyphMetrics(&glyph->metrics, cp); - } - return glyph; -} - -void TBFontFace::renderGlyph(TBFontGlyph *glyph) { - core_assert(!glyph->frag); - TBFontGlyphData glyph_data; - if (m_font_renderer->renderGlyph(&glyph_data, glyph->cp)) { - TBFontGlyphData *effect_glyph_data = m_effect.render(&glyph->metrics, &glyph_data); - TBFontGlyphData *result_glyph_data = effect_glyph_data != nullptr ? effect_glyph_data : &glyph_data; - - // The glyph data may be in uint8 format, which we have to convert since we always - // create fragments (and TBBitmap) in 32bit format. - uint32_t *glyph_dsta_src = result_glyph_data->data32; - if ((glyph_dsta_src == nullptr) && (result_glyph_data->data8 != nullptr)) { - if (m_temp_buffer.reserve(result_glyph_data->w * result_glyph_data->h * sizeof(uint32_t))) { - glyph_dsta_src = (uint32_t *)m_temp_buffer.getData(); - for (int y = 0; y < result_glyph_data->h; y++) { - for (int x = 0; x < result_glyph_data->w; x++) { -#ifdef TB_PREMULTIPLIED_ALPHA - uint8_t opacity = result_glyph_data->data8[x + y * result_glyph_data->stride]; - glyph_dsta_src[x + y * result_glyph_data->w] = TBColor(opacity, opacity, opacity, opacity); -#else - glyph_dsta_src[x + y * result_glyph_data->w] = - TBColor(255, 255, 255, result_glyph_data->data8[x + y * result_glyph_data->stride]); -#endif - } - } - } - } - - // Finally, the glyph data is ready and we can create a bitmap fragment. - if (glyph_dsta_src != nullptr) { - glyph->has_rgb = result_glyph_data->rgb; - m_glyph_cache->createFragment(glyph, result_glyph_data->w, result_glyph_data->h, result_glyph_data->stride, - glyph_dsta_src); - } - - delete effect_glyph_data; - } -#ifdef TB_RUNTIME_DEBUG_INFO - // char glyph_str[9]; - // int len = utf8::encode(cp, glyph_str); - // glyph_str[len] = 0; - // Log::debug("Created glyph %d (\"%s\"). Cache contains %d glyphs (%d%% full) using %d bitmaps.", - // cp, glyph_str, m_all_glyphs.CountLinks(), m_frag_manager.getUseRatio(), m_frag_manager.getNumMaps()); -#endif -} - -TBID TBFontFace::getHashId(UCS4 cp) const { - return cp * 3111 + m_font_desc.getFontFaceID(); -} - -TBFontGlyph *TBFontFace::getGlyph(UCS4 cp, bool renderIfNeeded) { - const TBID &hash_id = getHashId(cp); - TBFontGlyph *glyph = m_glyph_cache->getGlyph(hash_id, cp); - if (glyph == nullptr) { - glyph = createAndCacheGlyph(hash_id, cp); - } - if ((glyph != nullptr) && (glyph->frag == nullptr) && renderIfNeeded) { - renderGlyph(glyph); - } - return glyph; -} - -void TBFontFace::drawString(int x, int y, const TBColor &color, const char *str, int len) { - if (m_bgFont != nullptr) { - m_bgFont->drawString(x + m_bgX, y + m_bgY, m_bgColor, str, len); - } - - if (m_font_renderer != nullptr) { - g_renderer->beginBatchHint(TBRenderer::BATCH_HINT_DRAW_BITMAP_FRAGMENT); - } - - int i = 0; - while (i < len && str[i] != '\0') { - UCS4 cp = utf8::decode_next(str, &i, len); - if (cp == 0xFFFF) { - continue; - } - if (TBFontGlyph *glyph = getGlyph(cp, true)) { - if (glyph->frag != nullptr) { - TBRect dst_rect(x + glyph->metrics.x, y + glyph->metrics.y + getAscent(), glyph->frag->width(), - glyph->frag->height()); - TBRect src_rect(0, 0, glyph->frag->width(), glyph->frag->height()); - if (glyph->has_rgb) { - g_renderer->drawBitmap(dst_rect, src_rect, glyph->frag); - } else { - g_renderer->drawBitmapColored(dst_rect, src_rect, color, glyph->frag); - } - } - x += glyph->metrics.advance; - } else if (m_font_renderer == nullptr) // This is the test font. Use same glyph width as height and draw square. - { - g_tb_skin->paintRect(TBRect(x, y, m_metrics.height / 3, m_metrics.height), color, 1); - x += m_metrics.height / 3 + 1; - } - } - - if (m_font_renderer != nullptr) { - g_renderer->endBatchHint(); - } -} - -int TBFontFace::getStringWidth(const char *str, int len) { - int width = 0; - int i = 0; - while (i < len && str[i] != '\0') { - UCS4 cp = utf8::decode_next(str, &i, len); - if (cp == 0xFFFF) { - continue; - } - if (m_font_renderer == nullptr) { // This is the test font. Use same glyph width as height. - width += m_metrics.height / 3 + 1; - } else if (TBFontGlyph *glyph = getGlyph(cp, false)) { - width += glyph->metrics.advance; - } - } - return width; -} - -#ifdef TB_RUNTIME_DEBUG_INFO -void TBFontFace::debug() { - m_glyph_cache->debug(); -} -#endif // TB_RUNTIME_DEBUG_INFO - -// == TBFontManager =============================================================================== - -TBFontManager::TBFontManager() { - // Add the test dummy font with empty name (Equals to ID 0) - addFontInfo("-test-font-dummy-", ""); - m_test_font_desc.setSize(16); - createFontFace(m_test_font_desc); - - // Use the test dummy font as default by default - m_default_font_desc = m_test_font_desc; -} - -TBFontManager::~TBFontManager() { -} - -TBFontInfo *TBFontManager::addFontInfo(const char *filename, const char *name) { - if (TBFontInfo *fi = new TBFontInfo(filename, name)) { - if (m_font_info.add(fi->getID(), fi)) { - return fi; - } - delete fi; - } - return nullptr; -} - -TBFontInfo *TBFontManager::getFontInfo(const TBID &id) const { - return m_font_info.get(id); -} - -bool TBFontManager::hasFontFace(const TBFontDescription &fontDesc) const { - return m_fonts.get(fontDesc.getFontFaceID()) != nullptr; -} - -TBFontFace *TBFontManager::getFontFace(const TBFontDescription &fontDesc) { - if (TBFontFace *font = m_fonts.get(fontDesc.getFontFaceID())) { - return font; - } - if (TBFontFace *font = m_fonts.get(getDefaultFontDescription().getFontFaceID())) { - return font; - } - return m_fonts.get(m_test_font_desc.getFontFaceID()); -} - -TBFontFace *TBFontManager::createFontFace(const TBFontDescription &fontDesc) { - core_assert(!hasFontFace(fontDesc)); // There is already a font added with this description! - - TBFontInfo *fi = getFontInfo(fontDesc.getID()); - if (fi == nullptr) { - return nullptr; - } - - if (fi->getID() == 0) // Is this the test dummy font - { - if (TBFontFace *font = new TBFontFace(&m_glyph_cache, nullptr, fontDesc)) { - if (m_fonts.add(fontDesc.getFontFaceID(), font)) { - return font; - } - delete font; - } - return nullptr; - } - - // Iterate through font renderers until we find one capable of creating a font for this file. - for (TBFontRenderer *fr = m_font_renderers.getFirst(); fr != nullptr; fr = fr->getNext()) { - if (TBFontFace *font = fr->create(this, fi->getFilename(), fontDesc)) { - if (m_fonts.add(fontDesc.getFontFaceID(), font)) { - return font; - } - delete font; - } - } - return nullptr; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_font_renderer.h b/src/modules/ui/turbobadger/tb/tb_font_renderer.h deleted file mode 100644 index 5fb15d1e8..000000000 --- a/src/modules/ui/turbobadger/tb/tb_font_renderer.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_bitmap_fragment.h" -#include "tb_core.h" -#include "tb_font_desc.h" -#include "tb_linklist.h" -#include "tb_renderer.h" -#include "tb_tempbuffer.h" -#include "utf8/utf8.h" - -namespace tb { - -class TBBitmap; -class TBFontFace; - -/** TBFontGlyphData is rendering info used during glyph rendering by TBFontRenderer. - It does not own the data pointers. */ -class TBFontGlyphData { -public: - TBFontGlyphData() : data8(nullptr), data32(nullptr), w(0), h(0), stride(0), rgb(false) { - } - ~TBFontGlyphData() { - } - - uint8_t *data8; - uint32_t *data32; - int w, h, stride; - bool rgb; -}; - -/** TBGlyphMetrics contains metrics for a font glyph. */ -class TBGlyphMetrics { -public: - TBGlyphMetrics() : advance(0), x(0), y(0) { - } - int16_t advance, x, y; -}; - -/** TBFontMetrics contains metrics for a font face. */ -class TBFontMetrics { -public: - TBFontMetrics() : ascent(0), descent(0), height(0) { - } - int16_t ascent; ///< Ascent. See TBFontFace::getAscent() - int16_t descent; ///< Descent. See TBFontFace::getDescent() - int16_t height; ///< Height. See TBFontFace::getHeight() -}; - -/** TBFontRenderer renders glyphs from a font file. */ -class TBFontRenderer : public TBLinkOf { -public: - virtual ~TBFontRenderer() { - } - - /** Open the given font file with this renderer and return a new TBFontFace with it. - return nullptr if the file can't be opened by this renderer. */ - virtual TBFontFace *create(TBFontManager *fontManager, const char *filename, const TBFontDescription &fontDesc) = 0; - - virtual bool renderGlyph(TBFontGlyphData *data, UCS4 cp) = 0; - virtual void getGlyphMetrics(TBGlyphMetrics *metrics, UCS4 cp) = 0; - virtual TBFontMetrics getMetrics() = 0; - // virtual int getKernAdvance(UCS4 cp1, UCS4 cp2) = 0; -}; - -/** TBFontGlyph holds glyph metrics and bitmap fragment. - There's one of these for all rendered (both successful - and missing) glyphs in TBFontFace. */ -class TBFontGlyph : public TBLinkOf { -public: - TBFontGlyph(const TBID &hash_id, UCS4 cp); - TBID hash_id; - UCS4 cp; - TBGlyphMetrics metrics; ///< The glyph metrics. - TBBitmapFragment *frag; ///< The bitmap fragment, or nullptr if missing. - bool has_rgb; ///< if true, drawing should ignore text color. -}; - -/** TBFontGlyphCache caches glyphs for font faces. - Rendered glyphs use bitmap fragments from its fragment manager. */ -class TBFontGlyphCache : private TBRendererListener { -public: - TBFontGlyphCache(); - ~TBFontGlyphCache(); - - /** Get the glyph or nullptr if it is not in the cache. */ - TBFontGlyph *getGlyph(const TBID &hash_id, UCS4 cp); - - /** Create the glyph and put it in the cache. Returns the glyph, or nullptr on fail. */ - TBFontGlyph *createAndCacheGlyph(const TBID &hash_id, UCS4 cp); - - /** Create a bitmap fragment for the given glyph and render data. This may drop other - rendered glyphs from the fragment map. Returns the fragment, or nullptr on fail. */ - TBBitmapFragment *createFragment(TBFontGlyph *glyph, int w, int h, int stride, uint32_t *data); - -#ifdef TB_RUNTIME_DEBUG_INFO - /** Render the glyph bitmaps on screen, to analyze fragment positioning. */ - void debug(); -#endif - - // Implementing TBRendererListener - void onContextLost() override; - void onContextRestored() override; - -private: - void dropGlyphFragment(TBFontGlyph *glyph); - TBBitmapFragmentManager m_frag_manager; - TBHashTableAutoDeleteOf m_glyphs; - TBLinkListOf m_all_rendered_glyphs; -}; - -/** TBFontEffect applies an effect on each glyph that is rendered in a TBFontFace. */ -class TBFontEffect { -public: - TBFontEffect() : m_blur_radius(0) { - } - ~TBFontEffect() { - } - - /** Set blur radius. 0 means no blur. */ - void setBlurRadius(int blur_radius); - - /** Returns true if the result is in RGB and should not be painted using the color parameter - given to DrawString. In other words: It's a color glyph. */ - bool rendersInRGB() const { - return false; - } - - TBFontGlyphData *render(TBGlyphMetrics *metrics, const TBFontGlyphData *src); - -private: - // Blur data - int m_blur_radius; - TBTempBuffer m_kernel; - TBTempBuffer m_blur_temp; - TBTempBuffer m_data_dst; -}; - -/** TBFontFace represents a loaded font that can measure and render strings. */ -class TBFontFace { -public: - TBFontFace(TBFontGlyphCache *glyphCache, TBFontRenderer *renderer, const TBFontDescription &fontDesc); - ~TBFontFace(); - - /** Render all glyphs needed to display the string. */ - bool renderGlyphs(const char *glyphStr); - - /** Get the vertical distance (positive) from the horizontal baseline to the highest character coordinate - in a font face. */ - int getAscent() const { - return m_metrics.ascent; - } - - /** Get the vertical distance (positive) from the horizontal baseline to the lowest character coordinate - in the font face. */ - int getDescent() const { - return m_metrics.descent; - } - - /** Get height of the font in pixels. */ - int getHeight() const { - return m_metrics.height; - } - - /** Get the font description that was used to create this font. */ - TBFontDescription getFontDescription() const { - return m_font_desc; - } - - /** Get the effect object, so the effect can be changed. - Note: No glyphs are re-rendered. Only new glyphs are affected. */ - TBFontEffect *getEffect() { - return &m_effect; - } - - /** Draw string at position x, y (marks the upper left corner of the text). */ - void drawString(int x, int y, const TBColor &color, const char *str, int len); - - /** Measure the width of the given string. Should measure len characters or to the null - termination (whatever comes first). */ - int getStringWidth(const char *str, int len); - -#ifdef TB_RUNTIME_DEBUG_INFO - /** Render the glyph bitmaps on screen, to analyze fragment positioning. */ - void debug(); -#endif - - /** Set a background font which will always be rendered behind this one - when calling DrawString. Very usefull to add a shadow effect to a font. */ - void setBackgroundFont(TBFontFace *font, const TBColor &col, int xofs, int yofs); - -private: - TBID getHashId(UCS4 cp) const; - TBFontGlyph *getGlyph(UCS4 cp, bool render_if_needed); - TBFontGlyph *createAndCacheGlyph(const TBID &hash_id, UCS4 cp); - void renderGlyph(TBFontGlyph *glyph); - TBFontGlyphCache *m_glyph_cache; - TBFontRenderer *m_font_renderer; - TBFontDescription m_font_desc; - TBFontMetrics m_metrics; - TBFontEffect m_effect; - TBTempBuffer m_temp_buffer; - - TBFontFace *m_bgFont; - int m_bgX; - int m_bgY; - TBColor m_bgColor; -}; - -/** TBFontInfo provides information about a font file associated with a font id. */ -class TBFontInfo { -public: - /** Get the font filename. */ - const char *getFilename() const { - return m_filename.c_str(); - } - - /** Get the font name. */ - const char *getName() const { - return m_name.c_str(); - } - - /** Get the font ID that can be used to create this font from a - TBFontDescription (See TBFontDescription::setID) */ - TBID getID() const { - return m_id; - } - -private: - friend class TBFontManager; - TBFontInfo(const char *filename, const char *name) : m_filename(filename), m_name(name), m_id(name) { - } - core::String m_filename; - core::String m_name; - TBID m_id; -}; - -/** TBFontManager creates and owns font faces (TBFontFace) which are looked up from - TBFontDescription using GetFontFace. - - The fonts it can return must first have their file added and indexed (AddFontInfo), - and then created CreateFontFace. Otherwise when asking for a font and it doesn't - exist, it will use the default font. - - Font ID 0 is always populated with a dummy font that draws squares. This font is - generally not used for other things than unit testing or as fallback when there is - no font backend implemented yet. Since there is always at least the test font, no - nullptr checks are needed. -*/ -class TBFontManager { -public: - TBFontManager(); - ~TBFontManager(); - - /** Add a renderer so fonts supported by the renderer can be created. Ownership of the - renderer is taken, until calling RemoveRenderer. */ - void addRenderer(TBFontRenderer *renderer) { - m_font_renderers.addLast(renderer); - } - void removeRenderer(TBFontRenderer *renderer) { - m_font_renderers.remove(renderer); - } - - /** Add TBFontInfo for the given font filename, so it can be loaded and identified - using the font id in a TBFontDescription. */ - TBFontInfo *addFontInfo(const char *filename, const char *name); - - /** Get TBFontInfo for the given font id, or nullptr if there is no match. */ - TBFontInfo *getFontInfo(const TBID &id) const; - - /** Return true if there is a font loaded that match the given font description. */ - bool hasFontFace(const TBFontDescription &font_desc) const; - - /** Get a loaded font matching the description, or the default font if there is no exact match. - If there is not even any default font loaded, it will return the test dummy font (rendering - only squares). */ - TBFontFace *getFontFace(const TBFontDescription &font_desc); - - /** Create and add a font with the given description. Returns the created font face, or - nullptr on fail. The font is owned by this TBFontManager, and can be recieved from - GetFontFace using the same TBFontDescription. */ - TBFontFace *createFontFace(const TBFontDescription &font_desc); - - /** Set the default font description. This is the font description that will be used by default - for widgets. By default, the default description is using the test dummy font. */ - void setDefaultFontDescription(const TBFontDescription &fontDesc) { - m_default_font_desc = fontDesc; - } - TBFontDescription getDefaultFontDescription() const { - return m_default_font_desc; - } - - /** Return the glyph cache used for fonts created by this font manager. */ - TBFontGlyphCache *getGlyphCache() { - return &m_glyph_cache; - } - -private: - TBHashTableAutoDeleteOf m_font_info; - TBHashTableAutoDeleteOf m_fonts; - TBLinkListAutoDeleteOf m_font_renderers; - TBFontGlyphCache m_glyph_cache; - TBFontDescription m_default_font_desc; - TBFontDescription m_test_font_desc; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_font_renderer_stb.cpp b/src/modules/ui/turbobadger/tb/tb_font_renderer_stb.cpp deleted file mode 100644 index d087f605c..000000000 --- a/src/modules/ui/turbobadger/tb/tb_font_renderer_stb.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @file - */ - -#include "core/Assert.h" -#include "tb_font_renderer.h" -#include "tb_renderer.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" - -using namespace tb; - -#define STBTT_assert core_assert -#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -#include "thirdparty/stb_truetype.h" -#undef STB_TRUETYPE_IMPLEMENTATION - -/** STBFontRenderer renders fonts using stb_truetype.h (http://nothings.org/) */ - -class STBFontRenderer : public TBFontRenderer { -public: - STBFontRenderer(); - ~STBFontRenderer(); - - bool load(const char *filename, int size); - - virtual TBFontFace *create(TBFontManager *fontManager, const char *filename, const TBFontDescription &fontDesc); - - virtual TBFontMetrics getMetrics(); - virtual bool renderGlyph(TBFontGlyphData *data, UCS4 cp); - virtual void getGlyphMetrics(TBGlyphMetrics *metrics, UCS4 cp); - -private: - stbtt_fontinfo font; - TBTempBuffer ttf_buffer; - unsigned char *render_data = nullptr; - int font_size = 0; - float scale = 0.0F; -}; - -STBFontRenderer::STBFontRenderer() { -} - -STBFontRenderer::~STBFontRenderer() { - STBTT_free(render_data, 0); -} - -TBFontMetrics STBFontRenderer::getMetrics() { - TBFontMetrics metrics; - int ascent; - int descent; - int lineGap; - stbtt_GetFontVMetrics(&font, &ascent, &descent, &lineGap); - metrics.ascent = (int)(ascent * scale + 0.5F); - metrics.descent = (int)((-descent) * scale + 0.5F); - metrics.height = (int)((ascent - descent + lineGap) * scale + 0.5F); - return metrics; -} - -bool STBFontRenderer::renderGlyph(TBFontGlyphData *data, UCS4 cp) { - STBTT_free(render_data, 0); - render_data = stbtt_GetCodepointBitmap(&font, 0, scale, cp, &data->w, &data->h, 0, 0); - data->data8 = render_data; - data->stride = data->w; - data->rgb = false; - return data->data8 != nullptr; -} - -void STBFontRenderer::getGlyphMetrics(TBGlyphMetrics *metrics, UCS4 cp) { - int advanceWidth; - int leftSideBearing; - stbtt_GetCodepointHMetrics(&font, cp, &advanceWidth, &leftSideBearing); - metrics->advance = (int)(advanceWidth * scale + 0.5F); - int ix0; - int iy0; - int ix1; - int iy1; - stbtt_GetCodepointBitmapBox(&font, cp, 0, scale, &ix0, &iy0, &ix1, &iy1); - metrics->x = ix0; - metrics->y = iy0; -} - -bool STBFontRenderer::load(const char *filename, int size) { - if (!ttf_buffer.appendFile(filename)) { - return false; - } - - const unsigned char *ttf_ptr = (const unsigned char *)ttf_buffer.getData(); - stbtt_InitFont(&font, ttf_ptr, stbtt_GetFontOffsetForIndex(ttf_ptr, 0)); - - font_size = (int)(size * 1.3F); // FIX: Constant taken out of thin air because fonts get too small. - scale = stbtt_ScaleForPixelHeight(&font, (float)font_size); - return true; -} - -TBFontFace *STBFontRenderer::create(TBFontManager *fontManager, const char *filename, - const TBFontDescription &fontDesc) { - if (STBFontRenderer *fr = new STBFontRenderer()) { - if (fr->load(filename, (int)fontDesc.getSize())) { - if (TBFontFace *font = new TBFontFace(fontManager->getGlyphCache(), fr, fontDesc)) { - return font; - } - } - delete fr; - } - return nullptr; -} - -void register_stb_font_renderer() { - if (STBFontRenderer *fr = new STBFontRenderer) { - g_font_manager->addRenderer(fr); - } -} diff --git a/src/modules/ui/turbobadger/tb/tb_font_renderer_tbbf.cpp b/src/modules/ui/turbobadger/tb/tb_font_renderer_tbbf.cpp deleted file mode 100644 index d5510824d..000000000 --- a/src/modules/ui/turbobadger/tb/tb_font_renderer_tbbf.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/** - * @file - */ - -#include "image/Image.h" -#include "tb_font_renderer.h" -#include "tb_hashtable.h" -#include "tb_node_tree.h" -#include "tb_renderer.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" - -using namespace tb; - -struct GLYPH { - int x, w; -}; - -/** TBBFRenderer renders a bitmap font. - - A font is loaded from a text file and at least one image that contains - glyphs for a given size. The number of glyphs that the font contains is - defined by the glyph string defined in the text file. - - Text file format (in tb.txt format parsed by parser/tb_parser.h): - - - info>glyph_str Should specify which characters the image - file contains. - - info>rgb Set to 1 for color fonts that should never - care about the text color when drawing. - Set to 0 to let drawing blend using the text - color. Default 0. - - size xx Specify font size xx. Should contain the - following nodes: - - bitmap The image file name (in the same folder). - - ascent The ascent. Default 0. - - descent The descent. Default 0. - - x_ofs The x offset for all glyph. This can be - used in combination with advance_delta to - compensate for f.ex glow that extend - around the glyph. Default 0. - - advance_delta The advance delta for all glyphs. This can - be used to compensate for f.ex shadow that - should not add to each glyphs horizontal - advance. Default 0. - - space_advance The advance for the space character. - - Image file format - - Should contain the characters specified in the glyph_str. - - All characters should be placed on one long line. Each glyph will be - found, measured and cropped automatically. In order for this to work, - each glyph must touch pixels somewhere from the left to the right edge. - So if you f.ex have a quotation mark, you will have to make sure there - is pixels with alpha > 0 between the two dots, otherwise the dots will - be identified as different glyphs. -*/ -class TBBFRenderer : public TBFontRenderer { -public: - TBBFRenderer(); - ~TBBFRenderer(); - - bool load(const char *filename, int size); - bool findGlyphs(); - GLYPH *findNext(UCS4 cp, int x); - - virtual TBFontFace *create(TBFontManager *fontManager, const char *filename, const TBFontDescription &fontDesc); - - virtual TBFontMetrics getMetrics(); - virtual bool renderGlyph(TBFontGlyphData *data, UCS4 cp); - virtual void getGlyphMetrics(TBGlyphMetrics *metrics, UCS4 cp); - -private: - TBNode m_node; - TBFontMetrics m_metrics; - image::ImagePtr m_img; - int m_size; - int m_x_ofs; - int m_advance_delta; - int m_space_advance; - int m_rgb; - TBHashTableAutoDeleteOf m_glyph_table; -}; - -TBBFRenderer::TBBFRenderer() : m_size(0), m_x_ofs(0), m_advance_delta(0), m_space_advance(0), m_rgb(0) { -} - -TBBFRenderer::~TBBFRenderer() { -} - -TBFontMetrics TBBFRenderer::getMetrics() { - return m_metrics; -} - -bool TBBFRenderer::renderGlyph(TBFontGlyphData *data, UCS4 cp) { - if (cp == ' ') { - return false; - } - GLYPH *glyph; - if (((glyph = m_glyph_table.get(cp)) != nullptr) || ((glyph = m_glyph_table.get('?')) != nullptr)) { - data->w = glyph->w; - data->h = m_img->height(); - data->stride = m_img->width(); - data->data32 = (uint32_t *)(m_img->data()) + glyph->x; - data->rgb = m_rgb != 0; - return true; - } - return false; -} - -void TBBFRenderer::getGlyphMetrics(TBGlyphMetrics *metrics, UCS4 cp) { - metrics->x = m_x_ofs; - metrics->y = -m_metrics.ascent; - if (cp == ' ') { - metrics->advance = m_space_advance; - } else if (GLYPH *glyph = m_glyph_table.get(cp)) { - metrics->advance = glyph->w + m_advance_delta; - } else if (GLYPH *glyph = m_glyph_table.get('?')) { - metrics->advance = glyph->w + m_advance_delta; - } -} - -bool TBBFRenderer::load(const char *filename, int size) { - m_size = size; - if (!m_node.readFile(filename)) { - return false; - } - - // Check for size nodes and get the one closest to the size we want. - TBNode *size_node = nullptr; - for (TBNode *n = m_node.getFirstChild(); n != nullptr; n = n->getNext()) { - if (SDL_strcmp(n->getName(), "size") == 0) { - if ((size_node == nullptr) || - Abs(m_size - n->getValue().getInt()) < Abs(m_size - size_node->getValue().getInt())) { - size_node = n; - } - } - } - if (size_node == nullptr) { - return false; - } - - // Metrics - m_metrics.ascent = size_node->getValueInt("ascent", 0); - m_metrics.descent = size_node->getValueInt("descent", 0); - m_metrics.height = m_metrics.ascent + m_metrics.descent; - - // Other data - m_advance_delta = size_node->getValueInt("advance_delta", 0); - m_space_advance = size_node->getValueInt("space_advance", 0); - m_x_ofs = size_node->getValueInt("x_ofs", 0); - - // Info - m_rgb = m_node.getValueInt("info>rgb", 0); - - // Get the path for the bitmap file. - TBTempBuffer bitmap_filename; - if (!bitmap_filename.appendPath(filename)) { - return false; - } - - // Append the bitmap filename for the given size. - bitmap_filename.appendString(size_node->getValueString("bitmap", "")); - - m_img = image::loadImage(bitmap_filename.getData(), false); - - return findGlyphs(); -} - -inline unsigned char getAlpha(uint32_t color) { - return (color & 0xff000000) >> 24; -} - -bool TBBFRenderer::findGlyphs() { - if (!m_img) { - return false; - } - - const char *glyph_str = m_node.getValueString("info>glyph_str", nullptr); - if (glyph_str == nullptr) { - return false; - } - - int glyph_str_len = SDL_strlen(glyph_str); - int i = 0; - int x = 0; - while (UCS4 uc = utf8::decode_next(glyph_str, &i, glyph_str_len)) { - if (GLYPH *glyph = findNext(uc, x)) { - m_glyph_table.add(uc, glyph); // OOM! - x = glyph->x + glyph->w + 1; - } else { - break; - } - } - return true; -} - -GLYPH *TBBFRenderer::findNext(UCS4 cp, int x) { - int width = m_img->width(); - int height = m_img->height(); - uint32_t *data32 = (uint32_t *)m_img->data(); - - if (x >= width) { - return nullptr; - } - - GLYPH *glyph = new GLYPH; - if (glyph == nullptr) { - return nullptr; - } - - glyph->x = -1; - glyph->w = -1; - - // Find the left edge of the glyph - for (int i = x; i < width && glyph->x == -1; i++) { - for (int j = 0; j < height; j++) { - if (getAlpha(data32[i + j * width]) != 0U) { - glyph->x = x = i; - break; - } - } - } - - // Find the right edge of the glyph - for (int i = x; i < width; i++) { - int j; - for (j = 0; j < height; j++) { - if (getAlpha(data32[i + j * width]) != 0U) { - break; - } - } - if (j == height) // The whole col was clear, so we found the edge - { - glyph->w = i - glyph->x; - break; - } - } - - if (glyph->x == -1 || glyph->w == -1) { - delete glyph; - return nullptr; - } - return glyph; -} - -TBFontFace *TBBFRenderer::create(TBFontManager *fontManager, const char *filename, const TBFontDescription &fontDesc) { - if (strstr(filename, ".tb.txt") == nullptr) { - return nullptr; - } - if (TBBFRenderer *fr = new TBBFRenderer()) { - if (fr->load(filename, (int)fontDesc.getSize())) { - if (TBFontFace *font = new TBFontFace(fontManager->getGlyphCache(), fr, fontDesc)) { - return font; - } - } - delete fr; - } - return nullptr; -} - -void register_tbbf_font_renderer() { - if (TBBFRenderer *fr = new TBBFRenderer) { - g_font_manager->addRenderer(fr); - } -} diff --git a/src/modules/ui/turbobadger/tb/tb_geometry.cpp b/src/modules/ui/turbobadger/tb/tb_geometry.cpp deleted file mode 100644 index fbca41f38..000000000 --- a/src/modules/ui/turbobadger/tb/tb_geometry.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/** - * @file - */ - -#include "tb_geometry.h" -#include "core/Assert.h" - -namespace tb { - -bool TBRect::intersects(const TBRect &rect) const { - if (isEmpty() || rect.isEmpty()) { - return false; - } - return x + w > rect.x && x < rect.x + rect.w && y + h > rect.y && y < rect.y + rect.h; -} - -TBRect TBRect::moveIn(const TBRect &boundingRect) const { - return TBRect(ClampClipMax(x, boundingRect.x, boundingRect.x + boundingRect.w - w), - ClampClipMax(y, boundingRect.y, boundingRect.y + boundingRect.h - h), w, h); -} - -TBRect TBRect::centerIn(const TBRect &boundingRect) const { - return TBRect(boundingRect.x + (boundingRect.w - w) / 2, boundingRect.y + (boundingRect.h - h) / 2, w, h); -} - -TBRect TBRect::join(const TBRect &rect) const { - core_assert(!isInsideOut()); - core_assert(!rect.isInsideOut()); - - if (isEmpty()) { - return rect; - } - if (rect.isEmpty()) { - return *this; - } - - int minx = Min(x, rect.x); - int miny = Min(y, rect.y); - int maxx = x + w > rect.x + rect.w ? x + w : rect.x + rect.w; - int maxy = y + h > rect.y + rect.h ? y + h : rect.y + rect.h; - return TBRect(minx, miny, maxx - minx, maxy - miny); -} - -TBRect TBRect::clip(const TBRect &clipRect) const { - core_assert(!clipRect.isInsideOut()); - TBRect tmp; - if (!intersects(clipRect)) { - return tmp; - } - tmp.x = Max(x, clipRect.x); - tmp.y = Max(y, clipRect.y); - tmp.w = Min(x + w, clipRect.x + clipRect.w) - tmp.x; - tmp.h = Min(y + h, clipRect.y + clipRect.h) - tmp.y; - return tmp; -} - -// == TBRegion ========================================================================== - -TBRegion::TBRegion() : m_rects(nullptr), m_num_rects(0), m_capacity(0) { -} - -TBRegion::~TBRegion() { - removeAll(true); -} - -void TBRegion::removeRect(int index) { - core_assert(index >= 0 && index < m_num_rects); - for (int i = index; i < m_num_rects - 1; i++) { - m_rects[i] = m_rects[i + 1]; - } - m_num_rects--; -} - -void TBRegion::removeRectFast(int index) { - core_assert(index >= 0 && index < m_num_rects); - m_rects[index] = m_rects[--m_num_rects]; -} - -void TBRegion::removeAll(bool freeMemory) { - m_num_rects = 0; - if (freeMemory) { - delete[] m_rects; - m_rects = nullptr; - m_capacity = 0; - } -} - -bool TBRegion::set(const TBRect &rect) { - removeAll(); - return addRect(rect, false); -} - -bool TBRegion::growIfNeeded() { - if (m_num_rects == m_capacity) { - int new_m_capacity = Clamp(4, m_capacity * 2, 1024); - TBRect *new_rects = new TBRect[new_m_capacity]; - if (new_rects == nullptr) { - return false; - } - if (m_rects != nullptr) { - memmove(new_rects, m_rects, sizeof(TBRect) * m_capacity); - } - delete[] m_rects; - m_rects = new_rects; - m_capacity = new_m_capacity; - } - return true; -} - -bool TBRegion::addRect(const TBRect &rect, bool coalesce) { - if (coalesce) { - // If the rect can coalesce with any existing rect, - // just replace it with the union of both, doing coalesce - // check again recursively. - // Searching backwards is most likely to give a hit quicker - // in many usage scenarios. - for (int i = m_num_rects - 1; i >= 0; i--) { - if ( // Can coalesce vertically - (rect.x == m_rects[i].x && rect.w == m_rects[i].w && - (rect.y == m_rects[i].y + m_rects[i].h || - rect.y + rect.h == m_rects[i].y)) || // Can coalesce horizontally - (rect.y == m_rects[i].y && rect.h == m_rects[i].h && - (rect.x == m_rects[i].x + m_rects[i].w || rect.x + rect.w == m_rects[i].x))) { - TBRect union_rect = m_rects[i].join(rect); - removeRectFast(i); - return addRect(union_rect, true); - } - } - } - - if (!growIfNeeded()) { - return false; - } - m_rects[m_num_rects++] = rect; - return true; -} - -bool TBRegion::includeRect(const TBRect &rect) { - for (int i = 0; i < m_num_rects; i++) { - if (rect.intersects(m_rects[i])) { - // Make a region containing the non intersecting parts and then include - // those recursively (they might still intersect some other part of the region). - TBRegion inclusion_region; - if (!inclusion_region.addExcludingRects(rect, m_rects[i], false)) { - return false; - } - for (int j = 0; j < inclusion_region.m_num_rects; j++) { - if (!includeRect(inclusion_region.m_rects[j])) { - return false; - } - } - return true; - } - } - // Now we know that the rect can be added without overlap. - // Add it with coalesce checking to keep the number of rects down. - return addRect(rect, true); -} - -bool TBRegion::excludeRect(const TBRect &excludeRect) { - int num_rects_to_check = m_num_rects; - for (int i = 0; i < num_rects_to_check; i++) { - if (m_rects[i].intersects(excludeRect)) { - // Remove the existing rectangle we found we intersect - // and add the pieces we don't intersect. New rects - // will be added at the end of the list, so we can decrease - // num_rects_to_check. - TBRect rect = m_rects[i]; - removeRect(i); - num_rects_to_check--; - i--; - - if (!addExcludingRects(rect, excludeRect, true)) { - return false; - } - } - } - return true; -} - -bool TBRegion::addExcludingRects(const TBRect &rect, const TBRect &excludeRect, bool coalesce) { - core_assert(rect.intersects(excludeRect)); - TBRect remove = excludeRect.clip(rect); - - if (remove.y > rect.y) { - if (!addRect(TBRect(rect.x, rect.y, rect.w, remove.y - rect.y), coalesce)) { - return false; - } - } - if (remove.x > rect.x) { - if (!addRect(TBRect(rect.x, remove.y, remove.x - rect.x, remove.h), coalesce)) { - return false; - } - } - if (remove.x + remove.w < rect.x + rect.w) { - if (!addRect(TBRect(remove.x + remove.w, remove.y, rect.x + rect.w - (remove.x + remove.w), remove.h), - coalesce)) { - return false; - } - } - if (remove.y + remove.h < rect.y + rect.h) { - if (!addRect(TBRect(rect.x, remove.y + remove.h, rect.w, rect.y + rect.h - (remove.y + remove.h)), coalesce)) { - return false; - } - } - return true; -} - -const TBRect &TBRegion::getRect(int index) const { - core_assert(index >= 0 && index < m_num_rects); - return m_rects[index]; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_geometry.h b/src/modules/ui/turbobadger/tb/tb_geometry.h deleted file mode 100644 index 9231ca6aa..000000000 --- a/src/modules/ui/turbobadger/tb/tb_geometry.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" - -namespace tb { - -/** Simple point class. */ - -class TBPoint { -public: - int x, y; - TBPoint() : x(0), y(0) { - } - TBPoint(int x, int y) : x(x), y(y) { - } -}; - -/** Simple rectangle class. */ - -class TBRect { -public: - int x, y, w, h; - TBRect() : x(0), y(0), w(0), h(0) { - } - TBRect(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) { - } - - inline bool isEmpty() const { - return w <= 0 || h <= 0; - } - inline bool isInsideOut() const { - return w < 0 || h < 0; - } - inline bool equals(const TBRect &rect) const { - return rect.x == x && rect.y == y && rect.w == w && rect.h == h; - } - bool intersects(const TBRect &rect) const; - bool contains(const TBPoint &p) const { - return p.x >= x && p.y >= y && p.x < x + w && p.y < y + h; - } - - inline void reset() { - x = y = w = h = 0; - } - inline void set(int x, int y, int w, int h) { - this->x = x; - this->y = y; - this->w = w; - this->h = h; - } - - inline TBRect shrink(int left, int top, int right, int bottom) const { - return TBRect(x + left, y + top, w - left - right, h - top - bottom); - } - inline TBRect expand(int left, int top, int right, int bottom) const { - return shrink(-left, -top, -right, -bottom); - } - inline TBRect shrink(int tx, int ty) const { - return TBRect(x + tx, y + ty, w - tx * 2, h - ty * 2); - } - inline TBRect expand(int tx, int ty) const { - return shrink(-tx, -ty); - } - inline TBRect offset(int dx, int dy) const { - return TBRect(x + dx, y + dy, w, h); - } - - /** Return a rect moved inside bounding_rect. If the rect doesn't fit inside - bounding_rect, it will be placed so the x and/or y matches bounding_rect. */ - TBRect moveIn(const TBRect &bounding_rect) const; - - /** Return a rect centered in bounding_rect. */ - TBRect centerIn(const TBRect &bounding_rect) const; - - TBRect join(const TBRect &rect) const; - TBRect clip(const TBRect &clip_rect) const; -}; - -/** TBRegion does calculations on regions represented by a list of rectangles. */ - -class TBRegion { -public: - TBRegion(); - ~TBRegion(); - - /** Remove the rect at the given index. */ - void removeRect(int index); - - /** Remove the rect at the given index. - This method will change the order of rectangles after index. */ - void removeRectFast(int index); - - /** Remove all rectangles so the region becomes empty. - If free_memory is false, the internal buffers will be reused - if more rectangles are added again under its life time. */ - void removeAll(bool free_memory = true); - - /** Set the region to the given rect. */ - bool set(const TBRect &rect); - - /** Add the rect without doing any overlap check. - If coalesce is true, it will coalesce the rectangle - with existing rectangles if possible (until there's - nothing more to coalesce it with). */ - bool addRect(const TBRect &rect, bool coalesce); - - /** Include the rect in the region. - This will add only the parts that's not already in the region so the result doesn't - contain overlap parts. This assumes there's no overlap in the region already! */ - bool includeRect(const TBRect &includeRect); - - /** Exclude the rect from the region. */ - bool excludeRect(const TBRect &exclude_rect); - - /** Add the rectangles that's left of rect after excluding exclude_rect. */ - bool addExcludingRects(const TBRect &rect, const TBRect &exclude_rect, bool coalesce); - - bool isEmpty() const { - return m_num_rects == 0; - } - int getNumRects() const { - return m_num_rects; - } - const TBRect &getRect(int index) const; - -private: - TBRect *m_rects; - int m_num_rects; - int m_capacity; - bool growIfNeeded(); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_hash.h b/src/modules/ui/turbobadger/tb/tb_hash.h deleted file mode 100644 index a9db43e7e..000000000 --- a/src/modules/ui/turbobadger/tb/tb_hash.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_types.h" - -namespace tb { - -// On C++ compilers that support it, use constexpr for hash so that -// TBID comparisons turn into simple uint32 comparisons compile time. - -// FNV constants -static constexpr uint32_t basis = 2166136261U; -static constexpr uint32_t prime = 16777619U; - -// compile-time hash helper function -constexpr uint32_t TBGetHash_one(char c, const char *remain, uint32_t value) { - return c == 0 ? value : TBGetHash_one(remain[0], remain + 1, (value ^ c) * prime); -} - -// compile-time hash -constexpr uint32_t TBGetHash(const char *str) { - return ((str != nullptr) && (*str != 0)) ? TBGetHash_one(str[0], str + 1, basis) : 0; -} - -#define TBIDC(str) tb::TBGetHash(str) - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_hashtable.cpp b/src/modules/ui/turbobadger/tb/tb_hashtable.cpp deleted file mode 100644 index 5c3baaaea..000000000 --- a/src/modules/ui/turbobadger/tb/tb_hashtable.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * @file - */ - -#include "tb_hashtable.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" - -namespace tb { - -// FIX: reduce memory (block allocation of ITEM) -// FIX: should shrink when deleting single items (but not when adding items!) -// FIX: should grow when about 70% full instead of 100% - -TBHashTable::TBHashTable() : m_buckets(nullptr), m_num_buckets(0), m_num_items(0) { -} - -TBHashTable::~TBHashTable() { - removeAll(); -} - -void TBHashTable::removeAll(bool delContent) { - for (uint32_t i = 0; i < m_num_buckets; i++) { - ITEM *item = m_buckets[i]; - while (item != nullptr) { - ITEM *item_next = item->next; - if (delContent) { - deleteContent(item->content); - } - delete item; - item = item_next; - } - } - delete[] m_buckets; - m_buckets = nullptr; - m_num_buckets = m_num_items = 0; -} - -bool TBHashTable::rehash(uint32_t newNumBuckets) { - if (newNumBuckets == m_num_buckets) { - return true; - } - if (ITEM **new_buckets = new ITEM *[newNumBuckets]) { - SDL_memset(new_buckets, 0, sizeof(ITEM *) * newNumBuckets); - // Rehash all items into the new buckets - for (uint32_t i = 0; i < m_num_buckets; i++) { - ITEM *item = m_buckets[i]; - while (item != nullptr) { - ITEM *item_next = item->next; - // Add it to new_buckets - uint32_t bucket = item->key & (newNumBuckets - 1); - item->next = new_buckets[bucket]; - new_buckets[bucket] = item; - item = item_next; - } - } - // Delete old buckets and update - delete[] m_buckets; - m_buckets = new_buckets; - m_num_buckets = newNumBuckets; - return true; - } - return false; -} - -uint32_t TBHashTable::getSuitableBucketsCount() const { - // As long as we use FNV for TBID (in TBGetHash), power of two hash sizes are the best. - if (m_num_items == 0U) { - return 16; - } - return m_num_items * 2; -} - -void *TBHashTable::get(uint32_t key) const { - if (m_num_buckets == 0U) { - return nullptr; - } - uint32_t bucket = key & (m_num_buckets - 1); - ITEM *item = m_buckets[bucket]; - while (item != nullptr) { - if (item->key == key) { - return item->content; - } - item = item->next; - } - return nullptr; -} - -bool TBHashTable::add(uint32_t key, void *content) { - if (needRehash() && !rehash(getSuitableBucketsCount())) { - return false; - } - core_assert(!get(key)); - if (ITEM *item = new ITEM) { - uint32_t bucket = key & (m_num_buckets - 1); - item->key = key; - item->content = content; - item->next = m_buckets[bucket]; - m_buckets[bucket] = item; - m_num_items++; - return true; - } - return false; -} - -void *TBHashTable::remove(uint32_t key) { - if (m_num_buckets == 0U) { - return nullptr; - } - uint32_t bucket = key & (m_num_buckets - 1); - ITEM *item = m_buckets[bucket]; - ITEM *prev_item = nullptr; - while (item != nullptr) { - if (item->key == key) { - if (prev_item != nullptr) { - prev_item->next = item->next; - } else { - m_buckets[bucket] = item->next; - } - m_num_items--; - void *content = item->content; - delete item; - return content; - } - prev_item = item; - item = item->next; - } - core_assert(!"This hash table didn't contain the given key!"); - return nullptr; -} - -#ifdef TB_RUNTIME_DEBUG_INFO - -void TBHashTable::debug() { - Log::debug("Hash table: "); - int total_count = 0; - for (uint32_t i = 0; i < m_num_buckets; i++) { - int count = 0; - ITEM *item = m_buckets[i]; - while (item != nullptr) { - count++; - item = item->next; - } - Log::debug("%d ", count); - total_count += count; - } - Log::debug(" (total: %d of %d buckets)", total_count, m_num_buckets); -} - -#endif // TB_RUNTIME_DEBUG_INFO - -TBHashTableIterator::TBHashTableIterator(TBHashTable *hashTable) - : m_hash_table(hashTable), m_current_bucket(0), m_current_item(nullptr) { -} - -void *TBHashTableIterator::getNextContent() { - if (m_current_bucket == m_hash_table->m_num_buckets) { - return nullptr; - } - if ((m_current_item != nullptr) && (m_current_item->next != nullptr)) { - m_current_item = m_current_item->next; - } else { - if (m_current_item != nullptr) { - m_current_bucket++; - } - if (m_current_bucket == m_hash_table->m_num_buckets) { - return nullptr; - } - while (m_current_bucket < m_hash_table->m_num_buckets) { - m_current_item = m_hash_table->m_buckets[m_current_bucket]; - if (m_current_item != nullptr) { - break; - } - m_current_bucket++; - } - } - return m_current_item != nullptr ? m_current_item->content : nullptr; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_hashtable.h b/src/modules/ui/turbobadger/tb/tb_hashtable.h deleted file mode 100644 index ccfcf3e12..000000000 --- a/src/modules/ui/turbobadger/tb/tb_hashtable.h +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/Assert.h" -#include "tb_core.h" - -namespace tb { - -/** TBHashTable is a minimal hash table, for hashing anything using a uint32 key. */ - -class TBHashTable { -public: - TBHashTable(); - virtual ~TBHashTable(); - - /** Remove all items without deleting the content. */ - inline void removeAll() { - removeAll(false); - } - - /** Remove all items and delete the content. - This requires TBHashTable to be subclassed and implementing DeleteContent. - You would typically do this by using TBHashTableOf or TBHashTableAutoDeleteOf. */ - inline void deleteAll() { - removeAll(true); - } - - /** Get the content for the given key, or nullptr if not found. */ - void *get(uint32_t key) const; - - /** Add content with the given key. - Returns false if out of memory. */ - bool add(uint32_t key, void *content); - - /** Remove the content with the given key. */ - void *remove(uint32_t key); - - /** Delete the content with the given key. */ - inline void deleteKey(uint32_t key) { - deleteContent(remove(key)); - } - - /** Rehash the table so use the given number of buckets. - Returns false if out of memory. */ - bool rehash(uint32_t num_buckets); - - /** Return true if the hashtable itself think it's time to rehash. */ - inline bool needRehash() const { - // Grow if more items than buckets - return (m_num_buckets == 0U) || m_num_items >= m_num_buckets; - } - - /** Get the number of buckets the hashtable itself thinks is suitable for - the current number of items. */ - uint32_t getSuitableBucketsCount() const; - - /** Get the number of items in the hash table. */ - inline uint32_t getNumItems() const { - return m_num_items; - } - -#ifdef TB_RUNTIME_DEBUG_INFO - /** Print out some debug info about the hash table. */ - void debug(); -#endif - -protected: - /** Delete the content of a item. This is called if calling deleteAll(), and must be - implemented in a subclass that knows about the content type. */ - virtual void deleteContent(void *content) { - core_assert(!"You need to subclass and implement!"); - } - -private: - friend class TBHashTableIterator; - void removeAll(bool delete_content); - struct ITEM { - uint32_t key; - ITEM *next; - void *content; - } * *m_buckets; - uint32_t m_num_buckets; - uint32_t m_num_items; -}; - -/** TBHashTableIterator is a iterator for stepping through all content stored in a TBHashTable. */ -// FIX: make it safe in case the current item is removed from the hashtable -class TBHashTableIterator { -public: - TBHashTableIterator(TBHashTable *hash_table); - void *getNextContent(); - -private: - TBHashTable *m_hash_table; - uint32_t m_current_bucket; - TBHashTable::ITEM *m_current_item; -}; - -/** TBHashTableIteratorOf is a TBHashTableIterator which auto cast to the class type. */ -template class TBHashTableIteratorOf : private TBHashTableIterator { -public: - TBHashTableIteratorOf(TBHashTable *hashTable) : TBHashTableIterator(hashTable) { - } - inline T *getNextContent() { - return (T *)TBHashTableIterator::getNextContent(); - } -}; - -/** TBHashTableOf is a TBHashTable with the given class type as content. */ -template class TBHashTableOf : public TBHashTable { - // FIX: Don't do public inheritance! Either inherit privately and forward, or use a private member backend! -public: - inline T *get(uint32_t key) const { - return (T *)TBHashTable::get(key); - } - inline T *remove(uint32_t key) { - return (T *)TBHashTable::remove(key); - } - -protected: - virtual void deleteContent(void *content) { - delete (T *)content; - } -}; - -/** TBHashTableOf is a TBHashTable with the given class type as content. - It will delete all content automaticallt on destruction. */ -template class TBHashTableAutoDeleteOf : public TBHashTable { -public: - ~TBHashTableAutoDeleteOf() { - deleteAll(); - } - - inline T *get(uint32_t key) const { - return (T *)TBHashTable::get(key); - } - -protected: - virtual void deleteContent(void *content) { - delete (T *)content; - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_id.h b/src/modules/ui/turbobadger/tb/tb_id.h deleted file mode 100644 index 404cf7cf8..000000000 --- a/src/modules/ui/turbobadger/tb/tb_id.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_hash.h" -#include "tb_str.h" -#include "tb_types.h" - -namespace tb { - -/** TBID is a wrapper for a uint32 to be used as ID. - The uint32 can be set directly to any uint32, or it can be - set from a string which will be hashed into the uint32. */ -class TBID { -public: - constexpr TBID(uint32_t newid = 0) : id(newid) { - } - constexpr TBID(const char *string) : id(TBGetHash(string)) { - } - constexpr TBID(const TBID &newid) : id(newid.id) { - } - void set(uint32_t newid) { - id = newid; - } - void set(const TBID &newid) { - id = newid; - } - void set(const char *string) { - id = TBGetHash(string); - } - - operator uint32_t() const { - return id; - } - const TBID &operator=(const TBID &newid) { - set(newid); - return *this; - } - -private: - uint32_t id; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_inline_select.cpp b/src/modules/ui/turbobadger/tb/tb_inline_select.cpp deleted file mode 100644 index 8b083dd82..000000000 --- a/src/modules/ui/turbobadger/tb/tb_inline_select.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file - */ - -#include "tb_inline_select.h" -#include "core/Assert.h" -#include "core/StringUtil.h" - -namespace tb { - -// FIX: axis should affect the buttons arrow skin! -// FIX: unfocus should set the correct text! - -TBInlineSelectBase::TBInlineSelectBase() { - setSkinBg(TBIDC("TBInlineSelect")); - addChild(&m_layout); - m_layout.addChild(&m_buttons[0]); - m_layout.addChild(&m_editfield); - m_layout.addChild(&m_buttons[1]); - m_layout.setRect(getPaddingRect()); - m_layout.setGravity(WIDGET_GRAVITY_ALL); - m_layout.setSpacing(0); - m_buttons[0].setSkinBg(TBIDC("TBButton.flat")); - m_buttons[1].setSkinBg(TBIDC("TBButton.flat")); - m_buttons[0].getContentRoot()->addChild(new TBSkinImage(TBIDC("arrow.left"))); - m_buttons[1].getContentRoot()->addChild(new TBSkinImage(TBIDC("arrow.right"))); - m_buttons[0].setIsFocusable(false); - m_buttons[1].setIsFocusable(false); - m_buttons[0].setID(TBIDC("dec")); - m_buttons[1].setID(TBIDC("inc")); - m_buttons[0].setAutoRepeat(true); - m_buttons[1].setAutoRepeat(true); - m_editfield.setTextAlign(TB_TEXT_ALIGN_RIGHT); - m_editfield.setEditType(EDIT_TYPE_NUMBER); - m_editfield.setText("0"); -} - -TBInlineSelectBase::~TBInlineSelectBase() { - m_layout.removeChild(&m_buttons[1]); - m_layout.removeChild(&m_editfield); - m_layout.removeChild(&m_buttons[0]); - removeChild(&m_layout); -} - -void TBInlineSelectBase::onSkinChanged() { - m_layout.setRect(getPaddingRect()); -} - -void TBInlineSelect::setLimits(int min, int max) { - core_assert(min <= max); - m_min = min; - m_max = max; - setValue(m_value); -} - -void TBInlineSelect::onProcess() { - Super::onProcess(); - if (!_var || !_var->isDirty()) { - return; - } - setValue(_var->intVal()); -} - -void TBInlineSelect::setValueInternal(int value, bool updateText) { - value = Clamp(value, m_min, m_max); - if (value == m_value) { - return; - } - m_value = value; - if (_var) { - _var->setVal(value); - _var->markClean(); - } - - if (updateText) { - const core::String& strval = core::string::format("%d", m_value); - m_editfield.setText(strval); - } - - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); - - // Warning: Do nothing here since the event might have deleted us. - // If needed, check if we are alive using a safe pointer first. -} - -bool TBInlineSelect::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_KEY_DOWN) { - if (ev.special_key == TB_KEY_UP || ev.special_key == TB_KEY_DOWN) { - int dv = ev.special_key == TB_KEY_UP ? m_delta : -m_delta; - setValue(getValue() + dv); - return true; - } - } else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("dec")) { - setValue(getValue() - m_delta); - return true; - } else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("inc")) { - setValue(getValue() + m_delta); - return true; - } else if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_editfield) { - core::String text; - m_editfield.getText(text); - setValueInternal(core::string::toInt(text), false); - } - return false; -} - - -void TBInlineSelectDouble::setLimits(double min, double max) { - core_assert(min <= max); - m_min = min; - m_max = max; - setValueDouble(m_value); -} - -void TBInlineSelectDouble::onProcess() { - Super::onProcess(); - if (!_var || !_var->isDirty()) { - return; - } - setValueDouble(_var->floatVal()); -} - -void TBInlineSelectDouble::setValueInternal(double value, bool updateText) { - value = Clamp(value, m_min, m_max); - if (value == m_value) { - return; - } - m_value = value; - if (_var) { - _var->setVal((float)value); - _var->markClean(); - } - - if (updateText) { - const core::String& strval = core::string::format("%.3f", (float)m_value); - m_editfield.setText(strval); - } - - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); - - // Warning: Do nothing here since the event might have deleted us. - // If needed, check if we are alive using a safe pointer first. -} - -bool TBInlineSelectDouble::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_KEY_DOWN) { - if (ev.special_key == TB_KEY_UP || ev.special_key == TB_KEY_DOWN) { - double dv = ev.special_key == TB_KEY_UP ? m_delta : -m_delta; - setValueDouble(getValueDouble() + dv); - return true; - } - } else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("dec")) { - setValueDouble(getValueDouble() - m_delta); - return true; - } else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("inc")) { - setValueDouble(getValueDouble() + m_delta); - return true; - } else if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_editfield) { - core::String text; - m_editfield.getText(text); - setValueInternal(SDL_atof(text.c_str()), false); - } - return false; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_inline_select.h b/src/modules/ui/turbobadger/tb/tb_inline_select.h deleted file mode 100644 index a5ac9e809..000000000 --- a/src/modules/ui/turbobadger/tb/tb_inline_select.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_editfield.h" -#include "tb_select_item.h" -#include "tb_widgets_listener.h" - -namespace tb { - -/** TBSelectList is a select widget with no popups. Instead it has two - arrow buttons that cycle between the choices. - By default it is a number widget. - - FIX: Should also be possible to set a list of strings that will be - shown instead of numbers. -*/ -class TBInlineSelectBase : public TBWidget { -public: - TBOBJECT_SUBCLASS(TBInlineSelectBase, TBWidget); - - TBInlineSelectBase(); - virtual ~TBInlineSelectBase(); - - /** Set along which axis the content should layouted. */ - virtual void setAxis(AXIS axis) override { - m_layout.setAxis(axis); - } - virtual AXIS getAxis() const override { - return m_layout.getAxis(); - } - - virtual void onSkinChanged() override; - virtual void onInflate(const INFLATE_INFO &info) override; - -protected: - TBButton m_buttons[2]; - TBLayout m_layout; - TBEditField m_editfield; - - core::VarPtr _var; - core::String _command; -}; - -class TBInlineSelect : public TBInlineSelectBase { -public: - TBOBJECT_SUBCLASS(TBInlineSelect, TBInlineSelectBase); - - void setLimits(int min, int max); - int getMinValue() const { - return m_min; - } - int getMaxValue() const { - return m_max; - } - - virtual void setValue(int value) override { - setValueInternal(value, true); - } - virtual int getValue() const override { - return m_value; - } - - virtual void onProcess() override; - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - -protected: - int m_value = 0; - int m_min = 0, m_max = 100; - int m_delta = 1; - void setValueInternal(int value, bool update_text); -}; - -class TBInlineSelectDouble : public TBInlineSelectBase { -public: - TBOBJECT_SUBCLASS(TBInlineSelectDouble, TBInlineSelectBase); - - void setLimits(double min, double max); - double getMinValue() const { - return m_min; - } - double getMaxValue() const { - return m_max; - } - - virtual void setValueDouble(double value) override { - setValueInternal(value, true); - } - virtual double getValueDouble() const override { - return m_value; - } - - virtual void onProcess() override; - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - -protected: - double m_value = 0.0; - double m_min = 0.0, m_max = 100.0; - double m_delta = 1.0; - void setValueInternal(double value, bool update_text); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_language.cpp b/src/modules/ui/turbobadger/tb/tb_language.cpp deleted file mode 100644 index d38c4cdcb..000000000 --- a/src/modules/ui/turbobadger/tb/tb_language.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - */ - -#include "tb_language.h" -#include "tb_node_tree.h" -#include "tb_system.h" - -namespace tb { - -TBLanguage::~TBLanguage() { - clear(); -} - -bool TBLanguage::load(const char *filename) { - // Read the file into a node tree (even though it's only a flat list) - TBNode node; - if (!node.readFile(filename)) { - return false; - } - - // Go through all nodes and add to the strings hash table - TBNode *n = node.getFirstChild(); - while (n != nullptr) { - const char *str = n->getValue().getString(); - core::String *new_str = new core::String(str); - if ((new_str == nullptr) || !strings.add(TBID(n->getName()), new_str)) { - delete new_str; - return false; - } - n = n->getNext(); - } - return true; -} - -void TBLanguage::clear() { - strings.deleteAll(); -} - -const char *TBLanguage::getString(const TBID &id) { - if (core::String *str = strings.get(id)) { - return str->c_str(); - } - return ""; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_language.h b/src/modules/ui/turbobadger/tb/tb_language.h deleted file mode 100644 index ce47d1f11..000000000 --- a/src/modules/ui/turbobadger/tb/tb_language.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_hashtable.h" -#include "tb_id.h" - -namespace tb { - -/** TBLanguage is a basic language string manager. - Strings read into it can be looked up from a TBID, so either by a number - or the hash number from a string (done by TBID). - - Ex: GetString(10) (Get the string with id 10) - Ex: GetString("new") (Get the string with id new) - - In UI resources, you can refer to strings from language lookup by preceding a string with @. - - Ex: TBButton: text: @close (Create a button with text from lookup of "close") -*/ - -class TBLanguage { -public: - ~TBLanguage(); - - /** Load a file into this language manager. - Note: This *adds* strings read from the file, without clearing any existing - strings first. */ - bool load(const char *filename); - - /** Clear the list of strings. */ - void clear(); - - /** Return the string with the given id. - If there is no string with that id, "" will be returned - in release builds, and "" (populated with the id) will - be returned in debug builds. */ - const char *getString(const TBID &id); - -private: - TBHashTableOf strings; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_layout.cpp b/src/modules/ui/turbobadger/tb/tb_layout.cpp deleted file mode 100644 index 82d8c918b..000000000 --- a/src/modules/ui/turbobadger/tb/tb_layout.cpp +++ /dev/null @@ -1,492 +0,0 @@ -/** - * @file - */ - -#include "tb_layout.h" -#include "core/Assert.h" -#include "tb_skin_util.h" -#include "tb_system.h" - -namespace tb { - -TBLayout::TBLayout(AXIS axis) - : m_axis(axis), m_spacing(SPACING_FROM_SKIN), m_overflow(0), m_overflow_scroll(0), m_packed_init(0) { - m_packed.layout_mode_size = LAYOUT_SIZE_GRAVITY; - m_packed.layout_mode_pos = LAYOUT_POSITION_CENTER; - m_packed.layout_mode_overflow = LAYOUT_OVERFLOW_CLIP; - m_packed.layout_mode_dist = LAYOUT_DISTRIBUTION_PREFERRED; - m_packed.layout_mode_dist_pos = LAYOUT_DISTRIBUTION_POSITION_CENTER; - m_packed.paint_overflow_fadeout = 1; -} - -void TBLayout::setAxis(AXIS axis) { - if (axis == m_axis) { - return; - } - m_axis = axis; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - invalidateSkinStates(); -} - -void TBLayout::setSpacing(int spacing) { - if (spacing == m_spacing) { - return; - } - m_spacing = spacing; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); -} - -void TBLayout::setOverflowScroll(int overflowScroll) { - overflowScroll = Min(overflowScroll, m_overflow); - overflowScroll = Max(overflowScroll, 0); - if (overflowScroll == m_overflow_scroll) { - return; - } - m_overflow_scroll = overflowScroll; - invalidate(); - if (m_axis == AXIS_X) { - onScroll(m_overflow_scroll, 0); - } else { - onScroll(0, m_overflow_scroll); - } -} - -void TBLayout::setLayoutSize(LAYOUT_SIZE size) { - if (size == m_packed.layout_mode_size) { - return; - } - m_packed.layout_mode_size = size; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBLayout::setLayoutPosition(LAYOUT_POSITION pos) { - if (pos == m_packed.layout_mode_pos) { - return; - } - m_packed.layout_mode_pos = pos; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBLayout::setLayoutOverflow(LAYOUT_OVERFLOW overflow) { - if (overflow == m_packed.layout_mode_overflow) { - return; - } - m_packed.layout_mode_overflow = overflow; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBLayout::setLayoutDistribution(LAYOUT_DISTRIBUTION distribution) { - if (distribution == m_packed.layout_mode_dist) { - return; - } - m_packed.layout_mode_dist = distribution; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBLayout::setLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION distributionPos) { - if (distributionPos == m_packed.layout_mode_dist_pos) { - return; - } - m_packed.layout_mode_dist_pos = distributionPos; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBLayout::setLayoutOrder(LAYOUT_ORDER order) { - bool reversed = (order == LAYOUT_ORDER_TOP_TO_BOTTOM); - if (static_cast(reversed) == m_packed.mode_reverse_order) { - return; - } - m_packed.mode_reverse_order = reversed; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBLayout::invalidateLayout(INVALIDATE_LAYOUT il) { - m_packed.layout_is_invalid = 1; - // Continue invalidating parents (depending on il) - TBWidget::invalidateLayout(il); -} - -PreferredSize RotPreferredSize(const PreferredSize &ps, AXIS axis) { - if (axis == AXIS_X) { - return ps; - } - PreferredSize psr; - psr.max_w = ps.max_h; - psr.max_h = ps.max_w; - psr.min_w = ps.min_h; - psr.min_h = ps.min_w; - psr.pref_w = ps.pref_h; - psr.pref_h = ps.pref_w; - psr.size_dependency = - ((ps.size_dependency & SIZE_DEP_WIDTH_DEPEND_ON_HEIGHT) != 0U ? SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH - : SIZE_DEP_NONE) | - ((ps.size_dependency & SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH) != 0U ? SIZE_DEP_WIDTH_DEPEND_ON_HEIGHT - : SIZE_DEP_NONE); - return psr; -} - -SizeConstraints RotSizeConstraints(const SizeConstraints &sc, AXIS axis) { - return axis == AXIS_X ? sc : SizeConstraints(sc.available_h, sc.available_w); -} - -TBRect RotRect(const TBRect &rect, AXIS axis) { - if (axis == AXIS_X) { - return rect; - } - return TBRect(rect.y, rect.x, rect.h, rect.w); -} - -WIDGET_GRAVITY RotGravity(WIDGET_GRAVITY gravity, AXIS axis) { - if (axis == AXIS_X) { - return gravity; - } - WIDGET_GRAVITY r = WIDGET_GRAVITY_NONE; - r |= (gravity & WIDGET_GRAVITY_LEFT) != 0U ? WIDGET_GRAVITY_TOP : WIDGET_GRAVITY_NONE; - r |= (gravity & WIDGET_GRAVITY_TOP) != 0U ? WIDGET_GRAVITY_LEFT : WIDGET_GRAVITY_NONE; - r |= (gravity & WIDGET_GRAVITY_RIGHT) != 0U ? WIDGET_GRAVITY_BOTTOM : WIDGET_GRAVITY_NONE; - r |= (gravity & WIDGET_GRAVITY_BOTTOM) != 0U ? WIDGET_GRAVITY_RIGHT : WIDGET_GRAVITY_NONE; - return r; -} - -bool TBLayout::qualifyForExpansion(WIDGET_GRAVITY gravity) const { - if (m_packed.layout_mode_dist == LAYOUT_DISTRIBUTION_AVAILABLE) { - return true; - } - if (m_packed.layout_mode_dist == LAYOUT_DISTRIBUTION_GRAVITY && - (((gravity & WIDGET_GRAVITY_LEFT) != 0U) && ((gravity & WIDGET_GRAVITY_RIGHT) != 0U))) { - return true; - } - return false; -} - -int TBLayout::getWantedHeight(WIDGET_GRAVITY gravity, const PreferredSize &ps, int availableHeight) const { - int height = 0; - switch (m_packed.layout_mode_size) { - case LAYOUT_SIZE_GRAVITY: - height = (((gravity & WIDGET_GRAVITY_TOP) != 0U) && ((gravity & WIDGET_GRAVITY_BOTTOM) != 0U)) - ? availableHeight - : Min(availableHeight, ps.pref_h); - break; - case LAYOUT_SIZE_PREFERRED: - height = Min(availableHeight, ps.pref_h); - break; - case LAYOUT_SIZE_AVAILABLE: - height = Min(availableHeight, ps.max_h); - break; - } - height = Min(height, ps.max_h); - return height; -} - -TBWidget *TBLayout::getNextNonCollapsedWidget(TBWidget *child) const { - TBWidget *next = getNextInLayoutOrder(child); - while ((next != nullptr) && next->getVisibility() == WIDGET_VISIBILITY_GONE) { - next = getNextInLayoutOrder(next); - } - return next; -} - -int TBLayout::getTrailingSpace(TBWidget *child, int spacing) const { - if (spacing == 0) { - return 0; - } - if (getNextNonCollapsedWidget(child) == nullptr) { - return 0; - } - return spacing; -} - -int TBLayout::calculateSpacing() { - // Get spacing from skin, if not specified - int spacing = m_spacing; - if (spacing == SPACING_FROM_SKIN) { - if (TBSkinElement *e = getSkinBgElement()) { - spacing = e->spacing; - } - - core_assert(SPACING_FROM_SKIN == SKIN_VALUE_NOT_SPECIFIED); - if (spacing == SPACING_FROM_SKIN /*|| spacing == SKIN_VALUE_NOT_SPECIFIED*/) { - spacing = g_tb_skin->getDefaultSpacing(); - } - } - return spacing; -} - -TBWidget *TBLayout::getFirstInLayoutOrder() const { - return m_packed.mode_reverse_order ? getLastChild() : getFirstChild(); -} - -TBWidget *TBLayout::getNextInLayoutOrder(TBWidget *child) const { - return m_packed.mode_reverse_order ? child->getPrev() : child->getNext(); -} - -void TBLayout::validateLayout(const SizeConstraints &constraints, PreferredSize *calculatePs) { - // Layout notes: - // -All layout code is written for AXIS_X layout. - // Instead of duplicating the layout code for both AXIS_X and AXIS_Y, we simply - // rotate the in data (rect, gravity, preferred size) and the outdata (rect). - - if (calculatePs == nullptr) { - if (!m_packed.layout_is_invalid) { - return; - } - m_packed.layout_is_invalid = 0; - } else { - // Maximum size will grow below depending of the childrens maximum size - calculatePs->max_w = calculatePs->max_h = 0; - } - - const int spacing = calculateSpacing(); - const TBRect padding_rect = getPaddingRect(); - const TBRect layout_rect = RotRect(padding_rect, m_axis); - - const SizeConstraints inner_sc = - constraints.constrainByPadding(getRect().w - padding_rect.w, getRect().h - padding_rect.h); - - // Calculate totals for minimum and preferred width that we need for layout. - int total_preferred_w = 0; - int total_min_pref_diff_w = 0; - int total_max_pref_diff_w = 0; - for (TBWidget *child = getFirstInLayoutOrder(); child != nullptr; child = getNextInLayoutOrder(child)) { - if (child->getVisibility() == WIDGET_VISIBILITY_GONE) { - continue; - } - - const int ending_space = getTrailingSpace(child, spacing); - const PreferredSize ps = RotPreferredSize(child->getPreferredSize(inner_sc), m_axis); - const WIDGET_GRAVITY gravity = RotGravity(child->getGravity(), m_axis); - - total_preferred_w += ps.pref_w + ending_space; - total_min_pref_diff_w += ps.pref_w - ps.min_w; - - if (qualifyForExpansion(gravity)) { - int capped_max_w = Min(layout_rect.w, ps.max_w); - total_max_pref_diff_w += capped_max_w - ps.pref_w; - } - - if (calculatePs != nullptr) { - calculatePs->min_h = Max(calculatePs->min_h, ps.min_h); - calculatePs->pref_h = Max(calculatePs->pref_h, ps.pref_h); - calculatePs->min_w += ps.min_w + ending_space; - calculatePs->pref_w += ps.pref_w + ending_space; - calculatePs->max_w += ps.max_w + ending_space; - - // The widget height depends on layout and widget properties, so get what - // it would actually use if it was given max_h as available height. - // If we just used its max_h, that could increase the whole layout size - // even if the widget wouldn't actually use it. - int height = getWantedHeight(gravity, ps, ps.max_h); - calculatePs->max_h = Max(calculatePs->max_h, height); - - calculatePs->size_dependency |= ps.size_dependency; - } - } - - if (calculatePs != nullptr) { - // We just wanted to calculate preferred size, so return without layouting. - *calculatePs = RotPreferredSize(*calculatePs, m_axis); - return; - } - - TB_IF_DEBUG_SETTING(LAYOUT_PS_DEBUGGING, last_layout_time = TBSystem::getTimeMS()); - - // Pre Layout step (calculate distribution position) - int missing_space = Max(total_preferred_w - layout_rect.w, 0); - int extra_space = Max(layout_rect.w - total_preferred_w, 0); - - int offset = layout_rect.x; - if ((extra_space != 0) && m_packed.layout_mode_dist_pos != LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP) { - // To calculate the offset we need to predict the used space. We can do that by checking - // the distribution mode and total_max_pref_diff_w. That's how much the widgets could possible - // expand in the layout below. - - int used_space = total_preferred_w; - if (m_packed.layout_mode_dist != LAYOUT_DISTRIBUTION_PREFERRED) { - used_space += Min(extra_space, total_max_pref_diff_w); - } - - if (m_packed.layout_mode_dist_pos == LAYOUT_DISTRIBUTION_POSITION_CENTER) { - offset += (layout_rect.w - used_space) / 2; - } else { // LAYOUT_DISTRIBUTION_POSITION_RIGHT_BOTTOM - offset += layout_rect.w - used_space; - } - } - - // Layout - int used_space = 0; - for (TBWidget *child = getFirstInLayoutOrder(); child != nullptr; child = getNextInLayoutOrder(child)) { - if (child->getVisibility() == WIDGET_VISIBILITY_GONE) { - continue; - } - - const int ending_space = getTrailingSpace(child, spacing); - const PreferredSize ps = RotPreferredSize(child->getPreferredSize(inner_sc), m_axis); - const WIDGET_GRAVITY gravity = RotGravity(child->getGravity(), m_axis); - - // Calculate width. May shrink if space is missing, or grow if we have extra space. - int width = ps.pref_w; - if ((missing_space != 0) && (total_min_pref_diff_w != 0)) { - int diff_w = ps.pref_w - ps.min_w; - float factor = (float)diff_w / (float)total_min_pref_diff_w; - int removed = (int)(missing_space * factor); - removed = Min(removed, diff_w); - width -= removed; - - total_min_pref_diff_w -= diff_w; - missing_space -= removed; - } else if ((extra_space != 0) && (total_max_pref_diff_w != 0) && qualifyForExpansion(gravity)) { - int capped_max_w = Min(layout_rect.w, ps.max_w); - int diff_w = capped_max_w - ps.pref_w; - float factor = (float)diff_w / (float)total_max_pref_diff_w; - int added = (int)(extra_space * factor); - added = Min(added, diff_w); - width += added; - - total_max_pref_diff_w -= capped_max_w - ps.pref_w; - extra_space -= added; - } - - // Calculate height - int available_height = layout_rect.h; - int height = getWantedHeight(gravity, ps, available_height); - - // Calculate position - int pos = layout_rect.y; - switch (m_packed.layout_mode_pos) { - case LAYOUT_POSITION_CENTER: - pos += (available_height - height) / 2; - break; - case LAYOUT_POSITION_RIGHT_BOTTOM: - pos += available_height - height; - break; - case LAYOUT_POSITION_GRAVITY: - if (((gravity & WIDGET_GRAVITY_TOP) != 0U) && ((gravity & WIDGET_GRAVITY_BOTTOM) != 0U)) { - pos += (available_height - height) / 2; - } else if ((gravity & WIDGET_GRAVITY_BOTTOM) != 0U) { - pos += available_height - height; - } - break; - default: // LAYOUT_POSITION_LEFT_TOP - break; - } - - // Done! Set rect and increase used space - TBRect rect(used_space + offset, pos, width, height); - used_space += width + ending_space; - - child->setRect(RotRect(rect, m_axis)); - } - // Update overflow and overflow scroll - m_overflow = Max(0, used_space - layout_rect.w); - setOverflowScroll(m_overflow_scroll); -} - -PreferredSize TBLayout::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - // Do a layout pass (without layouting) to check childrens preferences. - PreferredSize ps; - validateLayout(constraints, &ps); - return ps; -} - -bool TBLayout::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE) { - int old_scroll = getOverflowScroll(); - setOverflowScroll(m_overflow_scroll + ev.delta_y * TBSystem::getPixelsPerLine()); - return m_overflow_scroll != old_scroll; - } - return false; -} - -void TBLayout::onPaintChildren(const PaintProps &paintProps) { - TBRect padding_rect = getPaddingRect(); - if (padding_rect.isEmpty()) { - return; - } - - // If we overflow the layout, apply clipping when painting children - TBRect old_clip_rect; - if (m_overflow != 0) { - // We only want clipping in one axis (the overflowing one) so we - // don't damage any expanded skins on the other axis. Add some fluff. - TBRect clip_rect = padding_rect; - const int fluff = 100; - - if (m_axis == AXIS_X) { - clip_rect = clip_rect.expand(m_overflow_scroll == 0 ? fluff : 0, fluff, - m_overflow_scroll == m_overflow ? fluff : 0, fluff); - } else { - clip_rect = clip_rect.expand(fluff, m_overflow_scroll == 0 ? fluff : 0, fluff, - m_overflow_scroll == m_overflow ? fluff : 0); - } - - old_clip_rect = g_renderer->setClipRect(clip_rect, true); - - TB_IF_DEBUG_SETTING(LAYOUT_CLIPPING, g_tb_skin->paintRect(clip_rect, TBColor(255, 0, 0, 200), 1)); - } - - // Paint children - TBWidget::onPaintChildren(paintProps); - - // Paint fadeout image over the overflowed edges - // to the indicate to used that it's overflowed. - if ((m_overflow != 0) && m_packed.paint_overflow_fadeout) { - TBID skin_x; - TBID skin_y; - if (m_axis == AXIS_X) { - skin_x = TBIDC("TBLayout.fadeout_x"); - } else { - skin_y = TBIDC("TBLayout.fadeout_y"); - } - - drawEdgeFadeout(padding_rect, skin_x, skin_y, m_overflow_scroll, m_overflow_scroll, - m_overflow - m_overflow_scroll, m_overflow - m_overflow_scroll); - } - - // Restore clipping - if (m_overflow != 0) { - g_renderer->setClipRect(old_clip_rect, false); - } -} - -void TBLayout::onProcess() { - SizeConstraints sc(getRect().w, getRect().h); - validateLayout(sc); -} - -void TBLayout::onResized(int oldW, int oldH) { - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); - SizeConstraints sc(getRect().w, getRect().h); - validateLayout(sc); -} - -void TBLayout::onInflateChild(TBWidget *child) { - // Do nothing since we're going to layout the child soon. -} - -void TBLayout::getChildTranslation(int &x, int &y) const { - if (m_axis == AXIS_X) { - x = -m_overflow_scroll; - y = 0; - } else { - x = 0; - y = -m_overflow_scroll; - } -} - -void TBLayout::scrollTo(int x, int y) { - setOverflowScroll(m_axis == AXIS_X ? x : y); -} - -TBWidget::ScrollInfo TBLayout::getScrollInfo() { - ScrollInfo info; - if (m_axis == AXIS_X) { - info.max_x = m_overflow; - info.x = m_overflow_scroll; - } else { - info.max_y = m_overflow; - info.y = m_overflow_scroll; - } - return info; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_layout.h b/src/modules/ui/turbobadger/tb/tb_layout.h deleted file mode 100644 index 244e1beda..000000000 --- a/src/modules/ui/turbobadger/tb/tb_layout.h +++ /dev/null @@ -1,171 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets.h" - -namespace tb { - -/** This means the spacing should be the default, read from the skin. */ -#define SPACING_FROM_SKIN TB_INVALID_DIMENSION - -/** Specifies which height widgets in a AXIS_X layout should have, - or which width widgets in a AXIS_Y layout should have. - No matter what, it will still prioritize minimum and maximum for each widget. */ -enum LAYOUT_SIZE { - LAYOUT_SIZE_GRAVITY, ///< Sizes depend on the gravity for each widget. (If the widget pulls - ///< towards both directions, it should grow to all available space) - LAYOUT_SIZE_PREFERRED, ///< Size will be the preferred so each widget may be sized differently. [default] - LAYOUT_SIZE_AVAILABLE ///< Size should grow to all available space -}; - -/** Specifies which y position widgets in a AXIS_X layout should have, - or which x position widgets in a AXIS_Y layout should have. */ -enum LAYOUT_POSITION { - LAYOUT_POSITION_CENTER, ///< Position is centered. [default] - LAYOUT_POSITION_LEFT_TOP, ///< Position is to the left for AXIS_Y layout and top for AXIS_X layout. - LAYOUT_POSITION_RIGHT_BOTTOM, ///< Position is to the right for AXIS_Y layout and bottom for AXIS_X layout. - LAYOUT_POSITION_GRAVITY, ///< Position depend on the gravity for each widget. (If the widget pulls - ///< towards both directions, it will be centered) -}; - -/** Specifies which width widgets in a AXIS_X layout should have, - or which height widgets in a AXIS_Y layout should have. */ -enum LAYOUT_DISTRIBUTION { - LAYOUT_DISTRIBUTION_PREFERRED, ///< Size will be the preferred so each widget may be sized differently. [default] - LAYOUT_DISTRIBUTION_AVAILABLE, ///< Size should grow to all available space - LAYOUT_DISTRIBUTION_GRAVITY ///< Sizes depend on the gravity for each widget. (If the widget pulls - ///< towards both directions, it should grow to all available space) -}; - -/** Specifies how widgets should be moved horizontally in a AXIS_X - layout (or vertically in a AXIS_Y layout) if there is extra space - available. */ -enum LAYOUT_DISTRIBUTION_POSITION { - LAYOUT_DISTRIBUTION_POSITION_CENTER, ///< Position centered. [default] - LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP, ///< Position to the upper left. - LAYOUT_DISTRIBUTION_POSITION_RIGHT_BOTTOM ///< Position to the lower right. -}; - -/** Layout order parameter for TBLayout::setLayoutOrder. */ -enum LAYOUT_ORDER { - LAYOUT_ORDER_BOTTOM_TO_TOP, ///< From bottom to top widget (default creation order). - LAYOUT_ORDER_TOP_TO_BOTTOM ///< From top to bottom widget. -}; - -/** Specifies what happens when there is not enough room for the layout, even - when all the children have been shrunk to their minimum size. */ -enum LAYOUT_OVERFLOW { - LAYOUT_OVERFLOW_CLIP, ///< Clip the chilren widgtes. [default] - LAYOUT_OVERFLOW_SCROLL ///< Create a scroller. - // LAYOUT_OVERFLOW_WRAP -}; - -/** TBLayout layouts its children along the given axis. - - Each widgets size depend on its preferred size (See TBWidget::getPreferredSize), - gravity, and the specified layout settings (See SetLayoutSize, SetLayoutPosition - SetLayoutOverflow, SetLayoutDistribution, SetLayoutDistributionPosition), and - the available size. - - Each widget is also separated by the specified spacing (See SetSpacing). -*/ - -class TBLayout : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBLayout, TBWidget); - - TBLayout(AXIS axis = AXIS_X); - - /** Set along which axis the content should be layouted */ - virtual void setAxis(AXIS axis) override; - virtual AXIS getAxis() const override { - return m_axis; - } - - /** Set the spacing between widgets in this layout. Setting the default (SPACING_FROM_SKIN) - will make it use the spacing specified in the skin. */ - void setSpacing(int spacing); - int getSpacing() const { - return m_spacing; - } - - /** Set the overflow scroll. If there is not enough room for all children in this layout, - it can scroll in the axis it's laid out. It does so automatically by wheel or panning also - for other LAYOUT_OVERFLOW than LAYOUT_OVERFLOW_SCROLL. */ - void setOverflowScroll(int overflow_scroll); - int getOverflowScroll() const { - return m_overflow_scroll; - } - - /** Set if a fadeout should be painter where the layout overflows or not. */ - void setPaintOverflowFadeout(bool paintFadeout) { - m_packed.paint_overflow_fadeout = paintFadeout; - } - - /** Set the layout size mode. See LAYOUT_SIZE. */ - void setLayoutSize(LAYOUT_SIZE size); - - /** Set the layout position mode. See LAYOUT_POSITION. */ - void setLayoutPosition(LAYOUT_POSITION pos); - - /** Set the layout size mode. See LAYOUT_OVERFLOW. */ - void setLayoutOverflow(LAYOUT_OVERFLOW overflow); - - /** Set the layout distribution mode. See LAYOUT_DISTRIBUTION. */ - void setLayoutDistribution(LAYOUT_DISTRIBUTION distribution); - - /** Set the layout distribution position mode. See LAYOUT_DISTRIBUTION_POSITION. */ - void setLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION distribution_pos); - - /** Set the layout order. The default is LAYOUT_ORDER_BOTTOM_TO_TOP, which begins - from bottom to top (default creation order). */ - void setLayoutOrder(LAYOUT_ORDER order); - - virtual void invalidateLayout(INVALIDATE_LAYOUT il) override; - - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override; - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onPaintChildren(const PaintProps &paint_props) override; - virtual void onProcess() override; - virtual void onResized(int oldW, int oldH) override; - virtual void onInflateChild(TBWidget *child) override; - virtual void getChildTranslation(int &x, int &y) const override; - virtual void scrollTo(int x, int y) override; - virtual TBWidget::ScrollInfo getScrollInfo() override; - -protected: - AXIS m_axis; - int m_spacing; - int m_overflow; - int m_overflow_scroll; - union { - struct { - uint32_t layout_is_invalid : 1; - uint32_t layout_mode_size : 4; - uint32_t layout_mode_pos : 4; - uint32_t layout_mode_overflow : 4; - uint32_t layout_mode_dist : 4; - uint32_t layout_mode_dist_pos : 4; - uint32_t mode_reverse_order : 1; - uint32_t paint_overflow_fadeout : 1; - } m_packed; - uint32_t m_packed_init; - }; - void validateLayout(const SizeConstraints &constraints, PreferredSize *calculate_ps = nullptr); - /** Can this TBLayout expand in its direction? */ - bool qualifyForExpansion(WIDGET_GRAVITY gravity) const; - int getWantedHeight(WIDGET_GRAVITY gravity, const PreferredSize &ps, int available_height) const; - TBWidget *getNextNonCollapsedWidget(TBWidget *child) const; - int getTrailingSpace(TBWidget *child, int spacing) const; - int calculateSpacing(); - TBWidget *getFirstInLayoutOrder() const; - TBWidget *getNextInLayoutOrder(TBWidget *child) const; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_linklist.cpp b/src/modules/ui/turbobadger/tb/tb_linklist.cpp deleted file mode 100644 index 34643d9d5..000000000 --- a/src/modules/ui/turbobadger/tb/tb_linklist.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/** - * @file - */ - -#include "tb_linklist.h" - -namespace tb { - -TBLinkListIterator::TBLinkListIterator(TBLinkList *linklist, TBLink *currentLink, bool forward) - : m_linklist(linklist), m_current_link(currentLink), m_forward(forward) { - doRegister(); -} - -TBLinkListIterator::TBLinkListIterator(const TBLinkListIterator &iter) - : m_linklist(iter.m_linklist), m_current_link(iter.m_current_link), m_forward(iter.m_forward) { - doRegister(); -} - -TBLinkListIterator::~TBLinkListIterator() { - unregister(); -} - -void TBLinkListIterator::doRegister() { - m_prev = nullptr; - m_next = m_linklist->first_iterator; - if (m_linklist->first_iterator != nullptr) { - m_linklist->first_iterator->m_prev = this; - } - m_linklist->first_iterator = this; -} - -void TBLinkListIterator::unregister() { - if (m_linklist == nullptr) { // Already unregistered - return; - } - if (m_prev != nullptr) { - m_prev->m_next = m_next; - } - if (m_next != nullptr) { - m_next->m_prev = m_prev; - } - if (m_linklist->first_iterator == this) { - m_linklist->first_iterator = m_next; - } -} - -void TBLinkListIterator::unregisterAndClear() { - unregister(); - m_linklist = nullptr; - m_current_link = nullptr; - m_prev = nullptr; - m_next = nullptr; -} - -const TBLinkListIterator &TBLinkListIterator::operator=(const TBLinkListIterator &iter) { - if (m_linklist != iter.m_linklist) { - // Change where we are registered if we change linklist. - unregister(); - m_linklist = iter.m_linklist; - doRegister(); - } - m_linklist = iter.m_linklist; - m_current_link = iter.m_current_link; - m_forward = iter.m_forward; - return *this; -} - -void TBLinkListIterator::reset() { - if (m_linklist != nullptr) { - m_current_link = m_forward ? m_linklist->first : m_linklist->last; - } else { - m_current_link = nullptr; - } -} - -TBLink *TBLinkListIterator::getAndStep() { - if (m_current_link == nullptr) { - return nullptr; - } - TBLink *current = m_current_link; - m_current_link = m_forward ? m_current_link->next : m_current_link->prev; - return current; -} - -void TBLinkListIterator::removeLink(TBLink *link) { - // If the current link is being removed, step away from it - if (m_current_link == link) { - getAndStep(); - } -} - -TBLinkList::~TBLinkList() { - removeAll(); - - // Make sure any live iterators for this linklist are cleared! - while (first_iterator != nullptr) { - first_iterator->unregisterAndClear(); - } -} - -void TBLinkList::addFirst(TBLink *link) { - core_assert(!link->linklist); // Link is already in some list! - link->linklist = this; - link->next = first; - if (first != nullptr) { - first->prev = link; - } - first = link; - if (last == nullptr) { - last = link; - } -} - -void TBLinkList::addLast(TBLink *link) { - core_assert(!link->linklist); // Link is already in some list! - link->linklist = this; - link->prev = last; - if (last != nullptr) { - last->next = link; - } - last = link; - if (first == nullptr) { - first = link; - } -} - -void TBLinkList::addBefore(TBLink *link, TBLink *reference) { - core_assert(reference->linklist == this); // Reference is not added to this list! - link->linklist = this; - link->prev = reference->prev; - link->next = reference; - if (reference->prev != nullptr) { - reference->prev->next = link; - } else { - first = link; - } - reference->prev = link; -} - -void TBLinkList::addAfter(TBLink *link, TBLink *reference) { - core_assert(reference->linklist == this); // Reference is not added to this list! - link->linklist = this; - link->prev = reference; - link->next = reference->next; - if (reference->next != nullptr) { - reference->next->prev = link; - } else { - last = link; - } - reference->next = link; -} - -void TBLinkList::remove(TBLink *link) { - core_assert(link->linklist == this); // Link is not added to this list! - - // Go through iterators and make sure there are no pointers - // to the link we remove. - TBLinkListIterator *iter = first_iterator; - while (iter != nullptr) { - iter->removeLink(link); - iter = iter->m_next; - } - // Remove the link - if (link->next != nullptr) { - link->next->prev = link->prev; - } - if (link->prev != nullptr) { - link->prev->next = link->next; - } - if (first == link) { - first = link->next; - } - if (last == link) { - last = link->prev; - } - link->linklist = nullptr; - link->prev = nullptr; - link->next = nullptr; -} - -void TBLinkList::removeAll() { - // Reset all iterators. - TBLinkListIterator *iter = first_iterator; - while (iter != nullptr) { - iter->m_current_link = nullptr; - iter = iter->m_next; - } - // Remove all links - TBLink *link = first; - while (link != nullptr) { - TBLink *next = link->next; - link->linklist = nullptr; - link->prev = nullptr; - link->next = nullptr; - link = next; - } - first = nullptr; - last = nullptr; -} - -int TBLinkList::countLinks() const { - int count = 0; - for (TBLink *link = first; link != nullptr; link = link->next) { - count++; - } - return count; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_linklist.h b/src/modules/ui/turbobadger/tb/tb_linklist.h deleted file mode 100644 index 8415e2fc5..000000000 --- a/src/modules/ui/turbobadger/tb/tb_linklist.h +++ /dev/null @@ -1,257 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/Assert.h" -#include "tb_core.h" - -namespace tb { - -class TBLinkList; -class TBLink; - -/** TBLinkListIterator - The backend class for a safe iteration of a TBLinkList. - - You would normally recieve a typed iterator from a TBLinkListOf::iterateForward - or TBLinkListOf::iterateBackward, instead of creating this object directly. - - Safe iteration means that if a link is removed from a linked list, _all_ iterators that currently - point to that link will automatically step to the next link in the iterators direction. */ - -class TBLinkListIterator { -public: - TBLinkListIterator(const TBLinkListIterator &iter); - TBLinkListIterator(TBLinkList *linklist, TBLink *current_link, bool forward); - ~TBLinkListIterator(); - - /** Set the iterator to the first link in we iterate forward, - or set it to the last link if we iterate backward. */ - void reset(); - - /** Get the current link or nullptr if out of bounds. */ - TBLink *get() const { - return m_current_link; - } - - /** Get the current link and step the iterator to the next (forward or backward). */ - TBLink *getAndStep(); - - operator TBLink *() const { - return m_current_link; - } - - const TBLinkListIterator &operator=(const TBLinkListIterator &iter); - -private: - TBLinkList *m_linklist; ///< The linklist we are iterating. - TBLink *m_current_link; ///< The current link, or nullptr. - bool m_forward; ///< true if we iterate from first to last item. - - TBLinkListIterator *m_prev; ///< Link in list of iterators for m_linklist - TBLinkListIterator *m_next; ///< Link in list of iterators for m_linklist - - /** RemoveLink is called when removing/deleting links in the target linklist. - This will make sure iterators skip the deleted item. */ - void removeLink(TBLink *link); - friend class TBLinkList; - - /** Add ourself to the chain of iterators in the linklist. */ - void doRegister(); - - /** Unlink ourself from the chain of iterators in the linklist. */ - void unregister(); - void unregisterAndClear(); -}; - -/** TBLink - The backend class to be inserted in TBLinkList. - Use the typed TBLinkOf for object storing! */ - -class TBLink { -public: - TBLink() : prev(nullptr), next(nullptr), linklist(nullptr) { - } - - /** Return true if the link is currently added to a list. */ - bool isInList() const { - return linklist != nullptr; - } - -public: - TBLink *prev; - TBLink *next; - TBLinkList *linklist; -}; - -template class TBLinkOf : public TBLink { -public: - inline T *getPrev() const { - return (T *)prev; - } - inline T *getNext() const { - return (T *)next; - } -}; - -/** TBLinkList - This is the backend for TBLinkListOf and TBLinkListAutoDeleteOf. - You should use the typed TBLinkListOf or TBLinkListAutoDeleteOf for object storing! */ - -class TBLinkList { -public: - TBLinkList() : first(nullptr), last(nullptr), first_iterator(nullptr) { - } - ~TBLinkList(); - - void remove(TBLink *link); - void removeAll(); - - void addFirst(TBLink *link); - void addLast(TBLink *link); - - void addBefore(TBLink *link, TBLink *reference); - void addAfter(TBLink *link, TBLink *reference); - - bool containsLink(TBLink *link) const { - return link->linklist == this; - } - - bool hasLinks() const { - return first != nullptr; - } - - int countLinks() const; - -public: - TBLink *first; - TBLink *last; - TBLinkListIterator *first_iterator; -}; - -/** TBLinkListOf is a double linked linklist. */ - -template class TBLinkListOf { -public: - /** Remove link from this linklist. */ - void remove(T *link) { - m_linklist.remove(static_cast *>(link)); - } - - /** Remove link from this linklist and delete it. */ - void doDelete(T *link) { - m_linklist.remove(static_cast *>(link)); - delete link; - } - - /** Remove all links without deleting them. */ - void removeAll() { - m_linklist.removeAll(); - } - - /** Delete all links in this linklist. */ - void deleteAll() { - while (T *t = getFirst()) { - doDelete(t); - } - } - - /** Add link first in this linklist. */ - void addFirst(T *link) { - m_linklist.addFirst(static_cast *>(link)); - } - - /** Add link last in this linklist. */ - void addLast(T *link) { - m_linklist.addLast(static_cast *>(link)); - } - - /** Add link before the reference link (which must be added to this linklist). */ - void addBefore(T *link, T *reference) { - m_linklist.addBefore(static_cast *>(link), reference); - } - - /** Add link after the reference link (which must be added to this linklist). */ - void addAfter(T *link, T *reference) { - m_linklist.addAfter(static_cast *>(link), reference); - } - - /** Return true if the link is currently added to this linklist. */ - bool containsLink(T *link) const { - return m_linklist.containsLink(static_cast *>(link)); - } - - /** Get the first link, or nullptr. */ - T *getFirst() const { - return (T *)static_cast *>(m_linklist.first); - } - - /** Get the last link, or nullptr. */ - T *getLast() const { - return (T *)static_cast *>(m_linklist.last); - } - - /** Return true if this linklist contains any links. */ - bool hasLinks() const { - return m_linklist.hasLinks(); - } - - /** Count the number of links in this list by iterating through all links. */ - int countLinks() const { - return m_linklist.countLinks(); - } - - /** Typed iterator for safe iteration. For more info, see TBLinkListIterator. */ - class Iterator : public TBLinkListIterator { - public: - Iterator(TBLinkListOf *linklistof, bool forward) - : TBLinkListIterator(&linklistof->m_linklist, - forward ? linklistof->m_linklist.first : linklistof->m_linklist.last, forward) { - } - Iterator(TBLinkListOf *linklistof, T *link, bool forward) - : TBLinkListIterator(&linklistof->m_linklist, link, forward) { - } - inline T *get() const { - return (T *)static_cast *>(TBLinkListIterator::get()); - } - inline T *getAndStep() { - return (T *)static_cast *>(TBLinkListIterator::getAndStep()); - } - inline operator T *() const { - return (T *)static_cast *>(get()); - } - }; - - /** Get a forward iterator that starts with the first link. */ - Iterator iterateForward() { - return Iterator(this, true); - } - - /** Get a forward iterator that starts with the given link. */ - Iterator iterateForward(T *link) { - return Iterator(this, link, true); - } - - /** Get a backward iterator that starts with the last link. */ - Iterator iterateBackward() { - return Iterator(this, false); - } - - /** Get a backward iterator that starts with the given link. */ - Iterator iterateBackward(T *link) { - return Iterator(this, link, false); - } - -private: - TBLinkList m_linklist; -}; - -/** TBLinkListAutoDeleteOf is a double linked linklist that deletes all links on destruction. */ - -template class TBLinkListAutoDeleteOf : public TBLinkListOf { -public: - ~TBLinkListAutoDeleteOf() { - TBLinkListOf::deleteAll(); - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_list.cpp b/src/modules/ui/turbobadger/tb/tb_list.cpp deleted file mode 100644 index 5e4fbdb27..000000000 --- a/src/modules/ui/turbobadger/tb/tb_list.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/** - * @file - */ - -#include "tb_list.h" -#include "core/Assert.h" -#include "core/StandardLib.h" -#include "tb_core.h" - -namespace tb { - -bool TBListBackend::add(void *data) { - if (!growIfNeeded()) { - return false; - } - m_data->list[m_data->num] = data; - m_data->num++; - return true; -} - -bool TBListBackend::add(void *data, int index) { - core_assert(index >= 0 && index <= getNumItems()); - if (!growIfNeeded()) { - return false; - } - if (index < m_data->num) { - SDL_memmove(&m_data->list[index + 1], &m_data->list[index], (m_data->num - index) * sizeof(void *)); - } - m_data->list[index] = data; - m_data->num++; - return true; -} - -void TBListBackend::set(void *data, int index) { - core_assert(index >= 0 && index < getNumItems()); - m_data->list[index] = data; -} - -void *TBListBackend::removeFast(int index) { - core_assert(index >= 0 && index < getNumItems()); - void *data = m_data->list[index]; - m_data->list[index] = m_data->list[m_data->num - 1]; - m_data->num--; - return data; -} - -void *TBListBackend::remove(int index) { - core_assert(index >= 0 && index < getNumItems()); - void *data = m_data->list[index]; - if (index < m_data->num - 1) { - SDL_memmove(&m_data->list[index], &m_data->list[index + 1], (m_data->num - index - 1) * sizeof(void *)); - } - m_data->num--; - return data; -} - -void TBListBackend::removeAll() { - core_free(m_data); - m_data = nullptr; -} - -void TBListBackend::swap(int index1, int index2) { - core_assert(index1 >= 0 && index1 < getNumItems()); - core_assert(index2 >= 0 && index2 < getNumItems()); - void *tmp = m_data->list[index1]; - m_data->list[index1] = m_data->list[index2]; - m_data->list[index2] = tmp; -} - -int TBListBackend::find(const void *data) const { - int num = getNumItems(); - for (int i = 0; i < num; i++) { - if (get(i) == data) { - return i; - } - } - return -1; -} - -void *TBListBackend::get(int index) const { - core_assert(index >= 0 && index < getNumItems()); - return m_data->list[index]; -} - -bool TBListBackend::reserve(int newCapacity) { - core_assert(newCapacity > 0); - if (newCapacity > getCapacity()) { - int num = getNumItems(); - if (char *new_data = (char *)core_realloc(m_data, sizeof(TBLIST_DATA) + sizeof(void *) * (newCapacity))) { - m_data = (TBLIST_DATA *)new_data; - m_data->num = num; - m_data->capacity = newCapacity; - m_data->list = (void **)(new_data + sizeof(TBLIST_DATA)); - return true; - } - return false; - } - return true; -} - -bool TBListBackend::growIfNeeded() { - int capacity = getCapacity(); - if (getNumItems() == capacity) { - return reserve(capacity == 0 ? 4 : capacity * 2); - } - return true; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_list.h b/src/modules/ui/turbobadger/tb/tb_list.h deleted file mode 100644 index 57d3f45a9..000000000 --- a/src/modules/ui/turbobadger/tb/tb_list.h +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -namespace tb { - -/** TBList is a list (array) of pointers to any kind of objects. - This is the backend for TBListOf and TBListAutoDeleteOf. - You should use the typed TBListOf or TBListAutoDeleteOf for object storing! */ -class TBListBackend { -public: - TBListBackend() : m_data(nullptr) { - } - ~TBListBackend() { - removeAll(); - } - bool reserve(int newCapacity); - bool growIfNeeded(); - bool add(void *data); - bool add(void *data, int index); - void set(void *data, int index); - void *get(int index) const; - void *operator[](int index) const { - return get(index); - } - void *removeFast(int index); - void *remove(int index); - void removeAll(); - void swap(int index1, int index2); - int find(const void *data) const; - int getNumItems() const { - return m_data != nullptr ? m_data->num : 0; - } - int getCapacity() const { - return m_data != nullptr ? m_data->capacity : 0; - } - -private: - struct TBLIST_DATA { - int num; - int capacity; - void **list; - }; - TBLIST_DATA *m_data; -}; - -/** TBListOf is a list (array) of pointers to the specified object type. - Note: The objects won't be deleted automatically. If you want that, - use TBListAutoDeleteOf! */ -template class TBListOf { -public: - /** Make sure there is space for at least num items in the list. Returns false on OOM failure. */ - bool reserve(int num) { - return m_list.reserve(num); - } - - /** Make sure there is space for at least one more item in the list. Returns false on OOM failure. - There's no need to call this, but it can make OOM handling easier in some situations since you - can guarantee there is space is in a list *before* you allocate an object to insert into it. */ - bool growIfNeeded() { - return m_list.growIfNeeded(); - } - - /** Add data at the end of the list. Returns false on OOM failure. */ - bool add(T *data) { - return m_list.add(data); - } - - /** Add data at the given index in the list. Returns false on OOM failure. */ - bool add(T *data, int index) { - return m_list.add(data, index); - } - - /** Replace the item at the index with the new data */ - void set(T *data, int index) { - m_list.set(data, index); - } - - /** Returns the content at position index. */ - T *get(int index) const { - return (T *)m_list.get(index); - } - - /** Returns the content at position index. */ - T *operator[](int index) const { - return (T *)m_list.get(index); - } - - /** Remove the item at position index from the list and returns the pointer. - This method should only be used when the order of the list is not important. - If the order is important, use Remove() */ - T *removeFast(int index) { - return (T *)m_list.removeFast(index); - } - - /** Remove the item at position index from the list and returns the pointer. */ - T *remove(int index) { - return (T *)m_list.remove(index); - } - - /** Deletes the item at position index after removing it from the list. - This method should only be used when the order of the list is not important. - If the order is important, use Delete() */ - void deleteFast(int index) { - delete (T *)m_list.removeFast(index); - } - - /** Deletes the item at position index after removing it from the list. */ - void doDelete(int index) { - delete (T *)m_list.remove(index); - } - - /** Remove all items without deleding them. */ - void removeAll() { - m_list.removeAll(); - } - - /** Remove and delete all items from the list. */ - void deleteAll() { - for (int i = 0; i < getNumItems(); i++) { - delete (T *)get(i); - } - m_list.removeAll(); - } - - /** Swap the items at index1 and index2 */ - void swap(int index1, int index2) { - m_list.swap(index1, index2); - } - - /** Search for the item with the given data and return the found index, or -1 if not found. */ - int find(const T *data) const { - return m_list.find(data); - } - - /** Get the number of items in the list. */ - int getNumItems() const { - return m_list.getNumItems(); - } - - /** Get the capacity of the list number of items it can hold without allocating more memory) */ - int getCapacity() const { - return m_list.getCapacity(); - } - -private: - TBListBackend m_list; -}; - -/** TBListAutoDeleteOf is a list (array) of pointers to the specified object type. - The objects will be deleted automatically on destruction. */ -template class TBListAutoDeleteOf : public TBListOf { -public: - ~TBListAutoDeleteOf() { - TBListOf::deleteAll(); - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_menu_window.cpp b/src/modules/ui/turbobadger/tb/tb_menu_window.cpp deleted file mode 100644 index 52455b353..000000000 --- a/src/modules/ui/turbobadger/tb/tb_menu_window.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file - */ - -#include "tb_menu_window.h" -#include "tb_widgets_listener.h" - -namespace tb { - -TBMenuWindow::TBMenuWindow(TBWidget *target, const TBID &id) : TBPopupWindow(target) { - setID(id); - setSkinBg(TBIDC("TBMenuWindow"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_select_list.getScrollContainer()->setAdaptToContentSize(true); - m_select_list.setIsFocusable(false); ///< Avoid it autoclosing its window on click - m_select_list.setSkinBg(""); - m_select_list.setRect(getPaddingRect()); - m_select_list.setGravity(WIDGET_GRAVITY_ALL); - addChild(&m_select_list); -} - -TBMenuWindow::~TBMenuWindow() { - removeChild(&m_select_list); -} - -void TBMenuWindow::onDie() { - m_select_list.setSource(nullptr); -} - -bool TBMenuWindow::show(TBSelectItemSource *source, const TBPopupAlignment &alignment, int initialValue) { - m_select_list.setValue(initialValue); - m_select_list.setSource(source); - m_select_list.validateList(); - - return TBPopupWindow::show(alignment); -} - -bool TBMenuWindow::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_CLICK && &m_select_list == ev.target) { - TBWidgetSafePointer this_widget(this); - - // Invoke the click on the target - TBWidgetEvent target_ev(EVENT_TYPE_CLICK); - target_ev.ref_id = ev.ref_id; - invokeEvent(target_ev); - - // If target not deleted, close - if (this_widget.get() != nullptr) { - close(); - } - return true; - } - return TBPopupWindow::onEvent(ev); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_menu_window.h b/src/modules/ui/turbobadger/tb/tb_menu_window.h deleted file mode 100644 index 94489e521..000000000 --- a/src/modules/ui/turbobadger/tb/tb_menu_window.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_popup_window.h" -#include "tb_select.h" - -namespace tb { - -/** TBMenuWindow is a popup window that shows a list of items (TBSelectList). - - When selected it will invoke a click with the id given to the menu, - and the id of the clicked item as ref_id, and then close itself. - - It may open sub items as new windows at the same time as this window is open.*/ - -class TBMenuWindow : public TBPopupWindow { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBMenuWindow, TBPopupWindow); - - TBMenuWindow(TBWidget *target, const TBID &id); - ~TBMenuWindow(); - - bool show(TBSelectItemSource *source, const TBPopupAlignment &alignment, int initial_value = -1); - - TBSelectList *getList() { - return &m_select_list; - } - - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onDie() override; - -private: - TBSelectList m_select_list; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_message_window.cpp b/src/modules/ui/turbobadger/tb/tb_message_window.cpp deleted file mode 100644 index 7e31b564c..000000000 --- a/src/modules/ui/turbobadger/tb/tb_message_window.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/** - * @file - */ - -#include "tb_message_window.h" -#include "core/Assert.h" -#include "tb_editfield.h" -#include "tb_language.h" -#include "tb_widgets_reader.h" - -namespace tb { - -// == TBMessageWindow ======================================================================================= - -TBMessageWindow::TBMessageWindow(TBWidget *target, const TBID &id) : m_target(target) { - TBWidgetListener::addGlobalListener(this); - setID(id); -} - -TBMessageWindow::~TBMessageWindow() { - TBWidgetListener::removeGlobalListener(this); - if (TBWidget *dimmer = m_dimmer.get()) { - dimmer->removeFromParent(); - delete dimmer; - } -} - -bool TBMessageWindow::show(const char *title, const char *message, TBMessageWindowSettings *settings) { - TBWidget *target = m_target.get(); - if (target == nullptr) { - return false; - } - - TBMessageWindowSettings default_settings; - if (settings == nullptr) { - settings = &default_settings; - } - - TBWidget *root = target->getParentRoot(); - - const char *source = "TBLayout: axis: y, distribution: available\n" - " TBLayout: distribution: available, size: available\n" - " TBSkinImage: id: 2\n" - " TBEditField: multiline: 1, readonly: 1, id: 1\n" - " TBLayout: distribution-position: right bottom, id: 3\n"; - if (!g_widgets_reader->loadData(getContentRoot(), source)) { - return false; - } - - setText(title); - - getWidgetByIDAndType(2)->setSkinBg(settings->icon_skin); - - TBEditField *editfield = getWidgetByIDAndType(1); - editfield->setStyling(settings->styling); - editfield->setText(message); - editfield->setTextAlign(settings->align); - editfield->setSkinBg(""); - - // Create buttons - if (settings->msg == TB_MSG_OK) { - addButton("TBMessageWindow.ok", true); - } else if (settings->msg == TB_MSG_OK_CANCEL) { - addButton("TBMessageWindow.ok", true); - addButton("TBMessageWindow.cancel", false); - } else if (settings->msg == TB_MSG_YES_NO) { - addButton("TBMessageWindow.yes", true); - addButton("TBMessageWindow.no", false); - } else if (settings->msg == TB_MSG_YES_NO_CANCEL) { - addButton("TBMessageWindow.yes", true); - addButton("TBMessageWindow.no", false); - addButton("TBMessageWindow.cancel", false); - } - - // Size to fit content. This will use the default size of the textfield. - resizeToFitContent(); - TBRect rect = getRect(); - - // Get how much we overflow the textfield has given the current width, and grow our height to show all we can. - // FIX: It would be better to use adapt-to-content on the editfield to achieve the most optimal size. - // At least when we do full blown multi pass size checking. - rect.h += editfield->getStyleEdit()->getOverflowY(); - - // Create background dimmer - if (settings->dimmer) { - if (TBDimmer *dimmer = new TBDimmer) { - root->addChild(dimmer); - m_dimmer.set(dimmer); - } - } - - // Center and size to the new height - TBRect bounds(0, 0, root->getRect().w, root->getRect().h); - setRect(rect.centerIn(bounds).moveIn(bounds).clip(bounds)); - root->addChild(this); - return true; -} - -void TBMessageWindow::addButton(const TBID &id, bool focused) { - TBLayout *layout = getWidgetByIDAndType(3); - if (layout == nullptr) { - return; - } - if (TBButton *btn = new TBButton) { - btn->setID(id); - btn->setText(g_tb_lng->getString(btn->getID())); - layout->addChild(btn); - if (focused) { - btn->setFocus(WIDGET_FOCUS_REASON_UNKNOWN); - } - } -} - -bool TBMessageWindow::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_CLICK && ev.target->isOfType()) { - TBWidgetSafePointer this_widget(this); - - // Invoke the click on the target - TBWidgetEvent target_ev(EVENT_TYPE_CLICK); - target_ev.ref_id = ev.target->getID(); - invokeEvent(target_ev); - - // If target got deleted, close - if (this_widget.get() != nullptr) { - close(); - } - return true; - } - if (ev.type == EVENT_TYPE_KEY_DOWN && ev.special_key == TB_KEY_ESC) { - TBWidgetEvent click_ev(EVENT_TYPE_CLICK); - m_close_button.invokeEvent(click_ev); - return true; - } - return TBWindow::onEvent(ev); -} - -void TBMessageWindow::onDie() { - if (TBWidget *dimmer = m_dimmer.get()) { - dimmer->die(); - } -} - -void TBMessageWindow::onWidgetDelete(TBWidget *widget) { - // If the target widget is deleted, close! - if (m_target.get() == nullptr) { - close(); - } -} - -bool TBMessageWindow::onWidgetDying(TBWidget *widget) { - // If the target widget or an ancestor of it is dying, close! - if (widget == m_target.get() || widget->isAncestorOf(m_target.get())) { - close(); - } - return false; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_message_window.h b/src/modules/ui/turbobadger/tb/tb_message_window.h deleted file mode 100644 index 78f6211a3..000000000 --- a/src/modules/ui/turbobadger/tb/tb_message_window.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets_listener.h" -#include "tb_window.h" - -namespace tb { - -enum TB_MSG { - TB_MSG_OK, - TB_MSG_OK_CANCEL, - TB_MSG_YES_NO, - TB_MSG_YES_NO_CANCEL, -}; - -/** TBMessageWindowSettings contains additional settings for TBMessageWindow. */ -class TBMessageWindowSettings { -public: - TBMessageWindowSettings() : msg(TB_MSG_OK), dimmer(false), styling(false) { - } - TBMessageWindowSettings(TB_MSG msg, const TBID &iconSkin) - : msg(msg), icon_skin(iconSkin), dimmer(false), styling(false), align(TB_TEXT_ALIGN_LEFT) { - } - -public: - TB_MSG msg; ///< The type of response for the message. - TBID icon_skin; ///< The icon skin (0 for no icon) - bool dimmer; ///< Set to true to dim background widgets by a TBDimmer. - bool styling; ///< Enable styling in the textfield. - TB_TEXT_ALIGN align = TB_TEXT_ALIGN_LEFT; ///< Text alignment in message box. -}; - -/** TBMessageWindow is a window for showing simple messages. - Events invoked in this window will travel up through the target widget. - - When the user click any of its buttons, it will invoke a click event - (with the window ID), with the clicked buttons id as ref_id. - Then it will delete itself. - - If the target widget is deleted while this window is alive, the - window will delete itself. */ -class TBMessageWindow : public TBWindow, private TBWidgetListener { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBMessageWindow, TBWindow); - - TBMessageWindow(TBWidget *target, const TBID &id); - virtual ~TBMessageWindow(); - - bool show(const char *title, const char *message, TBMessageWindowSettings *settings = nullptr); - - virtual TBWidget *getEventDestination() const override { - return m_target.get(); - } - - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onDie() override; - -private: - void addButton(const TBID &id, bool focused); - // TBWidgetListener - virtual void onWidgetDelete(TBWidget *widget) override; - virtual bool onWidgetDying(TBWidget *widget) override; - TBWidgetSafePointer m_dimmer; - TBWidgetSafePointer m_target; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_msg.cpp b/src/modules/ui/turbobadger/tb/tb_msg.cpp deleted file mode 100644 index 126352a01..000000000 --- a/src/modules/ui/turbobadger/tb/tb_msg.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/** - * @file - */ - -#include "tb_msg.h" -#include "tb_system.h" -#include - -namespace tb { - -/** List of all delayed messages */ -TBLinkListOf g_all_delayed_messages; - -/** List of all nondelayed messages. */ -TBLinkListOf g_all_normal_messages; - -TBMessage::TBMessage(const TBID &message, TBMessageData *data, double fireTimeMs, TBMessageHandler *mh) - : message(message), data(data), fire_time_ms(fireTimeMs), mh(mh) { -} - -TBMessage::~TBMessage() { - delete data; -} - -TBMessageHandler::TBMessageHandler() { -} - -TBMessageHandler::~TBMessageHandler() { - deleteAllMessages(); -} - -bool TBMessageHandler::postMessageDelayed(const TBID &message, TBMessageData *data, uint32_t delayInMs) { - return postMessageOnTime(message, data, TBSystem::getTimeMS() + (double)delayInMs); -} - -bool TBMessageHandler::postMessageOnTime(const TBID &message, TBMessageData *data, double fireTime) { - if (TBMessage *msg = new TBMessage(message, data, fireTime, this)) { - // Find the message that is already in the list that should fire later, so we can - // insert msg just before that. (Always keep the list ordered after fire time) - - // NOTE: If another message is added during OnMessageReceived, it might or might not be fired - // in the right order compared to other delayed messages, depending on if it's inserted before or - // after the message being processed! - - TBMessage *later_msg = nullptr; - TBMessageLink *link = g_all_delayed_messages.getFirst(); - while (link != nullptr) { - TBMessage *msg_in_list = static_cast(link); - if (msg_in_list->fire_time_ms > msg->fire_time_ms) { - later_msg = msg_in_list; - break; - } - link = link->getNext(); - } - - // Add it to the global list in the right order. - if (later_msg != nullptr) { - g_all_delayed_messages.addBefore(msg, later_msg); - } else { - g_all_delayed_messages.addLast(msg); - } - - // Add it to the list in messagehandler. - m_messages.addLast(msg); - - // If we added it first and there's no normal messages, the next fire time has - // changed and we have to reschedule the timer. - if ((g_all_normal_messages.getFirst() == nullptr) && g_all_delayed_messages.getFirst() == msg) { - TBSystem::rescheduleTimer(msg->fire_time_ms); - } - return true; - } - return false; -} - -bool TBMessageHandler::postMessage(const TBID &message, TBMessageData *data) { - if (TBMessage *msg = new TBMessage(message, data, 0, this)) { - g_all_normal_messages.addLast(msg); - m_messages.addLast(msg); - - // If we added it and there was no messages, the next fire time has - // changed and we have to reschedule the timer. - if (g_all_normal_messages.getFirst() == msg) { - TBSystem::rescheduleTimer(0); - } - return true; - } - return false; -} - -TBMessage *TBMessageHandler::getMessageByID(const TBID &message) { - TBLinkListOf::Iterator iter = m_messages.iterateForward(); - while (TBMessage *msg = iter.getAndStep()) { - if (msg->message == message) { - return msg; - } - } - return nullptr; -} - -void TBMessageHandler::deleteMessage(TBMessage *msg) { - core_assert(msg->mh == this); // This is not the message handler owning the message! - - // Remove from global list (g_all_delayed_messages or g_all_normal_messages) - if (g_all_delayed_messages.containsLink(msg)) { - g_all_delayed_messages.remove(msg); - } else if (g_all_normal_messages.containsLink(msg)) { - g_all_normal_messages.remove(msg); - } - - // Remove from local list - m_messages.remove(msg); - - delete msg; - - // Note: We could call TBSystem::RescheduleTimer if we think that deleting - // this message changed the time for the next message. -} - -void TBMessageHandler::deleteAllMessages() { - while (TBMessage *msg = m_messages.getFirst()) { - deleteMessage(msg); - } -} - -// static -void TBMessageHandler::processMessages() { - // Handle delayed messages - TBLinkListOf::Iterator iter = g_all_delayed_messages.iterateForward(); - while (TBMessage *msg = static_cast(iter.getAndStep())) { - if (TBSystem::getTimeMS() >= msg->fire_time_ms) { - // Remove from global list - g_all_delayed_messages.remove(msg); - // Remove from local list - msg->mh->m_messages.remove(msg); - - msg->mh->onMessageReceived(msg); - - delete msg; - } else { - break; // Since the list is sorted, all remaining messages should fire later - } - } - - // Handle normal messages - iter = g_all_normal_messages.iterateForward(); - while (TBMessage *msg = static_cast(iter.getAndStep())) { - // Remove from global list - g_all_normal_messages.remove(msg); - // Remove from local list - msg->mh->m_messages.remove(msg); - - msg->mh->onMessageReceived(msg); - - delete msg; - } -} - -// static -double TBMessageHandler::getNextMessageFireTime() { - if (g_all_normal_messages.getFirst() != nullptr) { - return 0; - } - - if (g_all_delayed_messages.getFirst() != nullptr) { - TBMessage *first_delayed_msg = static_cast(g_all_delayed_messages.getFirst()); - return first_delayed_msg->fire_time_ms; - } - - return TB_NOT_SOON; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_msg.h b/src/modules/ui/turbobadger/tb/tb_msg.h deleted file mode 100644 index 2b58e10fd..000000000 --- a/src/modules/ui/turbobadger/tb/tb_msg.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_id.h" -#include "tb_linklist.h" -#include "tb_object.h" -#include "tb_value.h" - -namespace tb { - -class TBMessageHandler; - -/** TB_NOT_SOON is returned from TBMessageHandler::getNextMessageFireTime - and means that there is currently no more messages to process. */ -#define TB_NOT_SOON 0xffffffff - -/** TBMessageData holds custom data to send with a posted message. */ - -class TBMessageData : public TBTypedObject { -public: - TBMessageData() { - } - TBMessageData(int v1, int v2) : v1(v1), v2(v2) { - } - virtual ~TBMessageData() { - } - -public: - TBValue v1; ///< Use for anything - TBValue v2; ///< Use for anything - TBID id1; ///< Use for anything - TBID id2; ///< Use for anything -}; - -/** TBMessageLink should never be created or subclassed anywhere except in TBMessage. - It's only purpose is to add a extra typed link for TBMessage, since it needs to be - added in multiple lists. */ -class TBMessageLink : public TBLinkOf {}; - -/** TBMessage is a message created and owned by TBMessageHandler. - It carries a message id, and may also carry a TBMessageData with - additional parameters. */ - -class TBMessage : public TBLinkOf, public TBMessageLink { -private: - TBMessage(const TBID &message, TBMessageData *data, double fire_time_ms, TBMessageHandler *mh); - ~TBMessage(); - -public: - TBID message; ///< The message id - TBMessageData *data; ///< The message data, or nullptr if no data is set - - /** The time which a delayed message should have fired (0 for non delayed messages) */ - double getFireTime() { - return fire_time_ms; - } - -private: - friend class TBMessageHandler; - double fire_time_ms; - TBMessageHandler *mh; -}; - -/** TBMessageHandler handles a list of pending messages posted to itself. - Messages can be delivered immediately or after a delay. - Delayed message are delivered as close as possible to the time they should fire. - Immediate messages are put on a queue and delivered as soon as possible, after any delayed - messages that has passed their delivery time. This queue is global (among all TBMessageHandlers) */ - -class TBMessageHandler { -public: - TBMessageHandler(); - virtual ~TBMessageHandler(); - - /** Posts a message to the target after a delay. - data may be nullptr if no extra data need to be sent. It will be deleted - automatically when the message is deleted. */ - bool postMessageDelayed(const TBID &message, TBMessageData *data, uint32_t delay_in_ms); - - /** Posts a message to the target at the given time (relative to TBSystem::getTimeMS()). - data may be nullptr if no extra data need to be sent. It will be deleted - automatically when the message is deleted. */ - bool postMessageOnTime(const TBID &message, TBMessageData *data, double fire_time); - - /** Posts a message to the target. - data may be nullptr if no extra data need to be sent. It will be deleted - automatically when the message is deleted. */ - bool postMessage(const TBID &message, TBMessageData *data); - - /** Check if this messagehandler has a pending message with the given id. - Returns the message if found, or nullptr. - If you want to delete the message, call DeleteMessage. */ - TBMessage *getMessageByID(const TBID &message); - - /** Delete the message from this message handler. */ - void deleteMessage(TBMessage *msg); - - /** Delete all messages from this message handler. */ - void deleteAllMessages(); - - /** Called when a message is delivered. - - This message won't be found using getMessageByID. It is already removed from the list. - You should not call DeleteMessage on this message. That is done automatically after this method exit. */ - virtual void onMessageReceived(TBMessage *msg) { - } - - // == static methods to handle the queue of messages ==================================================== - - /** Process any messages in queue. */ - static void processMessages(); - - /** Get when the time when ProcessMessages needs to be called again. - Always returns 0 if there is nondelayed messages to process, which means it needs to be called asap. - If there's only delayed messages to process, it returns the time that the earliest delayed message should be - fired. If there's no more messages to process at the moment, it returns TB_NOT_SOON (No call to ProcessMessages - is needed). */ - static double getNextMessageFireTime(); - -private: - TBLinkListOf m_messages; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_node_ref_tree.cpp b/src/modules/ui/turbobadger/tb/tb_node_ref_tree.cpp deleted file mode 100644 index ce076e253..000000000 --- a/src/modules/ui/turbobadger/tb/tb_node_ref_tree.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/** - * @file - */ - -#include "tb_node_ref_tree.h" -#include "tb_language.h" -#include "tb_system.h" - -namespace tb { - -// static -TBLinkListOf TBNodeRefTree::s_ref_trees; - -TBNodeRefTree::TBNodeRefTree(const char *name) : m_name(name), m_name_id(name) { - s_ref_trees.addLast(this); -} - -TBNodeRefTree::~TBNodeRefTree() { - s_ref_trees.remove(this); -} - -TBValue &TBNodeRefTree::getValue(const char *request) { - if (TBNode *node = m_node.getNodeFollowRef(request)) { - return node->getValue(); - } - Log::debug("TBNodeRefTree::getValue - Request not found: %s", request); - static TBValue nullval; - return nullval; -} - -// static -TBValue &TBNodeRefTree::getValueFromTree(const char *request) { - core_assert(*request == '@'); - TBNode tmp; - tmp.getValue().setString(request, TBValue::SET_AS_STATIC); - TBNode *node = TBNodeRefTree::followNodeRef(&tmp); - if (node != &tmp) { - return node->getValue(); - } - static TBValue nullval; - return nullval; -} - -void TBNodeRefTree::setValue(const char *request, const TBValue &value) { - if (TBNode *node = m_node.getNode(request, TBNode::GET_MISS_POLICY_CREATE)) { - // FIX: Only invoke the listener if it really changed. - node->getValue().copy(value); - invokeChangeListenersInternal(request); - } -} - -void TBNodeRefTree::invokeChangeListenersInternal(const char *request) { - TBLinkListOf::Iterator iter = m_listeners.iterateForward(); - while (TBNodeRefTreeListener *listener = iter.getAndStep()) { - listener->onDataChanged(this, request); - } -} - -// static -TBNodeRefTree *TBNodeRefTree::getRefTree(const char *name, int nameLen) { - for (TBNodeRefTree *rt = s_ref_trees.getFirst(); rt != nullptr; rt = rt->getNext()) { - if (SDL_strncmp(rt->getName(), name, nameLen) == 0) { - return rt; - } - } - return nullptr; -} - -// static -TBNode *TBNodeRefTree::followNodeRef(TBNode *node) { - // Detect circular loops by letting this call get a unique id. - // Update the id on each visited node and if it's already set, - // there's a loop. This cost the storage of id in each TBNode, - // and assumes the look up doesn't cause other lookups - // recursively. - // FIX: Switch to hare and teleporting tortouise? - static uint32_t s_cycle_id = 0; - uint32_t cycle_id = ++s_cycle_id; - TBNode *start_node = node; - - while (node->getValue().isString()) { - // If not a reference at all, we're done. - const char *node_str = node->getValue().getString(); - if (*node_str != '@') { - break; - } - - // If there's no tree name and request, we're done. It's probably a language string. - const char *name_start = node_str + 1; - const char *name_end = TBNode::getNextNodeSeparator(name_start); - if (*name_end == 0) { - break; - } - - TBNode *next_node = nullptr; - - // We have a "@>noderequest" string. Go ahead and do a local look up. - if (*name_start == '>') { - TBNode *local_root = node; - while (local_root->getParent() != nullptr) { - local_root = local_root->getParent(); - } - next_node = local_root->getNode(name_start + 1, TBNode::GET_MISS_POLICY_NULL); - } - // We have a "@treename>noderequest" string. Go ahead and look it up from the right node tree. - else if (TBNodeRefTree *rt = TBNodeRefTree::getRefTree(name_start, name_end - name_start)) { - next_node = rt->m_node.getNode(name_end + 1, TBNode::GET_MISS_POLICY_NULL); - } else { - Log::debug("TBNodeRefTree::ResolveNode - No tree found for request \"%s\" from node \"%s\"", node_str, - node->getValue().getString()); - break; - } - - if (next_node == nullptr) { - Log::debug("TBNodeRefTree::ResolveNode - Node not found on request \"%s\"", node_str); - break; - } - node = next_node; - - // Detect circular reference loop. - if (node->m_cycle_id != cycle_id) { - node->m_cycle_id = cycle_id; - } else { - Log::debug("TBNodeRefTree::ResolveNode - Reference loop detected on request \"%s\" from node \"%s\"", - node_str, node->getValue().getString()); - return start_node; - } - } - return node; -} - -// static -void TBNodeRefTree::resolveConditions(TBNode *parentNode) { - bool condition_ret = false; - TBNode *node = parentNode->getFirstChild(); - while (node != nullptr) { - bool delete_node = false; - bool move_children = false; - if (SDL_strcmp(node->getName(), "@if") == 0) { - condition_ret = node->getValueFollowRef().getInt() != 0; - if (condition_ret) { - move_children = true; - } - delete_node = true; - } else if (SDL_strcmp(node->getName(), "@else") == 0) { - condition_ret = !condition_ret; - if (condition_ret) { - move_children = true; - } - delete_node = true; - } - - // Make sure we'll skip any nodes added from a conditional branch. - TBNode *node_next = node->getNext(); - - if (move_children) { - // Resolve the branch first, since we'll skip it. - resolveConditions(node); - while (TBNode *content = node->getLastChild()) { - node->remove(content); - parentNode->addAfter(content, node); - } - } - - if (delete_node) { - parentNode->doDelete(node); - } else { - resolveConditions(node); - } - node = node_next; - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_node_ref_tree.h b/src/modules/ui/turbobadger/tb/tb_node_ref_tree.h deleted file mode 100644 index bb737bd45..000000000 --- a/src/modules/ui/turbobadger/tb/tb_node_ref_tree.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_id.h" -#include "tb_linklist.h" -#include "tb_node_tree.h" - -namespace tb { - -class TBNode; -class TBNodeRefTreeListener; - -/** TBNodeRefTree is a named TBNode. - Nodes under this node may be referenced from other nodes, either when - requesting a value (TBNode::getValueFollowRef), or while parsing the - node tree. While parsing, the values can be used for branch conditions - or branches of nodes can be included. */ -class TBNodeRefTree : public TBLinkOf { -public: - TBNodeRefTree(const char *name); - virtual ~TBNodeRefTree(); - - const char *getName() const { - return m_name.c_str(); - } - const TBID &getNameID() const { - return m_name_id; - } - - /** Read the data file. This will *not* invoke any change listener! */ - bool readFile(const char *filename) { - return m_node.readFile(filename); - } - void readData(const char *data) { - m_node.readData(data); - } - - /** Add a listener that is invoked on changes in this tree. */ - void addListener(TBNodeRefTreeListener *listener) { - m_listeners.addLast(listener); - } - - /** Remove a change listener from this tree. */ - void removeListener(TBNodeRefTreeListener *listener) { - m_listeners.remove(listener); - } - - /** Set the value for the given request and invoke the change listener. - Creates the nodes that doesn't exist. */ - virtual void setValue(const char *request, const TBValue &value); - - /** Get the value of the given request. Follows references if any. - Returns a null value if the request doesn't exist. */ - virtual TBValue &getValue(const char *request); - - /** Get the value of the given tree name and request (@treename>noderequest). - Returns a null value if the given tree or request doesn't exist. */ - static TBValue &getValueFromTree(const char *request); - - /** Return the tree with the given name, or nullptr if no matching tree exists. */ - static TBNodeRefTree *getRefTree(const char *name, int name_len); - - /** Go through the tree of nodes recursively and include - or remove branches depending on any conditions. */ - static void resolveConditions(TBNode *parent_node); - -private: - friend class TBNode; - friend class TBNodeTarget; - /** Follow any references to data trees and return the destination node. - If there's broken references, the node will be returned. */ - static TBNode *followNodeRef(TBNode *node); - - void invokeChangeListenersInternal(const char *request); - TBNode m_node; - core::String m_name; - TBID m_name_id; - TBLinkListOf m_listeners; - static TBLinkListOf s_ref_trees; -}; - -/** TBNodeRefTreeListener receive OnDataChanged when the - value of a node in a TBNodeRefTree is changed. - FIX: The listener can currently only listen to one tree. */ -class TBNodeRefTreeListener : public TBLinkOf { -public: - virtual ~TBNodeRefTreeListener() { - } - /** Called when the value is changed for the given node - in the given ref tree. The request is without tree name. */ - virtual void onDataChanged(TBNodeRefTree *rt, const char *request) = 0; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_node_tree.cpp b/src/modules/ui/turbobadger/tb/tb_node_tree.cpp deleted file mode 100644 index 7988050a6..000000000 --- a/src/modules/ui/turbobadger/tb/tb_node_tree.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/** - * @file - */ - -#include "tb_node_tree.h" -#include "core/Assert.h" -#include "core/StringUtil.h" -#include "tb_language.h" -#include "tb_node_ref_tree.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" - -namespace tb { - -TBNode::~TBNode() { - clear(); -} - -// static -TBNode *TBNode::create(const char *name) { - TBNode *n = new TBNode; - if ((n == nullptr) || ((n->m_name = SDL_strdup(name)) == nullptr)) { - delete n; - return nullptr; - } - return n; -} - -// static -TBNode *TBNode::create(const char *name, int nameLen) { - TBNode *n = new TBNode; - if ((n == nullptr) || ((n->m_name = (char *)SDL_malloc(nameLen + 1)) == nullptr)) { - delete n; - return nullptr; - } - SDL_memcpy(n->m_name, name, nameLen); - n->m_name[nameLen] = 0; - return n; -} - -// static -const char *TBNode::getNextNodeSeparator(const char *request) { - while (*request != 0 && *request != '>') { - request++; - } - return request; -} - -TBNode *TBNode::getNode(const char *request, GET_MISS_POLICY mp) { - // Iterate one node deeper for each sub request (non recursive) - TBNode *n = this; - while ((*request != 0) && (n != nullptr)) { - const char *nextend = getNextNodeSeparator(request); - int name_len = nextend - request; - TBNode *n_child = n->getNodeInternal(request, name_len); - if ((n_child == nullptr) && mp == GET_MISS_POLICY_CREATE) { - n_child = n->create(request, name_len); - if (n_child != nullptr) { - n->add(n_child); - } - } - n = n_child; - request = *nextend == 0 ? nextend : nextend + 1; - } - return n; -} - -TBNode *TBNode::getNodeFollowRef(const char *request, GET_MISS_POLICY mp) { - TBNode *node = getNode(request, mp); - if (node != nullptr) { - node = TBNodeRefTree::followNodeRef(node); - } - return node; -} - -TBNode *TBNode::getNodeInternal(const char *name, int nameLen) const { - for (TBNode *n = getFirstChild(); n != nullptr; n = n->getNext()) { - if (SDL_strncmp(n->m_name, name, nameLen) == 0 && n->m_name[nameLen] == 0) { - return n; - } - } - return nullptr; -} - -bool TBNode::cloneChildren(TBNode *source, bool followRefs) { - TBNode *item = source->getFirstChild(); - while (item != nullptr) { - TBNode *new_child = create(item->m_name); - if (new_child == nullptr) { - return false; - } - - new_child->m_value.copy(followRefs ? item->getValueFollowRef() : item->m_value); - add(new_child); - - if (!new_child->cloneChildren(item, followRefs)) { - return false; - } - item = item->getNext(); - } - return true; -} - -TBValue &TBNode::getValueFollowRef() { - return TBNodeRefTree::followNodeRef(this)->getValue(); -} - -int TBNode::getValueInt(const char *request, int def) { - TBNode *n = getNodeFollowRef(request); - return n != nullptr ? n->m_value.getInt() : def; -} - -float TBNode::getValueFloat(const char *request, float def) { - TBNode *n = getNodeFollowRef(request); - return n != nullptr ? n->m_value.getFloat() : def; -} - -const char *TBNode::getValueString(const char *request, const char *def) { - if (TBNode *node = getNodeFollowRef(request)) { - // We might have a language string. Those are not - // looked up in GetNode/ResolveNode. - if (node->getValue().isString()) { - const char *string = node->getValue().getString(); - if (*string == '@' && *TBNode::getNextNodeSeparator(string) == 0) { - string = g_tb_lng->getString(string + 1); - } - return string; - } - return node->getValue().getString(); - } - return def; -} - -const char *TBNode::getValueStringRaw(const char *request, const char *def) { - TBNode *n = getNodeFollowRef(request); - return n != nullptr ? n->m_value.getString() : def; -} - -class FileParser : public TBParserStream { -public: - bool read(const char *filename, TBParserTarget *target) { - f = TBFile::open(filename, TBFile::MODE_READ); - if (f == nullptr) { - return false; - } - TBParser p; - TBParser::STATUS status = p.read(this, target); - delete f; - return status == TBParser::STATUS_OK; - } - virtual int getMoreData(char *buf, int bufLen) { - return f->read(buf, 1, bufLen); - } - -private: - TBFile *f; -}; - -class DataParser : public TBParserStream { -public: - bool read(const char *data, int dataLen, TBParserTarget *target) { - m_data = data; - m_data_len = dataLen; - TBParser p; - TBParser::STATUS status = p.read(this, target); - return status == TBParser::STATUS_OK; - } - virtual int getMoreData(char *buf, int bufLen) { - const int consume = Min(bufLen, m_data_len); - SDL_memcpy(buf, m_data, consume); - m_data += consume; - m_data_len -= consume; - return consume; - } - -private: - const char *m_data; - int m_data_len; -}; - -class TBNodeTarget : public TBParserTarget { -public: - TBNodeTarget(TBNode *root, const char *filename) { - m_root_node = m_target_node = root; - m_filename = filename; - } - virtual void onError(int lineNr, const char *error) { -#ifdef TB_RUNTIME_DEBUG_INFO - Log::debug("%s(%d):Parse error: %s", m_filename, lineNr, error); -#endif // TB_RUNTIME_DEBUG_INFO - } - virtual void onComment(int lineNr, const char *comment) { - } - virtual void onToken(int lineNr, const char *name, TBValue &value) { - if (m_target_node == nullptr) { - return; - } - if (SDL_strcmp(name, "@file") == 0) { - includeFile(lineNr, value.getString()); - } else if (SDL_strcmp(name, "@include") == 0) { - includeRef(lineNr, value.getString()); - } else if (TBNode *n = TBNode::create(name)) { - n->m_value.takeOver(value); - m_target_node->add(n); - } - } - virtual void enter() { - if (m_target_node != nullptr) { - m_target_node = m_target_node->getLastChild(); - } - } - virtual void leave() { - core_assert(m_target_node != m_root_node); - if (m_target_node != nullptr) { - m_target_node = m_target_node->m_parent; - } - } - void includeFile(int lineNr, const char *filename) { - // Read the included file into a new TBNode and then - // move all the children to m_target_node. - TBTempBuffer include_filename; - include_filename.appendPath(m_filename); - include_filename.appendString(filename); - TBNode content; - if (content.readFile(include_filename.getData())) { - while (TBNode *content_n = content.getFirstChild()) { - content.remove(content_n); - m_target_node->add(content_n); - } - } else { - const core::String& err = core::string::format("Referenced file \"%s\" was not found!", include_filename.getData()); - onError(lineNr, err.c_str()); - } - } - void includeRef(int lineNr, const char *refstr) { - TBNode *refnode = nullptr; - if (*refstr == '@') { - TBNode tmp; - tmp.getValue().setString(refstr, TBValue::SET_AS_STATIC); - refnode = TBNodeRefTree::followNodeRef(&tmp); - } else // Local look-up - { - // Note: If we read to a target node that already contains - // nodes, we might look up nodes that's already there - // instead of new nodes. - refnode = m_root_node->getNode(refstr, TBNode::GET_MISS_POLICY_NULL); - - // Detect cycles - TBNode *cycle_detection = m_target_node; - while ((cycle_detection != nullptr) && (refnode != nullptr)) { - if (cycle_detection == refnode) { - refnode = nullptr; // We have a cycle, so just fail the inclusion. - } - cycle_detection = cycle_detection->getParent(); - } - } - if (refnode != nullptr) { - m_target_node->cloneChildren(refnode); - } else { - const core::String& err = core::string::format("Include \"%s\" was not found!", refstr); - onError(lineNr, err.c_str()); - } - } - -private: - TBNode *m_root_node; - TBNode *m_target_node; - const char *m_filename; -}; - -bool TBNode::readFile(const char *filename, TB_NODE_READ_FLAGS flags) { - if ((flags & TB_NODE_READ_FLAGS_APPEND) == 0U) { - clear(); - } - FileParser p; - TBNodeTarget t(this, filename); - if (p.read(filename, &t)) { - TBNodeRefTree::resolveConditions(this); - return true; - } - return false; -} - -bool TBNode::readData(const char *data, TB_NODE_READ_FLAGS flags) { - return readData(data, SDL_strlen(data), flags); -} - -bool TBNode::readData(const char *data, int dataLen, TB_NODE_READ_FLAGS flags) { - if ((flags & TB_NODE_READ_FLAGS_APPEND) == 0U) { - clear(); - } - DataParser p; - TBNodeTarget t(this, "{data}"); - if (!p.read(data, dataLen, &t)) { - return false; - } - TBNodeRefTree::resolveConditions(this); - return true; -} - -void TBNode::clear() { - SDL_free(m_name); - m_name = nullptr; - m_children.deleteAll(); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_node_tree.h b/src/modules/ui/turbobadger/tb/tb_node_tree.h deleted file mode 100644 index 31df61052..000000000 --- a/src/modules/ui/turbobadger/tb/tb_node_tree.h +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/Enum.h" -#include "parser/tb_parser.h" -#include "tb_linklist.h" - -namespace tb { - -enum TB_NODE_READ_FLAGS { - TB_NODE_READ_FLAGS_NONE = 0, - /** Read nodes without clearing first. Can be used to append - data from multiple sources, or inject dependencies. */ - TB_NODE_READ_FLAGS_APPEND = 1, -}; -CORE_ENUM_BIT_OPERATIONS(TB_NODE_READ_FLAGS); - -/** TBNode is a tree node with a string name and a value (TBValue). - It may have a parent TBNode and child TBNodes. - - Getting the value of this node or any child, may optionally follow - references to nodes in any existing TBNodeRefTree (by name). - - During ReadFile/ReadData, it may also select which branches to include - or exclude conditionally by lookup up values in TBNodeRefTree. */ -class TBNode : public TBLinkOf { -public: - TBNode() : m_name(nullptr), m_parent(nullptr), m_cycle_id(0) { - } - ~TBNode(); - - /** Create a new node with the given name. */ - static TBNode *create(const char *name); - - /** Read a tree of nodes from file into this node. Returns true on success. */ - bool readFile(const char *filename, TB_NODE_READ_FLAGS flags = TB_NODE_READ_FLAGS_NONE); - - /** Read a tree of nodes from a null terminated string buffer. */ - bool readData(const char *data, TB_NODE_READ_FLAGS flags = TB_NODE_READ_FLAGS_NONE); - - /** Read a tree of nodes from a buffer with a known length. */ - bool readData(const char *data, int data_len, TB_NODE_READ_FLAGS flags = TB_NODE_READ_FLAGS_NONE); - - /** Clear the contens of this node. */ - void clear(); - - /** Add node as child to this node. */ - void add(TBNode *n) { - m_children.addLast(n); - n->m_parent = this; - } - - /** Add node before the reference node (which must be a child to this node). */ - void addBefore(TBNode *n, TBNode *reference) { - m_children.addBefore(n, reference); - n->m_parent = this; - } - - /** Add node after the reference node (which must be a child to this node). */ - void addAfter(TBNode *n, TBNode *reference) { - m_children.addAfter(n, reference); - n->m_parent = this; - } - - /** Remove child node n from this node. */ - void remove(TBNode *n) { - m_children.remove(n); - n->m_parent = nullptr; - } - - /** Remove and delete child node n from this node. */ - void doDelete(TBNode *n) { - m_children.doDelete(n); - } - - /** Create duplicates of all items in source and add them to this node. - If follow_refs is true, any references will be followed and the final target - will be cloned instead of the ref node. - Note: Nodes does not replace existing nodes with the same name. Cloned nodes - are added after any existing nodes. */ - bool cloneChildren(TBNode *source, bool follow_refs = false); - - enum GET_MISS_POLICY { - /** GetNode will return nullptr if the node doesn't exist. */ - GET_MISS_POLICY_NULL, - /** GetNode will create all missing nodes for the request. */ - GET_MISS_POLICY_CREATE - }; - - /** Get a node from the given request. - If the node doesn't exist, it will either return nullptr or create - missing nodes, depending on the miss policy. - It can find nodes in children as well. Names are separated by a ">". - - Ex: GetNode("dishes>pizza>special>batman") */ - TBNode *getNode(const char *request, GET_MISS_POLICY mp = GET_MISS_POLICY_NULL); - - /** Returns the name of this node. */ - const char *getName() const { - return m_name; - } - - /** Returns the value of this node. */ - TBValue &getValue() { - return m_value; - } - - /** Returns the value of this node. - Will follow eventual references to TBNodeRefTree. */ - TBValue &getValueFollowRef(); - - /** Get a value from the given request as an integer. - Will follow eventual references to TBNodeRefTree. - If the value is not specified, it returns the default value (def). */ - int getValueInt(const char *request, int def); - - /** Get a value from the given request as an float. - Will follow eventual references to TBNodeRefTree. - If the value is not specified, it returns the default value (def). */ - float getValueFloat(const char *request, float def); - - /** Get a value from the given request as an string. - Will follow eventual references to TBNodeRefTree. - Will also return any referenced language string. - If the value is not specified, it returns the default value (def). */ - const char *getValueString(const char *request, const char *def); - - /** Same as getValueString, but won't look up language string references. */ - const char *getValueStringRaw(const char *request, const char *def); - - /** Get the next position in request that is a sub node separator, - or the end of the string. */ - static const char *getNextNodeSeparator(const char *request); - - inline TBNode *getParent() const { - return m_parent; - } - inline TBNode *getFirstChild() const { - return m_children.getFirst(); - } - inline TBNode *getLastChild() const { - return m_children.getLast(); - } - -private: - friend class TBNodeTarget; - friend class TBNodeRefTree; - TBNode *getNodeFollowRef(const char *request, GET_MISS_POLICY mp = GET_MISS_POLICY_NULL); - TBNode *getNodeInternal(const char *name, int name_len) const; - static TBNode *create(const char *name, int name_len); - char *m_name; - TBValue m_value; - TBLinkListOf m_children; - TBNode *m_parent; - uint32_t m_cycle_id; ///< Used to detect circular references. -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_object.h b/src/modules/ui/turbobadger/tb/tb_object.h deleted file mode 100644 index c8063c678..000000000 --- a/src/modules/ui/turbobadger/tb/tb_object.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_linklist.h" - -namespace tb { - -typedef void *TB_TYPE_ID; - -/* TBTypedObject implements custom RTTI so we can get type safe casts, - and the class name at runtime. - - Each subclass is expected to define TBOBJECT_SUBCLASS to get the - necessary implementations, instead of implementing those manually. */ -class TBTypedObject { -public: - virtual ~TBTypedObject() { - } - - /** A static template method that returns a unique id for each type. */ - template static TB_TYPE_ID getTypeId() { - static char type_id; - return &type_id; - } - - /** Returns true if the class or the base class matches the type id */ - virtual bool isOfTypeId(const TB_TYPE_ID typeId) const { - return typeId == getTypeId(); - } - - /** Returns this object as the given type or nullptr if it's not that type. */ - template T *safeCastTo() const { - return (T *)(isOfTypeId(getTypeId()) ? this : nullptr); - } - - /** Return true if this object can safely be casted to the given type. */ - template bool isOfType() const { - return static_cast(safeCastTo()); - } - - /** Get the classname of the object. */ - virtual const char *getClassName() const { - return "TBTypedObject"; - } -}; - -/** Returns the given object as the given type, or nullptr if it's not that type - or if the object is nullptr. */ -template T *TBSafeCast(TBTypedObject *obj) { - return obj ? obj->safeCastTo() : nullptr; -} - -/** Returns the given object as the given type, or nullptr if it's not that type - or if the object is nullptr. */ -template const T *TBSafeCast(const TBTypedObject *obj) { - return obj ? obj->safeCastTo() : nullptr; -} - -/** Implement the methods for safe typecasting without requiring RTTI. */ -#define TBOBJECT_SUBCLASS(clazz, baseclazz) \ - private: using Super = baseclazz; public: \ - virtual const char *getClassName() const override { \ - return #clazz; \ - } \ - virtual bool isOfTypeId(const tb::TB_TYPE_ID type_id) const override { \ - return getTypeId() == type_id ? true : baseclazz::isOfTypeId(type_id); \ - } - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_popup_window.cpp b/src/modules/ui/turbobadger/tb/tb_popup_window.cpp deleted file mode 100644 index fdd1c20d7..000000000 --- a/src/modules/ui/turbobadger/tb/tb_popup_window.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file - */ - -#include "tb_popup_window.h" -#include "tb_widgets_listener.h" - -namespace tb { - -TBRect TBPopupAlignment::getAlignedRect(TBWidget *popup, TBWidget *target) const { - TBWidget *root = target->getParentRoot(); - - SizeConstraints sc(root->getRect().w, root->getRect().h); - - PreferredSize ps = popup->getPreferredSize(sc); - - // Amount of pixels that should be avoided if the target rect needs to be moved. - int avoid_w = 0; - int avoid_h = 0; - - int x = 0; - int y = 0; - int w = Min(ps.pref_w, root->getRect().w); - int h = Min(ps.pref_h, root->getRect().h); - - if (pos_in_root.x != UNSPECIFIED && pos_in_root.y != UNSPECIFIED) { - // Position is specified in absolute root coords - x = pos_in_root.x; - y = pos_in_root.y; - avoid_w = pos_offset.x; - avoid_h = pos_offset.y; - // Make sure it's moved into view horizontally - if (align == TB_ALIGN_TOP || align == TB_ALIGN_BOTTOM) { - x = Clamp(x, 0, root->getRect().w - w); - } - } else { - target->convertToRoot(x, y); - - if (align == TB_ALIGN_TOP || align == TB_ALIGN_BOTTOM) { - if (expand_to_target_width) { - w = Max(w, target->getRect().w); - } - - // If the menu is aligned top or bottom, limit its height to the worst case available height. - // Being in the center of the root, that is half the root height minus the target rect. - h = Min(h, root->getRect().h / 2 - target->getRect().h); - } - avoid_w = target->getRect().w; - avoid_h = target->getRect().h; - } - - if (align == TB_ALIGN_BOTTOM) { - y = y + avoid_h + h > root->getRect().h ? y - h : y + avoid_h; - } else if (align == TB_ALIGN_TOP) { - y = y - h < 0 ? y + avoid_h : y - h; - } else if (align == TB_ALIGN_RIGHT) { - x = x + avoid_w + w > root->getRect().w ? x - w : x + avoid_w; - y = Min(y, root->getRect().h - h); - } else // if (align == TB_ALIGN_LEFT) - { - x = x - w < 0 ? x + avoid_w : x - w; - y = Min(y, root->getRect().h - h); - } - return TBRect(x, y, w, h); -} - -TBPopupWindow::TBPopupWindow(TBWidget *target) : m_target(target) { - TBWidgetListener::addGlobalListener(this); - - invokePointerCancel(); - setSkinBg(TBIDC("TBPopupWindow"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - setSettings(WINDOW_SETTINGS_NONE); -} - -TBPopupWindow::~TBPopupWindow() { - TBWidgetListener::removeGlobalListener(this); -} - -bool TBPopupWindow::show(const TBPopupAlignment &alignment) { - // Calculate and set a good size for the popup window - setRect(alignment.getAlignedRect(this, m_target.get())); - - TBWidget *root = m_target.get()->getParentRoot(); - root->addChild(this); - return true; -} - -bool TBPopupWindow::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_KEY_DOWN && ev.special_key == TB_KEY_ESC) { - close(); - return true; - } - return TBWindow::onEvent(ev); -} - -void TBPopupWindow::onWidgetFocusChanged(TBWidget *widget, bool focused) { - if (focused && !isEventDestinationFor(widget)) { - close(); - } -} - -bool TBPopupWindow::onWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) { - if ((ev.type == EVENT_TYPE_POINTER_DOWN || ev.type == EVENT_TYPE_CONTEXT_MENU) && - !isEventDestinationFor(ev.target)) { - close(); - } - return false; -} - -void TBPopupWindow::onWidgetDelete(TBWidget *widget) { - // If the target widget is deleted, close! - if (m_target.get() == nullptr) { - close(); - } -} - -bool TBPopupWindow::onWidgetDying(TBWidget *widget) { - // If the target widget or an ancestor of it is dying, close! - if (widget == m_target.get() || widget->isAncestorOf(m_target.get())) { - close(); - } - return false; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_popup_window.h b/src/modules/ui/turbobadger/tb/tb_popup_window.h deleted file mode 100644 index 73a11bed3..000000000 --- a/src/modules/ui/turbobadger/tb/tb_popup_window.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets_listener.h" -#include "tb_window.h" - -namespace tb { - -/** TBPopupAlignment describes the preferred alignment of a popup - relative to a target widget or a given point. - - It calculates the rect to be used to match these preferences - for any given popup and target. */ -class TBPopupAlignment { -public: - static const int UNSPECIFIED = TB_INVALID_DIMENSION; - - /** Align relative to the target widget. */ - TBPopupAlignment(TB_ALIGN align = TB_ALIGN_BOTTOM) - : pos_in_root(UNSPECIFIED, UNSPECIFIED), align(align), expand_to_target_width(true) { - } - - /** Align relative to the given position (coordinates relative to the root widget). */ - TBPopupAlignment(const TBPoint &posInRoot, TB_ALIGN align = TB_ALIGN_BOTTOM) - : pos_in_root(posInRoot), align(align), expand_to_target_width(true) { - } - - /** Align relative to the given position (coordinates relative to the root widget). - Applies an additional offset. */ - TBPopupAlignment(const TBPoint &posInRoot, const TBPoint &posOffset) - : pos_in_root(posInRoot), pos_offset(posOffset), align(TB_ALIGN_BOTTOM), expand_to_target_width(true) { - } - - /** Calculate a good rect for the given popup window using its preferred size and - the preferred alignment information stored in this class. */ - TBRect getAlignedRect(TBWidget *popup, TBWidget *target) const; - - TBPoint pos_in_root; - TBPoint pos_offset; - - TB_ALIGN align; - /** If true, the width of the popup will be at least the same as the target widget - if the alignment is TB_ALIGN_TOP or TB_ALIGN_BOTTOM. */ - bool expand_to_target_width; -}; - -/** TBPopupWindow is a popup window that redirects any child widgets events - through the given target. It will automatically close on click events that - are not sent through this popup. */ - -class TBPopupWindow : public TBWindow, private TBWidgetListener { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBPopupWindow, TBWindow); - - TBPopupWindow(TBWidget *target); - ~TBPopupWindow(); - - bool show(const TBPopupAlignment &alignment); - - virtual TBWidget *getEventDestination() const override { - return m_target.get(); - } - - virtual bool onEvent(const TBWidgetEvent &ev) override; - -private: - TBWidgetSafePointer m_target; - // TBWidgetListener - virtual void onWidgetFocusChanged(TBWidget *widget, bool focused) override; - virtual bool onWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) override; - virtual void onWidgetDelete(TBWidget *widget) override; - virtual bool onWidgetDying(TBWidget *widget) override; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_renderer.cpp b/src/modules/ui/turbobadger/tb/tb_renderer.cpp deleted file mode 100644 index cb15e0206..000000000 --- a/src/modules/ui/turbobadger/tb/tb_renderer.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file - */ - -#include "tb_renderer.h" - -namespace tb { - -void TBRenderer::invokeContextLost() { - TBLinkListOf::Iterator iter = m_listeners.iterateForward(); - while (TBRendererListener *listener = iter.getAndStep()) { - listener->onContextLost(); - } -} - -void TBRenderer::invokeContextRestored() { - TBLinkListOf::Iterator iter = m_listeners.iterateForward(); - while (TBRendererListener *listener = iter.getAndStep()) { - listener->onContextRestored(); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_renderer.h b/src/modules/ui/turbobadger/tb/tb_renderer.h deleted file mode 100644 index b0800ba2e..000000000 --- a/src/modules/ui/turbobadger/tb/tb_renderer.h +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_color.h" -#include "tb_core.h" -#include "tb_geometry.h" -#include "tb_linklist.h" -#include "video/Camera.h" - -namespace tb { - -class TBBitmapFragment; - -/** TBRendererListener is a listener for TBRenderer. */ -class TBRendererListener : public TBLinkOf { -public: - virtual ~TBRendererListener() { - } - - /** Called when the context has been lost and all TBBitmaps need to be deleted. - NOTE: Only do cleanup here. It's not safe to do work on any bitmap since the - context is already lost. */ - virtual void onContextLost() = 0; - - /** Called when the context has been restored again, and new TBBitmaps can be created - again. */ - virtual void onContextRestored() = 0; -}; - -/** TBBitmap is a minimal interface for bitmap to be painted by TBRenderer. */ - -class TBBitmap { -public: - /** Note: Implementations for batched renderers should call TBRenderer::FlushBitmap - to make sure any active batch is being flushed before the bitmap is deleted. */ - virtual ~TBBitmap() { - } - - virtual int width() = 0; - virtual int height() = 0; - - /** Update the bitmap with the given data (in BGRA32 format). - Note: Implementations for batched renderers should call TBRenderer::FlushBitmap - to make sure any active batch is being flushed before the bitmap is changed. */ - virtual void setData(uint32_t *data) = 0; -}; - -/** TBRenderer is a minimal interface for painting strings and bitmaps. */ - -class TBRenderer { -protected: - video::Camera _camera; -public: - TBRenderer() : _camera(video::CameraType::FirstPerson, video::CameraMode::Orthogonal) {} - - virtual ~TBRenderer() { - } - - /** Should be called before invoking paint on any widget. - render_target_w and render_target_h should be the size of the render target - that the renderer renders to. I.e window size, screen size or frame buffer object. */ - virtual void beginPaint(int renderTargetW, int renderTargetH) = 0; - virtual void endPaint() = 0; - - /** Translate all drawing with the given offset */ - virtual void translate(int dx, int dy) = 0; - - /** Set the current opacity that should apply to all drawing (0.f-1.f). */ - virtual void setOpacity(float opacity) = 0; - virtual float getOpacity() = 0; - - /** Set a clip rect to the renderer. add_to_current should be true when - pushing a new cliprect that should clip inside the last clip rect, - and false when restoring. - It will return the clip rect that was in use before this call. */ - virtual TBRect setClipRect(const TBRect &rect, bool addToCurrent) = 0; - - /** Get the current clip rect. Note: This may be different from the rect - sent to SetClipRect, due to intersecting with the previous cliprect! */ - virtual TBRect getClipRect() = 0; - - /** Draw the src_rect part of the fragment stretched to dst_rect. - dst_rect or src_rect can have negative width and height to achieve horizontal and vertical flip. */ - virtual void drawBitmap(const TBRect &dstRect, const TBRect &srcRect, TBBitmapFragment *bitmapFragment) = 0; - - /** Draw the src_rect part of the bitmap stretched to dst_rect. - dst_rect or src_rect can have negative width and height to achieve horizontal and vertical flip. */ - virtual void drawBitmap(const TBRect &dstRect, const TBRect &srcRect, TBBitmap *bitmap) = 0; - - /** Draw the src_rect part of the fragment stretched to dst_rect. - The bitmap will be used as a mask for the color. - dst_rect or src_rect can have negative width and height to achieve horizontal and vertical flip. */ - virtual void drawBitmapColored(const TBRect &dstRect, const TBRect &srcRect, const TBColor &color, - TBBitmapFragment *bitmapFragment) = 0; - - /** Draw the src_rect part of the bitmap stretched to dst_rect. - The bitmap will be used as a mask for the color. - dst_rect or src_rect can have negative width and height to achieve horizontal and vertical flip. */ - virtual void drawBitmapColored(const TBRect &dstRect, const TBRect &srcRect, const TBColor &color, - TBBitmap *bitmap) = 0; - - /** Draw the bitmap tiled into dst_rect. */ - virtual void drawBitmapTile(const TBRect &dstRect, TBBitmap *bitmap) = 0; - - /** Make sure the given bitmap fragment is flushed from any batching, because it may - be changed or deleted after this call. */ - virtual void flushBitmapFragment(TBBitmapFragment *bitmapFragment) = 0; - - /** Create a new TBBitmap from the given data (in BGRA32 format). - Width and height must be a power of two. - Return nullptr if fail. */ - virtual TBBitmap *createBitmap(int width, int height, uint32_t *data) = 0; - - /** Add a listener to this renderer. Does not take ownership. */ - void addListener(TBRendererListener *listener) { - m_listeners.addLast(listener); - } - - /** Remove a listener from this renderer. */ - void removeListener(TBRendererListener *listener) { - m_listeners.remove(listener); - } - - /** Invoke OnContextLost on all listeners. - Call when bitmaps should be forgotten. */ - void invokeContextLost(); - - /** Invoke OnContextRestored on all listeners. - Call when bitmaps can safely be restored. */ - void invokeContextRestored(); - - /** Defines the hint given to BeginBatchHint. */ - enum BATCH_HINT { - /** All calls are either DrawBitmap or DrawBitmapColored with the same bitmap - fragment. */ - BATCH_HINT_DRAW_BITMAP_FRAGMENT - }; - - const video::Camera& camera() const { - return _camera; - } - - /** A hint to batching renderers that the following set of draw calls are of the - same type so batching might be optimized. - The hint defines what operations are allowed between BeginBatchHint - until EndBatchHint is called. All other draw operations are invalid. - It's not valid to nest calls to BeginBatchHint. */ - virtual void beginBatchHint(BATCH_HINT hint) { - } - - /** End the hint scope started with BeginBatchHint. */ - virtual void endBatchHint() { - } - - virtual void flush() { - } - -private: - TBLinkListOf m_listeners; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_scroll_container.cpp b/src/modules/ui/turbobadger/tb/tb_scroll_container.cpp deleted file mode 100644 index a985d5bab..000000000 --- a/src/modules/ui/turbobadger/tb/tb_scroll_container.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/** - * @file - */ - -#include "tb_scroll_container.h" -#include "core/Assert.h" -#include "tb_system.h" - -namespace tb { - -TBScrollBarVisibility TBScrollBarVisibility::solve(SCROLL_MODE mode, int contentW, int contentH, int availableW, - int availableH, int scrollbarXH, int scrollbarYW) { - TBScrollBarVisibility visibility; - visibility.visible_w = availableW; - visibility.visible_h = availableH; - - if (mode == SCROLL_MODE_X_Y) { - visibility.y_on = true; - visibility.x_on = true; - visibility.visible_w -= scrollbarYW; - visibility.visible_h -= scrollbarXH; - } else if (mode == SCROLL_MODE_OFF) { - } else if (mode == SCROLL_MODE_Y) { - visibility.y_on = true; - visibility.visible_w -= scrollbarYW; - } else if (mode == SCROLL_MODE_Y_AUTO) { - if (contentH > availableH) { - visibility.y_on = true; - visibility.visible_w -= scrollbarYW; - } - } else if (mode == SCROLL_MODE_X_AUTO_Y_AUTO) { - if (contentW > visibility.visible_w) { - visibility.x_on = true; - visibility.visible_h = availableH - scrollbarXH; - } - if (contentH > visibility.visible_h) { - visibility.y_on = true; - visibility.visible_w = availableW - scrollbarYW; - } - if (contentW > visibility.visible_w) { - visibility.x_on = true; - visibility.visible_h = availableH - scrollbarXH; - } - } - return visibility; -} - -void TBScrollContainerRoot::onPaintChildren(const PaintProps &paintProps) { - // We only want clipping in one axis (the overflowing one) so we - // don't damage any expanded skins on the other axis. Add some fluff. - const int fluff = 100; - TBScrollContainer *sc = static_cast(getParent()); - TBRect clip_rect = getPaddingRect().expand( - sc->m_scrollbar_x.canScrollNegative() ? 0 : fluff, sc->m_scrollbar_y.canScrollNegative() ? 0 : fluff, - sc->m_scrollbar_x.canScrollPositive() ? 0 : fluff, sc->m_scrollbar_y.canScrollPositive() ? 0 : fluff); - - TBRect old_clip_rect = g_renderer->setClipRect(clip_rect, true); - - TB_IF_DEBUG_SETTING(LAYOUT_CLIPPING, g_tb_skin->paintRect(clip_rect, TBColor(255, 0, 0, 200), 1)); - - TBWidget::onPaintChildren(paintProps); - - g_renderer->setClipRect(old_clip_rect, false); -} - -void TBScrollContainerRoot::getChildTranslation(int &x, int &y) const { - TBScrollContainer *sc = static_cast(getParent()); - x = (int)-sc->m_scrollbar_x.getValue(); - y = (int)-sc->m_scrollbar_y.getValue(); -} - -TBScrollContainer::TBScrollContainer() { - addChild(&m_scrollbar_x); - addChild(&m_scrollbar_y); - addChild(&m_root); - m_scrollbar_y.setAxis(AXIS_Y); -} - -TBScrollContainer::~TBScrollContainer() { - removeChild(&m_root); - removeChild(&m_scrollbar_y); - removeChild(&m_scrollbar_x); -} - -void TBScrollContainer::setAdaptToContentSize(bool adapt) { - if (m_adapt_to_content_size == adapt) { - return; - } - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - m_adapt_to_content_size = adapt; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); -} - -void TBScrollContainer::setAdaptContentSize(bool adapt) { - if (m_adapt_content_size == adapt) { - return; - } - m_adapt_content_size = adapt; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBScrollContainer::setScrollMode(SCROLL_MODE mode) { - if (mode == m_mode) { - return; - } - m_mode = mode; - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); -} - -void TBScrollContainer::scrollTo(int x, int y) { - int old_x = m_scrollbar_x.getValue(); - int old_y = m_scrollbar_y.getValue(); - m_scrollbar_x.setValue(x); - m_scrollbar_y.setValue(y); - if (old_x != m_scrollbar_x.getValue() || old_y != m_scrollbar_y.getValue()) { - invalidate(); - } -} - -TBWidget::ScrollInfo TBScrollContainer::getScrollInfo() { - ScrollInfo info; - info.min_x = static_cast(m_scrollbar_x.getMinValue()); - info.min_y = static_cast(m_scrollbar_y.getMinValue()); - info.max_x = static_cast(m_scrollbar_x.getMaxValue()); - info.max_y = static_cast(m_scrollbar_y.getMaxValue()); - info.x = m_scrollbar_x.getValue(); - info.y = m_scrollbar_y.getValue(); - return info; -} - -void TBScrollContainer::invalidateLayout(INVALIDATE_LAYOUT il) { - m_layout_is_invalid = true; - // No recursion up to parents here unless we adapt to content size. - if (m_adapt_to_content_size) { - TBWidget::invalidateLayout(il); - } -} - -TBRect TBScrollContainer::getPaddingRect() { - int visible_w = getRect().w; - int visible_h = getRect().h; - if (m_scrollbar_x.getOpacity() != 0.0F) { - visible_h -= m_scrollbar_x.getPreferredSize().pref_h; - } - if (m_scrollbar_y.getOpacity() != 0.0F) { - visible_w -= m_scrollbar_y.getPreferredSize().pref_w; - } - return TBRect(0, 0, visible_w, visible_h); -} - -PreferredSize TBScrollContainer::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - PreferredSize ps; - ps.pref_w = ps.pref_h = 100; - ps.min_w = ps.min_h = 50; - if (m_adapt_to_content_size) { - if (TBWidget *content_child = m_root.getFirstChild()) { - ps = content_child->getPreferredSize(constraints); - int scrollbar_y_w = m_scrollbar_y.getPreferredSize().pref_w; - int scrollbar_x_h = m_scrollbar_x.getPreferredSize().pref_h; - - ps.pref_w += scrollbar_y_w; - ps.max_w += scrollbar_y_w; - - if (m_mode == SCROLL_MODE_X_Y || m_mode == SCROLL_MODE_X_AUTO_Y_AUTO) { - ps.pref_h += scrollbar_x_h; - ps.max_h += scrollbar_x_h; - } - } - } - return ps; -} - -bool TBScrollContainer::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_CHANGED && (ev.target == &m_scrollbar_x || ev.target == &m_scrollbar_y)) { - invalidate(); - onScroll(m_scrollbar_x.getValue(), m_scrollbar_y.getValue()); - return true; - } - if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE) { - double old_val_y = m_scrollbar_y.getValueDouble(); - m_scrollbar_y.setValueDouble(old_val_y + ev.delta_y * TBSystem::getPixelsPerLine()); - double old_val_x = m_scrollbar_x.getValueDouble(); - m_scrollbar_x.setValueDouble(old_val_x + ev.delta_x * TBSystem::getPixelsPerLine()); - return (m_scrollbar_x.getValueDouble() != old_val_x || m_scrollbar_y.getValueDouble() != old_val_y); - } - if (ev.type == EVENT_TYPE_KEY_DOWN) { - if (ev.special_key == TB_KEY_LEFT && m_scrollbar_x.canScrollNegative()) { - scrollBySmooth(-TBSystem::getPixelsPerLine(), 0); - } else if (ev.special_key == TB_KEY_RIGHT && m_scrollbar_x.canScrollPositive()) { - scrollBySmooth(TBSystem::getPixelsPerLine(), 0); - } else if (ev.special_key == TB_KEY_UP && m_scrollbar_y.canScrollNegative()) { - scrollBySmooth(0, -TBSystem::getPixelsPerLine()); - } else if (ev.special_key == TB_KEY_DOWN && m_scrollbar_y.canScrollPositive()) { - scrollBySmooth(0, TBSystem::getPixelsPerLine()); - } else if (ev.special_key == TB_KEY_PAGE_UP && m_scrollbar_y.canScrollNegative()) { - scrollBySmooth(0, -getPaddingRect().h); - } else if (ev.special_key == TB_KEY_PAGE_DOWN && m_scrollbar_y.canScrollPositive()) { - scrollBySmooth(0, getPaddingRect().h); - } else if (ev.special_key == TB_KEY_HOME) { - scrollToSmooth(m_scrollbar_x.getValue(), 0); - } else if (ev.special_key == TB_KEY_END) { - scrollToSmooth(m_scrollbar_x.getValue(), (int)m_scrollbar_y.getMaxValue()); - } else { - return false; - } - return true; - } - return false; -} - -void TBScrollContainer::onProcess() { - SizeConstraints sc(getRect().w, getRect().h); - validateLayout(sc); -} - -void TBScrollContainer::validateLayout(const SizeConstraints &constraints) { - if (!m_layout_is_invalid) { - return; - } - m_layout_is_invalid = false; - - // Layout scrollbars (no matter if they are visible or not) - int scrollbar_y_w = m_scrollbar_y.getPreferredSize().pref_w; - int scrollbar_x_h = m_scrollbar_x.getPreferredSize().pref_h; - m_scrollbar_x.setRect(TBRect(0, getRect().h - scrollbar_x_h, getRect().w - scrollbar_y_w, scrollbar_x_h)); - m_scrollbar_y.setRect(TBRect(getRect().w - scrollbar_y_w, 0, scrollbar_y_w, getRect().h)); - - if (TBWidget *content_child = m_root.getFirstChild()) { - int horizontal_padding = TBScrollBarVisibility::isAlwaysOnY(m_mode) ? scrollbar_y_w : 0; - int vertical_padding = TBScrollBarVisibility::isAlwaysOnX(m_mode) ? scrollbar_x_h : 0; - - SizeConstraints inner_sc = constraints.constrainByPadding(horizontal_padding, vertical_padding); - - PreferredSize ps = content_child->getPreferredSize(inner_sc); - - TBScrollBarVisibility visibility = TBScrollBarVisibility::solve(m_mode, ps.pref_w, ps.pref_h, getRect().w, - getRect().h, scrollbar_x_h, scrollbar_y_w); - m_scrollbar_x.setOpacity(visibility.x_on ? 1.F : 0.F); - m_scrollbar_y.setOpacity(visibility.y_on ? 1.F : 0.F); - m_root.setRect(TBRect(0, 0, visibility.visible_w, visibility.visible_h)); - - int content_w; - int content_h; - if (m_adapt_content_size) { - content_w = Max(ps.pref_w, m_root.getRect().w); - content_h = Max(ps.pref_h, m_root.getRect().h); - if (!visibility.x_on && m_root.getRect().w < ps.pref_w) { - content_w = Min(ps.pref_w, m_root.getRect().w); - } - } else { - content_w = ps.pref_w; - content_h = ps.pref_h; - } - - content_child->setRect(TBRect(0, 0, content_w, content_h)); - double limit_max_w = Max(0, content_w - m_root.getRect().w); - double limit_max_h = Max(0, content_h - m_root.getRect().h); - m_scrollbar_x.setLimits(0, limit_max_w, m_root.getRect().w); - m_scrollbar_y.setLimits(0, limit_max_h, m_root.getRect().h); - } -} - -void TBScrollContainer::onResized(int oldW, int oldH) { - invalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY); - SizeConstraints sc(getRect().w, getRect().h); - validateLayout(sc); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_scroll_container.h b/src/modules/ui/turbobadger/tb/tb_scroll_container.h deleted file mode 100644 index 620b695ef..000000000 --- a/src/modules/ui/turbobadger/tb/tb_scroll_container.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets_common.h" - -namespace tb { - -enum SCROLL_MODE { - SCROLL_MODE_X_Y, ///< X and Y always scroll-mode: xy - SCROLL_MODE_Y, ///< Y always (X never) scroll-mode: y - SCROLL_MODE_Y_AUTO, ///< Y auto (X never) scroll-mode: y-auto - SCROLL_MODE_X_AUTO_Y_AUTO, ///< X auto, Y auto scroll-mode: auto - SCROLL_MODE_OFF ///< X any Y never scroll-mode: off -}; - -/** TBScrollContainerRoot - Internal for TBScrollContainer */ -class TBScrollContainerRoot : public TBWidget { -private: // May only be used by TBScrollContainer. - friend class TBScrollContainer; - TBScrollContainerRoot() { - } - -public: - virtual void onPaintChildren(const PaintProps &paint_props) override; - virtual void getChildTranslation(int &x, int &y) const override; -}; - -/** TBScrollBarVisibility - Helper for TBScrollContainer or any other scrollable - container that needs to solve scrollbar visibility according to SCROLL_MODE. */ -class TBScrollBarVisibility { -public: - TBScrollBarVisibility() : x_on(false), y_on(false), visible_w(0), visible_h(0) { - } - - static TBScrollBarVisibility solve(SCROLL_MODE mode, int contentW, int contentH, int availableW, int availableH, - int scrollbarXH, int scrollbarYW); - static bool isAlwaysOnX(SCROLL_MODE mode) { - return mode == SCROLL_MODE_X_Y; - } - static bool isAlwaysOnY(SCROLL_MODE mode) { - return mode == SCROLL_MODE_X_Y || mode == SCROLL_MODE_Y; - } - -public: - bool x_on, y_on; - int visible_w, visible_h; -}; - -/** TBScrollContainer - A container with scrollbars that can scroll its children. */ -class TBScrollContainer : public TBWidget { - friend class TBScrollContainerRoot; - -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBScrollContainer, TBWidget); - - TBScrollContainer(); - ~TBScrollContainer(); - - /** Set to true if the preferred size of this container should adapt to the preferred - size of the content. This is disabled by default. */ - void setAdaptToContentSize(bool adapt); - bool getAdaptToContentSize() { - return m_adapt_to_content_size; - } - - /** Set to true if the content should adapt to the available size of this container - when it's larger than the preferred size. */ - void setAdaptContentSize(bool adapt); - bool getAdaptContentSize() { - return m_adapt_content_size; - } - - void setScrollMode(SCROLL_MODE mode); - SCROLL_MODE getScrollMode() { - return m_mode; - } - - virtual void scrollTo(int x, int y) override; - virtual TBWidget::ScrollInfo getScrollInfo() override; - virtual TBWidget *getScrollRoot() override { - return &m_root; - } - - virtual void invalidateLayout(INVALIDATE_LAYOUT il) override; - - virtual TBRect getPaddingRect() override; - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override; - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onProcess() override; - virtual void onResized(int oldW, int oldH) override; - - virtual TBWidget *getContentRoot() override { - return &m_root; - } - -protected: - TBScrollBar m_scrollbar_x; - TBScrollBar m_scrollbar_y; - TBScrollContainerRoot m_root; - bool m_adapt_to_content_size = false; - bool m_adapt_content_size = false; - bool m_layout_is_invalid = false; - SCROLL_MODE m_mode = SCROLL_MODE_X_Y; - void validateLayout(const SizeConstraints &constraints); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_scroller.cpp b/src/modules/ui/turbobadger/tb/tb_scroller.cpp deleted file mode 100644 index 4b9e4a03a..000000000 --- a/src/modules/ui/turbobadger/tb/tb_scroller.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/** - * @file - */ - -#include "tb_scroller.h" -#include "tb_system.h" -#include "tb_widgets.h" -#include - -namespace tb { - -// == Misc constants ==================================================================== - -#define PAN_TARGET_FPS 60 -#define PAN_MSG_DELAY_MS ((double)(1000.0 / PAN_TARGET_FPS)) - -#define PAN_START_THRESHOLD_MS 50 -#define PAN_POWER_ACC_THRESHOLD_MS 600 - -#define PAN_POWER_MULTIPLIER 1.3f - -#define SCROLL_DECAY 200.0f - -#define SF_GATE_THRESHOLD 0.01f - -// Lab: -// http://www.madtealab.com/?V=1&C=6&F=5&G=1&O=1&W=774&GW=720&GH=252&GX=13.389616776278201&GY=4.790704772336853&GS=0.13102127484993598&EH=189&a=3.6666666666666665&aMa=20&aN=OrgSpeed&bMa=3&bN=CurPos&c=8&cMa=60&cI=1&cN=FrameRate&d=16&dMa=16&dI=1&dN=numSimulatedSeconds&l=2.388888888888889&lMa=5&lN=Decay&m=0.1&mMa=0.1&mN=GateThreshold&f1=OrgSpeed+%2A+exp%28-x+%2F+Decay%29&f1N=Speed&f2=CurPos+%2B+OrgSpeed+%2A+%281-exp%28-x+%2F+Decay%29%29%2A+Decay&f2N=Pos&f3=marker%28x%2C+predictGatedPoint%29&f3N=GatePoint&f4=aToF%28simulatedPoints%2Cnearest%2C0%2CnumSimulatedSeconds%29%28x%29&f4N=Iterated&f5=OrgSpeed+%2A+x&f5N=Linear1&Expr=%0ApredictGatedPoint+%3D+-log%28GateThreshold+%2F+%28OrgSpeed%29%29+%2A+Decay%0A%0Avar+cur+%3D+OrgSpeed%0AsimulatedPoints+%3D+sample%28function%28%29+%7B%0A+++cur+%3D+cur+%2A+%281+-+0.05%29%3B%0A+++return+cur%0A+%7D%2C+%5BnumSimulatedSeconds+%2A+FrameRate%5D%29%3B%0A%0ApredictGatedPoint - -float TBScrollerFunction::getDurationFromSpeed(float startSpeed) { - float abs_start_speed = Abs(startSpeed); - if (abs_start_speed <= SF_GATE_THRESHOLD) { - return 0; - } - return -logf(SF_GATE_THRESHOLD / abs_start_speed) * m_decay; -} - -float TBScrollerFunction::getSpeedFromDistance(float distance) { - float speed = distance / m_decay; - if (distance > SF_GATE_THRESHOLD) { - return speed + SF_GATE_THRESHOLD; - } - if (distance < -SF_GATE_THRESHOLD) { - return speed - SF_GATE_THRESHOLD; - } - return speed; -} - -float TBScrollerFunction::getDistanceAtTime(float startSpeed, float elapsedTimeMs) { - core_assert(elapsedTimeMs >= 0); - return startSpeed * (1 - expf(-elapsedTimeMs / m_decay)) * m_decay; -} - -int TBScrollerFunction::getDistanceAtTimeInt(float startSpeed, float elapsedTimeMs) { - float distance = getDistanceAtTime(startSpeed, elapsedTimeMs); - return (int)(distance < 0 ? distance - 0.5F : distance + 0.5F); -} - -// == TBScroller ======================================================================== - -TBScroller::TBScroller(TBWidget *target) - : m_target(target), m_snap_listener(nullptr), m_func(SCROLL_DECAY), m_previous_pan_dx(0), m_previous_pan_dy(0), - m_scroll_start_ms(0), m_scroll_duration_x_ms(0), m_scroll_duration_y_ms(0), m_pan_power_multiplier_x(1), - m_pan_power_multiplier_y(1) { - reset(); -} - -TBScroller::~TBScroller() { -} - -void TBScroller::reset() { - m_is_started = false; - m_pan_dx = m_pan_dy = 0; - m_pan_time_ms = 0; - m_pan_delta_time_ms = 0; - m_scroll_start_speed_ppms_x = m_scroll_start_speed_ppms_y = 0; - m_scroll_start_scroll_x = m_scroll_start_scroll_y = 0; - // don't reset m_previous_pan_dx and m_previous_pan_dy here. - // don't reset m_pan_power here. It's done on start since it's needed for next pan! - m_expected_scroll_x = m_expected_scroll_y = 0; -} - -void TBScroller::onScrollBy(int dx, int dy, bool accumulative) { - if (!isStarted()) { - start(); - } - - float ppms_x = m_func.getSpeedFromDistance((float)dx); - float ppms_y = m_func.getSpeedFromDistance((float)dy); - - if (accumulative && isScrolling()) { - TBWidget::ScrollInfo info = m_target->getScrollInfo(); - // If new direction is the same as the current direction, - // calculate the speed needed for the remaining part and - // add that to the new scroll speed. - if ((ppms_x < 0) == (m_scroll_start_speed_ppms_x < 0)) { - int distance_x = m_func.getDistanceAtTimeInt(m_scroll_start_speed_ppms_x, - m_func.getDurationFromSpeed(m_scroll_start_speed_ppms_x)); - int distance_remaining_x = m_scroll_start_scroll_x + distance_x - info.x; - distance_remaining_x += m_func.getDistanceAtTimeInt(ppms_x, m_func.getDurationFromSpeed(ppms_x)); - ppms_x = m_func.getSpeedFromDistance((float)distance_remaining_x); - } - if ((ppms_y < 0) == (m_scroll_start_speed_ppms_y < 0)) { - int distance_y = m_func.getDistanceAtTimeInt(m_scroll_start_speed_ppms_y, - m_func.getDurationFromSpeed(m_scroll_start_speed_ppms_y)); - int distance_remaining_y = m_scroll_start_scroll_y + distance_y - info.y; - distance_remaining_y += m_func.getDistanceAtTimeInt(ppms_y, m_func.getDurationFromSpeed(ppms_y)); - ppms_y = m_func.getSpeedFromDistance((float)distance_remaining_y); - } - } - - adjustToSnappingAndScroll(ppms_x, ppms_y); -} - -bool TBScroller::onPan(int dx, int dy) { - if (!isStarted()) { - start(); - } - - // Pan the target - const int in_dx = dx; - const int in_dy = dy; - m_target->scrollByRecursive(dx, dy); - - // Calculate the pan speed. Smooth it out with the - // previous pan speed to reduce fluctuation a little. - double now_ms = TBSystem::getTimeMS(); - if (m_pan_time_ms != 0.0) { - if (m_pan_delta_time_ms != 0.0) { - m_pan_delta_time_ms = (now_ms - m_pan_time_ms + m_pan_delta_time_ms) / 2.0F; - } else { - m_pan_delta_time_ms = now_ms - m_pan_time_ms; - } - } - - m_pan_time_ms = now_ms; - m_pan_dx = (m_pan_dx + in_dx) / 2.0F; - m_pan_dy = (m_pan_dy + in_dy) / 2.0F; - - // If we change direction, reset the pan power multiplier in that axis. - if (m_pan_dx != 0 && (m_previous_pan_dx < 0) != (m_pan_dx < 0)) { - m_pan_power_multiplier_x = 1; - } - if (m_pan_dy != 0 && (m_previous_pan_dy < 0) != (m_pan_dy < 0)) { - m_pan_power_multiplier_y = 1; - } - m_previous_pan_dx = m_pan_dx; - m_previous_pan_dy = m_pan_dy; - - return in_dx != dx || in_dy != dy; -} - -void TBScroller::onPanReleased() { - if (TBSystem::getTimeMS() < m_pan_time_ms + PAN_START_THRESHOLD_MS) { - // Don't start scroll if we have too little speed. - // This will prevent us from scrolling accidently. - float pan_start_distance_threshold_px = 2 * TBSystem::getDPI() / 100.0F; - if (Abs(m_pan_dx) < pan_start_distance_threshold_px && Abs(m_pan_dy) < pan_start_distance_threshold_px) { - stopOrSnapScroll(); - return; - } - - if (m_pan_delta_time_ms == 0) { - stopOrSnapScroll(); - return; - } - - float ppms_x = (float)m_pan_dx / (float)m_pan_delta_time_ms; - float ppms_y = (float)m_pan_dy / (float)m_pan_delta_time_ms; - ppms_x *= m_pan_power_multiplier_x; - ppms_y *= m_pan_power_multiplier_y; - - adjustToSnappingAndScroll(ppms_x, ppms_y); - } else { - stopOrSnapScroll(); - } -} - -void TBScroller::start() { - if (isStarted()) { - return; - } - m_is_started = true; - double now_ms = TBSystem::getTimeMS(); - if (now_ms < m_scroll_start_ms + PAN_POWER_ACC_THRESHOLD_MS) { - m_pan_power_multiplier_x *= PAN_POWER_MULTIPLIER; - m_pan_power_multiplier_y *= PAN_POWER_MULTIPLIER; - } else { - m_pan_power_multiplier_x = m_pan_power_multiplier_y = 1; - } -} - -void TBScroller::stop() { - deleteAllMessages(); - reset(); -} - -bool TBScroller::stopIfAlmostStill() { - double now_ms = TBSystem::getTimeMS(); - if (now_ms > m_scroll_start_ms + (double)m_scroll_duration_x_ms && - now_ms > m_scroll_start_ms + (double)m_scroll_duration_y_ms) { - stop(); - return true; - } - return false; -} - -void TBScroller::stopOrSnapScroll() { - adjustToSnappingAndScroll(0, 0); - if (!isScrolling()) { - stop(); - } -} - -void TBScroller::adjustToSnappingAndScroll(float ppmsX, float ppmsY) { - if (m_snap_listener != nullptr) { - // Calculate the distance - int distance_x = m_func.getDistanceAtTimeInt(ppmsX, m_func.getDurationFromSpeed(ppmsX)); - int distance_y = m_func.getDistanceAtTimeInt(ppmsY, m_func.getDurationFromSpeed(ppmsY)); - - // Let the snap listener modify the distance - TBWidget::ScrollInfo info = m_target->getScrollInfo(); - int target_x = distance_x + info.x; - int target_y = distance_y + info.y; - m_snap_listener->onScrollSnap(m_target, target_x, target_y); - distance_x = target_x - info.x; - distance_y = target_y - info.y; - - // Get the start speed from the new distance - ppmsX = m_func.getSpeedFromDistance((float)distance_x); - ppmsY = m_func.getSpeedFromDistance((float)distance_y); - } - - scroll(ppmsX, ppmsY); -} - -void TBScroller::scroll(float startSpeedPpmsX, float startSpeedPpmsY) { - // Set start values - m_scroll_start_ms = TBSystem::getTimeMS(); - getTargetScrollXY(m_scroll_start_scroll_x, m_scroll_start_scroll_y); - m_scroll_start_speed_ppms_x = startSpeedPpmsX; - m_scroll_start_speed_ppms_y = startSpeedPpmsY; - - // Calculate duration for the scroll (each axis independently) - m_scroll_duration_x_ms = m_func.getDurationFromSpeed(m_scroll_start_speed_ppms_x); - m_scroll_duration_y_ms = m_func.getDurationFromSpeed(m_scroll_start_speed_ppms_y); - - if (stopIfAlmostStill()) { - return; - } - - // Post the pan message if we don't already have one - if (getMessageByID(TBIDC("scroll")) == nullptr) { - // Update expected translation - getTargetChildTranslation(m_expected_scroll_x, m_expected_scroll_y); - - postMessageDelayed(TBIDC("scroll"), nullptr, (uint32_t)PAN_MSG_DELAY_MS); - } -} - -bool TBScroller::isScrolling() { - return getMessageByID(TBIDC("scroll")) != nullptr; -} - -void TBScroller::getTargetChildTranslation(int &x, int &y) const { - int root_x = 0; - int root_y = 0; - int child_translation_x = 0; - int child_translation_y = 0; - TBWidget *scroll_root = m_target->getScrollRoot(); - scroll_root->convertToRoot(root_x, root_y); - scroll_root->getChildTranslation(child_translation_x, child_translation_y); - x = root_x + child_translation_x; - y = root_y + child_translation_y; -} - -void TBScroller::getTargetScrollXY(int &x, int &y) const { - x = 0; - y = 0; - TBWidget *tmp = m_target->getScrollRoot(); - while (tmp != nullptr) { - TBWidget::ScrollInfo info = tmp->getScrollInfo(); - x += info.x; - y += info.y; - tmp = tmp->getParent(); - } -} - -void TBScroller::onMessageReceived(TBMessage *msg) { - if (msg->message == TBIDC("scroll")) { - int actual_scroll_x = 0; - int actual_scroll_y = 0; - getTargetChildTranslation(actual_scroll_x, actual_scroll_y); - if (actual_scroll_x != m_expected_scroll_x || actual_scroll_y != m_expected_scroll_y) { - // Something else has affected the target child translation. - // This should abort the scroll. - // This could happen f.ex if something shrunk the scroll limits, - // some other action changed scroll position, or if another - // scroller started operating on a sub child that when reacing - // its scroll limit, started scrolling its chain of parents. - stop(); - return; - } - - // Calculate the time elapsed from scroll start. Clip within the - // duration for each axis. - double now_ms = TBSystem::getTimeMS(); - float elapsed_time_x = (float)(now_ms - m_scroll_start_ms); - float elapsed_time_y = elapsed_time_x; - elapsed_time_x = Min(elapsed_time_x, m_scroll_duration_x_ms); - elapsed_time_y = Min(elapsed_time_y, m_scroll_duration_y_ms); - - // Get the new scroll position from the current distance in each axis. - int scroll_x = m_func.getDistanceAtTimeInt(m_scroll_start_speed_ppms_x, elapsed_time_x); - int scroll_y = m_func.getDistanceAtTimeInt(m_scroll_start_speed_ppms_y, elapsed_time_y); - scroll_x += m_scroll_start_scroll_x; - scroll_y += m_scroll_start_scroll_y; - - // Get the scroll delta and invoke ScrollByRecursive. - int curr_scroll_x; - int curr_scroll_y; - getTargetScrollXY(curr_scroll_x, curr_scroll_y); - const int dx = scroll_x - curr_scroll_x; - const int dy = scroll_y - curr_scroll_y; - - int idx = dx; - int idy = dy; - m_target->scrollByRecursive(idx, idy); - - // Update expected translation - getTargetChildTranslation(m_expected_scroll_x, m_expected_scroll_y); - - if (((dx != 0) && actual_scroll_x == m_expected_scroll_x) && - ((dy != 0) && actual_scroll_y == m_expected_scroll_y)) { - // We didn't get anywhere despite we tried, - // so we're done (reached the end). - stop(); - return; - } - - if (!stopIfAlmostStill()) { - double next_fire_time = msg->getFireTime() + PAN_MSG_DELAY_MS; - // avoid timer catch-up if program went sleeping for a while. - next_fire_time = Max(next_fire_time, now_ms); - postMessageOnTime(TBIDC("scroll"), nullptr, next_fire_time); - } - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_scroller.h b/src/modules/ui/turbobadger/tb/tb_scroller.h deleted file mode 100644 index ddd14920a..000000000 --- a/src/modules/ui/turbobadger/tb/tb_scroller.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_msg.h" - -namespace tb { - -class TBWidget; - -/** TBScrollerFunction does the calculations of time, speed and distance - that decides how the slow down of a scroll will happen. - - Note: Speed is in pixels per millisecond. Duration is in milliseconds - and distance is in pixels. Distance and speed may be negative! */ -class TBScrollerFunction { -public: - TBScrollerFunction(float decay) : m_decay(decay) { - } - - /** Calculate the duration needed until the end distance is reached - from the given start speed. */ - float getDurationFromSpeed(float start_speed); - - /** Calculate the start speed needed to reach the given distance. */ - float getSpeedFromDistance(float distance); - - /** Calculate the distance reached at the given elapsed_time_ms with the given start_speed. */ - float getDistanceAtTime(float startSpeed, float elapsedTimeMs); - - /** Same as GetDistanceAtTime but rounded to integer. */ - int getDistanceAtTimeInt(float startSpeed, float elapsedTimeMs); - -private: - float m_decay; -}; - -/** TBScrollerSnapListener may override the target scroll position of a TBScroller. */ - -class TBScrollerSnapListener { -public: - virtual ~TBScrollerSnapListener(){}; - - /** Called when the target scroll position is calculated. - - target_widget is the widget being scroller. - target_x, target_y is the suggested target scroll position which may be changed - to something else in this call. - - Note: The scroll positions are relative to the target widget (inner scrolled TBWidget). - If there's nested scrollable widgets, only the inner scrolled widget applies snapping. */ - virtual void onScrollSnap(TBWidget *targetWidget, int &targetX, int &targetY) = 0; -}; - -/** TBScroller handles panning while the pointer is down and measure the pan - speed over time. It also handles continued scrolling when the pointer has - been released with a flick. */ -class TBScroller : private TBMessageHandler { -public: - TBScroller(TBWidget *target); - ~TBScroller(); - - /** Set the listener that may override the target scroll position. */ - void setSnapListener(TBScrollerSnapListener *listener) { - m_snap_listener = listener; - } - - /** Start tracking pan movement from calls to OnPan. */ - void start(); - - /** Stop tracking pan movement from calls to OnPan, - or stop any ongoing scrolling. */ - void stop(); - - /** Return true if the pan tracking is started or. */ - bool isStarted() const { - return m_is_started; - } - - /** Get the widget that will be panned/scrolled. Any parent of this - widget may also be panned/scrolled. */ - TBWidget *getTarget() const { - return m_target; - } - - /** Pan the target widget (or any parent) with the given deltas. - Should be called while the pointer is down. - This will track the pan speed over time. */ - bool onPan(int dx, int dy); - - /** The panning ends and the scroller should start scrolling. - Should be called when the pointer is released. */ - void onPanReleased(); - - /** Start the scroller based on the given delta. Doesn't - require previous calls to OnPan or OnPanReleased. - - If accumulative is true, the given delta will be - added to any on going scroll. If it's false, any - ongoing scroll will be canceled. */ - void onScrollBy(int dx, int dy, bool accumulative); - -private: - virtual void onMessageReceived(TBMessage *msg); - bool isScrolling(); - bool stopIfAlmostStill(); - void stopOrSnapScroll(); - void reset(); - void adjustToSnappingAndScroll(float ppmsX, float ppmsY); - void scroll(float startSpeedPpmsX, float startSpeedPpmsY); - void getTargetChildTranslation(int &x, int &y) const; - void getTargetScrollXY(int &x, int &y) const; - TBWidget *m_target; - TBScrollerSnapListener *m_snap_listener; - TBScrollerFunction m_func; - bool m_is_started; - float m_pan_dx, m_pan_dy; - float m_previous_pan_dx, m_previous_pan_dy; - double m_pan_time_ms; - double m_pan_delta_time_ms; - float m_scroll_start_speed_ppms_x, m_scroll_start_speed_ppms_y; - double m_scroll_start_ms; - float m_scroll_duration_x_ms, m_scroll_duration_y_ms; - int m_scroll_start_scroll_x, m_scroll_start_scroll_y; - float m_pan_power_multiplier_x; - float m_pan_power_multiplier_y; - int m_expected_scroll_x; - int m_expected_scroll_y; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_select.cpp b/src/modules/ui/turbobadger/tb/tb_select.cpp deleted file mode 100644 index cd7e26179..000000000 --- a/src/modules/ui/turbobadger/tb/tb_select.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/** - * @file - */ - -#include "tb_select.h" -#include "tb_language.h" -#include "tb_menu_window.h" -#include "tb_sort.h" -#include "tb_tempbuffer.h" -#include "tb_widgets_listener.h" -#include "core/StringUtil.h" - -namespace tb { - -// == Sort callback for sorting items =================================================== - -int select_list_sort_cb(TBSelectItemSource *source, const int *a, const int *b) { - const int value = SDL_strcmp(source->getItemString(*a), source->getItemString(*b)); - return source->getSort() == TB_SORT_DESCENDING ? -value : value; -} - -TBSelectList::TBSelectList() - : m_value(-1), m_list_is_invalid(false), m_scroll_to_current(false), - m_header_lng_string_id(TBIDC("TBList.header")) { - _sortCallback = select_list_sort_cb; - setSource(&m_default_source); - setIsFocusable(true); - setSkinBg(TBIDC("TBSelectList"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_container.setGravity(WIDGET_GRAVITY_ALL); - m_container.setRect(getPaddingRect()); - addChild(&m_container); - m_layout.setGravity(WIDGET_GRAVITY_ALL); - m_layout.setAxis(AXIS_Y); - m_layout.setSpacing(0); - m_layout.setLayoutPosition(LAYOUT_POSITION_LEFT_TOP); - m_layout.setLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); - m_layout.setLayoutSize(LAYOUT_SIZE_AVAILABLE); - m_container.getContentRoot()->addChild(&m_layout); - m_container.setScrollMode(SCROLL_MODE_Y_AUTO); - m_container.setAdaptContentSize(true); -} - -TBSelectList::~TBSelectList() { - m_layout.removeFromParent(); - m_container.removeFromParent(); - setSource(nullptr); -} - -void TBSelectList::onSourceChanged() { - invalidateList(); -} - -void TBSelectList::onItemChanged(int index) { - if (m_list_is_invalid) { // We're updating all widgets soon. - return; - } - - TBWidget *old_widget = getItemWidget(index); - if (old_widget == nullptr) { // We don't have this widget so we have nothing to update. - return; - } - - // Replace the old widget representing the item, with a new one. Preserve its state. - WIDGET_STATE old_state = old_widget->getStateRaw(); - - if (TBWidget *widget = createAndAddItemAfter(index, old_widget)) { - widget->setStateRaw(old_state); - } - - old_widget->removeFromParent(); - delete old_widget; -} - -void TBSelectList::onItemAdded(int index) { - if (m_list_is_invalid) { // We're updating all widgets soon. - return; - } - - // Sorting, filtering etc. makes it messy to handle dynamic addition of items. - // Resort to invalidate the entire list (may even be faster anyway) - invalidateList(); -} - -void TBSelectList::onItemRemoved(int index) { - if (m_list_is_invalid) { // We're updating all widgets soon. - return; - } - - // Sorting, filtering etc. makes it messy to handle dynamic addition of items. - // Resort to invalidate the entire list (may even be faster anyway) - invalidateList(); -} - -void TBSelectList::onAllItemsRemoved() { - invalidateList(); - m_value = -1; -} - -void TBSelectList::setFilter(const char *filter) { - core::String new_filter; - if ((filter != nullptr) && (*filter != 0)) { - new_filter = filter; - } - if (m_filter == new_filter) { - return; - } - m_filter = new_filter; - invalidateList(); -} - -void TBSelectList::setHeaderString(const TBID &id) { - if (m_header_lng_string_id == id) { - return; - } - m_header_lng_string_id = id; - invalidateList(); -} - -void TBSelectList::invalidateList() { - if (m_list_is_invalid) { - return; - } - m_list_is_invalid = true; - invalidate(); -} - -void TBSelectList::validateList() { - if (!m_list_is_invalid) { - return; - } - m_list_is_invalid = false; - // FIX: Could delete and create only the changed items (faster filter change) - - // Remove old items - while (TBWidget *child = m_layout.getContentRoot()->getFirstChild()) { - child->removeFromParent(); - delete child; - } - if ((m_source == nullptr) || (m_source->getNumItems() == 0)) { - return; - } - - // Create a sorted list of the items we should include using the current filter. - TBTempBuffer sort_buf; - if (!sort_buf.reserve(m_source->getNumItems() * sizeof(int))) { - return; // Out of memory - } - int *sorted_index = (int *)sort_buf.getData(); - - // Populate the sorted index list - int num_sorted_items = 0; - for (int i = 0; i < m_source->getNumItems(); i++) { - if (m_filter.empty() || m_source->filter(i, m_filter)) { - sorted_index[num_sorted_items++] = i; - } - } - - // Sort - if (m_source->getSort() != TB_SORT_NONE) { - insertion_sort(sorted_index, num_sorted_items, m_source, _sortCallback); - } - - // Show header if we only show a subset of all items. - if (!m_filter.empty()) { - if (TBWidget *widget = new TBTextField()) { - const core::String& str = core::string::format(g_tb_lng->getString(m_header_lng_string_id), num_sorted_items, m_source->getNumItems()); - widget->setText(str); - widget->setSkinBg(TBIDC("TBList.header")); - widget->setState(WIDGET_STATE_DISABLED, true); - widget->setGravity(WIDGET_GRAVITY_ALL); - widget->data.setInt(-1); - m_layout.getContentRoot()->addChild(widget); - } - } - - // Create new items - for (int i = 0; i < num_sorted_items; i++) { - createAndAddItemAfter(sorted_index[i], nullptr); - } - - selectItem(m_value, true); - - // FIX: Should not scroll just because we update the list. Only automatically first time! - m_scroll_to_current = true; -} - -TBWidget *TBSelectList::createAndAddItemAfter(int index, TBWidget *reference) { - if (TBWidget *widget = m_source->createItemWidget(index, this)) { - // Use item data as widget to index lookup - widget->data.setInt(index); - m_layout.getContentRoot()->addChildRelative(widget, WIDGET_Z_REL_AFTER, reference); - return widget; - } - return nullptr; -} - -void TBSelectList::setValue(int value) { - if (value == m_value) { - return; - } - - selectItem(m_value, false); - m_value = value; - selectItem(m_value, true); - scrollToSelectedItem(); - - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - if (TBWidget *widget = getItemWidget(m_value)) { - ev.ref_id = widget->getID(); - } - invokeEvent(ev); -} - -TBID TBSelectList::getSelectedItemID() const { - if ((m_source != nullptr) && m_value >= 0 && m_value < m_source->getNumItems()) { - return m_source->getItemID(m_value); - } - return TBID(); -} - -void TBSelectList::selectItem(int index, bool selected) { - if (TBWidget *widget = getItemWidget(index)) { - widget->setState(WIDGET_STATE_SELECTED, selected); - } -} - -TBWidget *TBSelectList::getItemWidget(int index) { - if (index == -1) { - return nullptr; - } - for (TBWidget *tmp = m_layout.getContentRoot()->getFirstChild(); tmp != nullptr; tmp = tmp->getNext()) { - if (tmp->data.getInt() == index) { - return tmp; - } - } - return nullptr; -} - -void TBSelectList::scrollToSelectedItem() { - if (m_list_is_invalid) { - m_scroll_to_current = true; - return; - } - m_scroll_to_current = false; - if (TBWidget *widget = getItemWidget(m_value)) { - m_container.scrollIntoView(widget->getRect()); - } else { - m_container.scrollTo(0, 0); - } -} - -void TBSelectList::onSkinChanged() { - m_container.setRect(getPaddingRect()); -} - -void TBSelectList::onProcess() { - validateList(); -} - -void TBSelectList::onProcessAfterChildren() { - if (m_scroll_to_current) { - scrollToSelectedItem(); - } -} - -bool TBSelectList::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_CLICK && ev.target->getParent() == m_layout.getContentRoot()) { - // setValue (EVENT_TYPE_CHANGED) might cause something to delete this (f.ex closing - // the dropdown menu. We want to sent another event, so ensure we're still around. - TBWidgetSafePointer this_widget(this); - - int index = ev.target->data.getInt(); - setValue(index); - - // If we're still around, invoke the click event too. - if (this_widget.get() != nullptr) { - TBSelectList *target_list = this; - // If the parent window is a TBMenuWindow, we will iterate up the event destination - // chain to find the top TBMenuWindow and invoke the event there. - // That way events in submenus will reach the caller properly, and seem like it was - // invoked on the top menu. - TBWindow *window = getParentWindow(); - while (TBMenuWindow *menu_win = TBSafeCast(window)) { - target_list = menu_win->getList(); - window = menu_win->getEventDestination()->getParentWindow(); - } - - // Invoke the click event on the target list - TBWidgetEvent ev(EVENT_TYPE_CLICK); - if (TBWidget *widget = getItemWidget(m_value)) { - ev.ref_id = widget->getID(); - } - target_list->invokeEvent(ev); - } - return true; - } - if (ev.type == EVENT_TYPE_KEY_DOWN) { - if (changeValue(ev.special_key)) { - return true; - } - - // Give the scroll container a chance to handle the key so it may - // scroll. This matters if the list itself is focused instead of - // some child view of any select item (since that would have passed - // the container already) - if (getScrollContainer()->onEvent(ev)) { - return true; - } - } - return false; -} - -bool TBSelectList::changeValue(SPECIAL_KEY key) { - if ((m_source == nullptr) || (m_layout.getContentRoot()->getFirstChild() == nullptr)) { - return false; - } - - bool forward; - if (key == TB_KEY_HOME || key == TB_KEY_DOWN) { - forward = true; - } else if (key == TB_KEY_END || key == TB_KEY_UP) { - forward = false; - } else { - return false; - } - - TBWidget *item_root = m_layout.getContentRoot(); - TBWidget *current = getItemWidget(m_value); - TBWidget *origin = nullptr; - if (key == TB_KEY_HOME || ((current == nullptr) && key == TB_KEY_DOWN)) { - current = item_root->getFirstChild(); - } else if (key == TB_KEY_END || ((current == nullptr) && key == TB_KEY_UP)) { - current = item_root->getLastChild(); - } else { - origin = current; - } - - while (current != nullptr) { - if (current != origin && !current->getDisabled()) { - break; - } - current = forward ? current->getNext() : current->getPrev(); - } - // Select and focus what we found - if (current != nullptr) { - setValue(current->data.getInt()); - return true; - } - return false; -} - -// == TBSelectDropdown ========================================== - -TBSelectDropdown::TBSelectDropdown() : m_value(-1) { - setSource(&m_default_source); - setSkinBg(TBIDC("TBSelectDropdown"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_arrow.setSkinBg(TBIDC("TBSelectDropdown.arrow"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - getContentRoot()->addChild(&m_arrow); -} - -TBSelectDropdown::~TBSelectDropdown() { - getContentRoot()->removeChild(&m_arrow); - setSource(nullptr); - closeWindow(); -} - -void TBSelectDropdown::onSourceChanged() { - m_value = -1; - if ((m_source != nullptr) && (m_source->getNumItems() != 0)) { - setValue(0); - } -} - -void TBSelectDropdown::onItemChanged(int index) { -} - -void TBSelectDropdown::setValue(int value) { - if (value == m_value || (m_source == nullptr)) { - return; - } - m_value = value; - - if (m_value < 0) { - setText(""); - } else if (m_value < m_source->getNumItems()) { - setText(m_source->getItemString(m_value)); - } - - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); -} - -TBID TBSelectDropdown::getSelectedItemID() const { - if ((m_source != nullptr) && m_value >= 0 && m_value < m_source->getNumItems()) { - return m_source->getItemID(m_value); - } - return TBID(); -} - -void TBSelectDropdown::openWindow() { - if ((m_source == nullptr) || (m_source->getNumItems() == 0) || (m_window_pointer.get() != nullptr)) { - return; - } - - if (TBMenuWindow *window = new TBMenuWindow(this, TBIDC("TBSelectDropdown.window"))) { - m_window_pointer.set(window); - window->setSkinBg(TBIDC("TBSelectDropdown.window")); - window->show(m_source, TBPopupAlignment(), getValue()); - } -} - -void TBSelectDropdown::closeWindow() { - if (TBMenuWindow *window = getMenuIfOpen()) { - window->close(); - } -} - -TBMenuWindow *TBSelectDropdown::getMenuIfOpen() const { - return TBSafeCast(m_window_pointer.get()); -} - -bool TBSelectDropdown::onEvent(const TBWidgetEvent &ev) { - if (ev.target == this && ev.type == EVENT_TYPE_CLICK) { - // Open the menu, or set the value and close it if already open (this will - // happen when clicking by keyboard since that will call click on this button) - if (TBMenuWindow *menu_window = getMenuIfOpen()) { - TBWidgetSafePointer tmp(this); - int value = menu_window->getList()->getValue(); - menu_window->die(); - if (tmp.get() != nullptr) { - setValue(value); - } - } else { - openWindow(); - } - return true; - } - if (ev.target->getID() == TBIDC("TBSelectDropdown.window") && ev.type == EVENT_TYPE_CLICK) { - // Set the value of the clicked item - if (TBMenuWindow *menu_window = getMenuIfOpen()) { - setValue(menu_window->getList()->getValue()); - } - return true; - } - if (ev.target == this && (m_source != nullptr) && ev.isKeyEvent()) { - if (TBMenuWindow *menu_window = getMenuIfOpen()) { - // Redirect the key strokes to the list - TBWidgetEvent redirected_ev(ev); - return menu_window->getList()->invokeEvent(redirected_ev); - } - } - return false; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_select.h b/src/modules/ui/turbobadger/tb/tb_select.h deleted file mode 100644 index d7a70abe1..000000000 --- a/src/modules/ui/turbobadger/tb/tb_select.h +++ /dev/null @@ -1,184 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_scroll_container.h" -#include "tb_select_item.h" -#include "tb_window.h" - -namespace tb { - -class TBMenuWindow; - -/** TBSelectList shows a scrollable list of items provided by a TBSelectItemSource. */ - -class TBSelectList : public TBWidget, public TBSelectItemViewer { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSelectList, TBWidget); - - TBSelectList(); - ~TBSelectList(); - - /** Get the default item source for this widget. This source can be used to add - items of type TBGenericStringItem to this widget. - It is the item source that is fed from resource files. - - If you need to add other types of items, or if you want to share item sources - between several TBSelectDropDown/TBSelectList widgets, use SetSource using a - external item source. */ - TBGenericStringItemSource *getDefaultSource() { - return &m_default_source; - } - - /** Set filter string so only matching items will be showed. - Set nullptr or empty string to remove filter and show all items. */ - void setFilter(const core::String& filter) { - setFilter(filter.c_str()); - } - void setFilter(const char *filter); - const char *getFilter() const { - return m_filter.c_str(); - } - - /** Set the language string id for the header. The header is shown - at the top of the list when only a subset of all items are shown. */ - void setHeaderString(const TBID &id); - - /** Make the list update its items to reflect the items from the - in the current source. The update will take place next time - the list is validated. */ - void invalidateList(); - - /** Make sure the list is reflecting the current items in the source. */ - void validateList(); - - /** The value is the selected item. In lists with multiple selectable - items it's the item that is the current focus. */ - virtual void setValue(int value) override; - virtual int getValue() const override { - return m_value; - } - - /** Get the ID of the selected item, or 0 if there is no item selected. */ - TBID getSelectedItemID() const; - - /** Change the value to a non disabled item that is visible with the current - filter. Returns true if it successfully found another item. - Valid keys: - TB_KEY_UP - Previous item. - TB_KEY_DOWN - Next item. - TB_KEY_HOME - First item. - TB_KEY_END - Last item. */ - bool changeValue(SPECIAL_KEY key); - - /** Set the selected state of the item at the given index. If you want - to unselect the previously selected item, use setValue. */ - void selectItem(int index, bool selected); - TBWidget *getItemWidget(int index); - - /** Scroll to the current selected item. The scroll may be delayed until - the items has been layouted if the layout is currently invalid. */ - void scrollToSelectedItem(); - - /** Return the scrollcontainer used in this list. */ - TBScrollContainer *getScrollContainer() { - return &m_container; - } - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual void onSkinChanged() override; - virtual void onProcess() override; - virtual void onProcessAfterChildren() override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - - // == TBSelectItemViewer ================================================== - virtual void onSourceChanged() override; - virtual void onItemChanged(int index) override; - virtual void onItemAdded(int index) override; - virtual void onItemRemoved(int index) override; - virtual void onAllItemsRemoved() override; - - inline void setSortCallback(int (*func)(TBSelectItemSource *source, const int *a, const int *b)) { - _sortCallback = func; - } - -protected: - TBScrollContainer m_container; - TBLayout m_layout; - TBGenericStringItemSource m_default_source; - int m_value; - core::String m_filter; - bool m_list_is_invalid; - bool m_scroll_to_current; - TBID m_header_lng_string_id; - - int (*_sortCallback)(TBSelectItemSource *source, const int *a, const int *b); - -private: - TBWidget *createAndAddItemAfter(int index, TBWidget *reference); -}; - -/** TBSelectDropdown shows a button that opens a popup with a TBSelectList with items - provided by a TBSelectItemSource. */ - -class TBSelectDropdown : public TBButton, public TBSelectItemViewer { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSelectDropdown, TBButton); - - TBSelectDropdown(); - ~TBSelectDropdown(); - - /** Get the default item source for this widget. This source can be used to add - items of type TBGenericStringItem to this widget. - It is the item source that is fed from resource files. - - If you need to add other types of items, or if you want to share item sources - between several TBSelectDropDown/TBSelectList widgets, use SetSource using a - external item source. */ - TBGenericStringItemSource *getDefaultSource() { - return &m_default_source; - } - - /** Set the selected item. */ - virtual void setValue(int value) override; - virtual int getValue() const override { - return m_value; - } - - /** Get the ID of the selected item, or 0 if there is no item selected. */ - TBID getSelectedItemID() const; - - /** Open the window if the model has items. */ - void openWindow(); - - /** Close the window if it is open. */ - void closeWindow(); - - /** Return the menu window if it's open, or nullptr. */ - TBMenuWindow *getMenuIfOpen() const; - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - - // == TBSelectItemViewer ================================================== - virtual void onSourceChanged() override; - virtual void onItemChanged(int index) override; - virtual void onItemAdded(int index) override { - } - virtual void onItemRemoved(int index) override { - } - virtual void onAllItemsRemoved() override { - } - -protected: - TBGenericStringItemSource m_default_source; - TBSkinImage m_arrow; - int m_value; - TBWidgetSafePointer m_window_pointer; ///< Points to the dropdown window if opened -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_select_item.cpp b/src/modules/ui/turbobadger/tb/tb_select_item.cpp deleted file mode 100644 index 57293034f..000000000 --- a/src/modules/ui/turbobadger/tb/tb_select_item.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @file - */ - -#include "core/Assert.h" -#include "tb_language.h" -#include "tb_menu_window.h" -#include "tb_select.h" -#include "tb_widgets_listener.h" -#include - -namespace tb { - -/** TBSimpleLayoutItemWidget is a item containing a layout with the following: - -TBSkinImage showing the item image. - -TBTextField showing the item string. - -TBSkinImage showing the arrow for items with a submenu. - It also handles submenu events. */ -class TBSimpleLayoutItemWidget : public TBLayout, private TBWidgetListener { -public: - TBSimpleLayoutItemWidget(const TBID &image, TBSelectItemSource *source, const char *str); - ~TBSimpleLayoutItemWidget(); - virtual bool onEvent(const TBWidgetEvent &ev); - -private: - TBSelectItemSource *m_source; - TBTextField m_textfield; - TBSkinImage m_image; - TBSkinImage m_image_arrow; - TBMenuWindow *m_menu; ///< Points to the submenu window if opened - virtual void onWidgetDelete(TBWidget *widget); - void openSubMenu(); - void closeSubMenu(); -}; - -TBSimpleLayoutItemWidget::TBSimpleLayoutItemWidget(const TBID &image, TBSelectItemSource *source, const char *str) - : m_source(source), m_menu(nullptr) { - setSkinBg(TBIDC("TBSelectItem")); - setLayoutDistribution(LAYOUT_DISTRIBUTION_AVAILABLE); - setPaintOverflowFadeout(false); - - if (image != 0U) { - m_image.setSkinBg(image); - m_image.setIgnoreInput(true); - addChild(&m_image); - } - - m_textfield.setText(str); - m_textfield.setTextAlign(TB_TEXT_ALIGN_LEFT); - m_textfield.setIgnoreInput(true); - addChild(&m_textfield); - - if (source != nullptr) { - m_image_arrow.setSkinBg(TBIDC("arrow.right")); - m_image_arrow.setIgnoreInput(true); - addChild(&m_image_arrow); - } -} - -TBSimpleLayoutItemWidget::~TBSimpleLayoutItemWidget() { - m_image_arrow.removeFromParent(); - m_textfield.removeFromParent(); - m_image.removeFromParent(); - closeSubMenu(); -} - -bool TBSimpleLayoutItemWidget::onEvent(const TBWidgetEvent &ev) { - if ((m_source != nullptr) && ev.type == EVENT_TYPE_CLICK && ev.target == this) { - openSubMenu(); - return true; - } - return false; -} - -void TBSimpleLayoutItemWidget::onWidgetDelete(TBWidget *widget) { - core_assert(widget == m_menu); - closeSubMenu(); -} - -void TBSimpleLayoutItemWidget::openSubMenu() { - if (m_menu != nullptr) { - return; - } - - // Open a new menu window for the submenu with this widget as target - m_menu = new TBMenuWindow(this, TBIDC("submenu")); - if (m_menu != nullptr) { - setState(WIDGET_STATE_SELECTED, true); - m_menu->addListener(this); - m_menu->show(m_source, TBPopupAlignment(TB_ALIGN_RIGHT), -1); - } -} - -void TBSimpleLayoutItemWidget::closeSubMenu() { - if (m_menu == nullptr) { - return; - } - - setState(WIDGET_STATE_SELECTED, false); - m_menu->removeListener(this); - if (!m_menu->getIsDying()) { - m_menu->close(); - } - m_menu = nullptr; -} - -void TBSelectItemViewer::setSource(TBSelectItemSource *source) { - if (m_source == source) { - return; - } - - if (m_source != nullptr) { - m_source->m_viewers.remove(this); - } - m_source = source; - if (m_source != nullptr) { - m_source->m_viewers.addLast(this); - } - - onSourceChanged(); -} - -TBSelectItemSource::~TBSelectItemSource() { - // If this core_assert trig, you are deleting a model that's still set on some - // TBSelect widget. That might be dangerous. - core_assert(!m_viewers.hasLinks()); -} - -bool TBSelectItemSource::filter(int index, const char *filter) { - const char *str = getItemString(index); - return (str != nullptr) && (stristr(str, filter) != nullptr); -} - -TBWidget *TBSelectItemSource::createItemWidget(int index, TBSelectItemViewer *viewer) { - const char *string = getItemString(index); - TBSelectItemSource *sub_source = getItemSubSource(index); - TBID image = getItemImage(index); - if ((sub_source != nullptr) || (image != 0U)) { - if (TBSimpleLayoutItemWidget *itemwidget = new TBSimpleLayoutItemWidget(image, sub_source, string)) { - return itemwidget; - } - } else if ((string != nullptr) && *string == '-') { - if (TBSeparator *separator = new TBSeparator) { - separator->setGravity(WIDGET_GRAVITY_ALL); - separator->setSkinBg(TBIDC("TBSelectItem.separator")); - return separator; - } - } else if (TBTextField *textfield = new TBTextField) { - textfield->setSkinBg("TBSelectItem"); - textfield->setText(string); - textfield->setTextAlign(TB_TEXT_ALIGN_LEFT); - return textfield; - } - return nullptr; -} - -void TBSelectItemSource::invokeItemChanged(int index, TBSelectItemViewer *excludeViewer) { - TBLinkListOf::Iterator iter = m_viewers.iterateForward(); - while (TBSelectItemViewer *viewer = iter.getAndStep()) { - if (viewer != excludeViewer) { - viewer->onItemChanged(index); - } - } -} - -void TBSelectItemSource::invokeItemAdded(int index) { - TBLinkListOf::Iterator iter = m_viewers.iterateForward(); - while (TBSelectItemViewer *viewer = iter.getAndStep()) { - viewer->onItemAdded(index); - } -} - -void TBSelectItemSource::invokeItemRemoved(int index) { - TBLinkListOf::Iterator iter = m_viewers.iterateForward(); - while (TBSelectItemViewer *viewer = iter.getAndStep()) { - viewer->onItemRemoved(index); - } -} - -void TBSelectItemSource::invokeAllItemsRemoved() { - TBLinkListOf::Iterator iter = m_viewers.iterateForward(); - while (TBSelectItemViewer *viewer = iter.getAndStep()) { - viewer->onAllItemsRemoved(); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_select_item.h b/src/modules/ui/turbobadger/tb/tb_select_item.h deleted file mode 100644 index edb7ef344..000000000 --- a/src/modules/ui/turbobadger/tb/tb_select_item.h +++ /dev/null @@ -1,273 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/String.h" -#include "tb_linklist.h" -#include "tb_list.h" -#include "tb_value.h" - -namespace tb { - -class TBSelectItemSource; - -enum TB_SORT { - TB_SORT_NONE, ///< No sorting. Items appear in list order. - TB_SORT_ASCENDING, ///< Ascending sort. - TB_SORT_DESCENDING ///< Descending sort. -}; - -/** TBSelectItemViewer is the viewer for items provided by TBSelectItemSource. - There can be multiple viewers for each source. The viewer will recieve - callbacks when the source is changed, so it can update itself. -*/ -class TBSelectItemViewer : public TBLinkOf { -public: - TBSelectItemViewer() : m_source(nullptr) { - } - virtual ~TBSelectItemViewer() { - } - - /** Set the source which should provide the items for this viewer. - This source needs to live longer than this viewer. - Set nullptr to unset currently set source. */ - void setSource(TBSelectItemSource *source); - TBSelectItemSource *getSource() const { - return m_source; - } - - /** Called when the source has changed or been unset by calling SetSource. */ - virtual void onSourceChanged() = 0; - - /** Called when the item at the given index has changed in a way that should - update the viewer. */ - virtual void onItemChanged(int index) = 0; - - /** Called when the item at the given index has been added. */ - virtual void onItemAdded(int index) = 0; - - /** Called when the item at the given index has been removed. */ - virtual void onItemRemoved(int index) = 0; - - /** Called when all items have been removed. */ - virtual void onAllItemsRemoved() = 0; - -protected: - TBSelectItemSource *m_source; -}; - -/** TBSelectItemSource is a item provider interface for list widgets (TBSelectList and - TBSelectDropdown). - - Instead of feeding all list widgets with all items all the time, the list widgets - will ask TBSelectItemSource when it needs it. The list widgets may also apply - filtering so only a subset of all the items are shown. - - CreateItemWidget can be overridden to create any set of widget content for each item. - - This class has no storage of items. If you want an array storage of items, - use the subclass TBSelectItemSourceList. If you implement your own storage, - remember to call InvokeItem[Added/...] to notify viewers that they need to update. -*/ -class TBSelectItemSource { -public: - TBSelectItemSource() : m_sort(TB_SORT_NONE) { - } - virtual ~TBSelectItemSource(); - - /** Return true if an item matches the given filter text. - By default, it returns true if GetItemString contains filter. */ - virtual bool filter(int index, const char *filter); - - bool filter(int index, const core::String& filterStr) { - return filter(index, filterStr.c_str()); - } - - /** Get the string of an item. If an item has more than one string, - return the one that should be used for inline-find (pressing keys - in the list will scroll to the item starting with the same letters), - and for sorting the list. */ - virtual const char *getItemString(int index) const = 0; - - /** Get the source to be used if this item should open a sub menu. */ - virtual TBSelectItemSource *getItemSubSource(int index) { - return nullptr; - } - - /** Get the skin image to be painted before the text for this item. */ - virtual TBID getItemImage(int /*index*/) const { - return TBID(); - } - - /** Get the id of the item. */ - virtual TBID getItemID(int /*index*/) const { - return TBID(); - } - - /** Create the item representation widget(s). By default, it will create - a TBTextField for string-only items, and other types for items that - also has image or submenu. */ - virtual TBWidget *createItemWidget(int index, TBSelectItemViewer *viewer); - - /** Get the number of items */ - virtual int getNumItems() const = 0; - - /** Set sort type. Default is TB_SORT_NONE. */ - void setSort(TB_SORT sort) { - m_sort = sort; - } - TB_SORT getSort() const { - return m_sort; - } - - /** Invoke onItemChanged on all open viewers for this source. */ - void invokeItemChanged(int index, TBSelectItemViewer *exclude_viewer = nullptr); - void invokeItemAdded(int index); - void invokeItemRemoved(int index); - void invokeAllItemsRemoved(); - -private: - friend class TBSelectItemViewer; - TBLinkListOf m_viewers; - TB_SORT m_sort; -}; - -/** TBSelectItemSourceList is an item provider for list widgets (TBSelectList and - TBSelectDropdown). It stores items of the type specified by the template in an array. */ -template class TBSelectItemSourceList : public TBSelectItemSource { -public: - TBSelectItemSourceList() { - } - virtual ~TBSelectItemSourceList() { - deleteAllItems(); - } - virtual const char *getItemString(int index) const { - return getItem(index)->str.c_str(); - } - virtual TBSelectItemSource *getItemSubSource(int index) { - return getItem(index)->sub_source; - } - virtual TBID getItemImage(int index) const { - return getItem(index)->skin_image; - } - virtual TBID getItemID(int index) const { - return getItem(index)->id; - } - virtual int getNumItems() const { - return m_items.getNumItems(); - } - virtual TBWidget *createItemWidget(int index, TBSelectItemViewer *viewer) { - if (TBWidget *widget = TBSelectItemSource::createItemWidget(index, viewer)) { - T *item = m_items[index]; - widget->setID(item->id); - return widget; - } - return nullptr; - } - - inline int getItemIndex(const T *item) const { - return m_items.find(item); - } - - inline bool isFirst(const T *item) const { - const int idx = getItemIndex(item); - return idx == 0; - } - - inline bool isLast(const T *item) const { - const int idx = getItemIndex(item); - if (idx == -1) { - return false; - } - return idx == getNumItems() - 1; - } - - /** Add a new item at the given index. */ - bool addItem(T *item, int index) { - if (m_items.add(item, index)) { - invokeItemAdded(index); - return true; - } - return false; - } - - /** Add a new item last. */ - bool addItem(T *item) { - return addItem(item, m_items.getNumItems()); - } - - void swap(int idx1, int idx2, TBSelectItemViewer *exclude_viewer = nullptr) { - m_items.swap(idx1, idx2); - invokeItemChanged(idx1, exclude_viewer); - invokeItemChanged(idx2, exclude_viewer); - } - - /** Get the item at the given index. */ - T *getItem(int index) const { - return m_items[index]; - } - - /** Delete the item at the given index. */ - void deleteItem(int index) { - if (!m_items.getNumItems()) { - return; - } - m_items.doDelete(index); - invokeItemRemoved(index); - } - - /** Delete all items. */ - void deleteAllItems() { - if (!m_items.getNumItems()) { - return; - } - m_items.deleteAll(); - invokeAllItemsRemoved(); - } - -private: - TBListOf m_items; -}; - -/** TBGenericStringItem item for TBGenericStringItemSource. - It has a string and may have a skin image and sub item source. */ -class TBGenericStringItem { -public: - TBGenericStringItem(const TBGenericStringItem &other) - : str(other.str), id(other.id), sub_source(other.sub_source), tag(other.tag) { - } - TBGenericStringItem(const char *str) : str(str), sub_source(nullptr) { - } - TBGenericStringItem(const char *str, TBID id) : str(str), id(id), sub_source(nullptr) { - } - TBGenericStringItem(const char *str, TBSelectItemSource *subSource) : str(str), sub_source(subSource) { - } - const TBGenericStringItem &operator=(const TBGenericStringItem &other) { - str = other.str; - id = other.id; - sub_source = other.sub_source; - tag = other.tag; - return *this; - } - - void setSkinImage(const TBID &image) { - skin_image = image; - } - -public: - core::String str; - TBID id; - TBID skin_image; - TBSelectItemSource *sub_source; - - /** This value is free to use for anything. It's not used internally. */ - TBValue tag; -}; - -/** TBGenericStringItemSource is an item source list providing items of type TBGenericStringItem. */ - -class TBGenericStringItemSource : public TBSelectItemSourceList {}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_skin.cpp b/src/modules/ui/turbobadger/tb/tb_skin.cpp deleted file mode 100644 index 0e3e0dfd5..000000000 --- a/src/modules/ui/turbobadger/tb/tb_skin.cpp +++ /dev/null @@ -1,899 +0,0 @@ -/** - * @file - */ - -#include "tb_skin.h" -#include "core/Assert.h" -#include "core/Log.h" -#include "tb_node_tree.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" - -namespace tb { - -// == Util functions ========================================================== - -/*TB_TEXT_ALIGN StringToTextAlign(const char *align_str) -{ - TB_TEXT_ALIGN align = TB_TEXT_ALIGN_CENTER; - if (SDL_strstr(state_str, "left")) align = TB_TEXT_ALIGN_LEFT; - if (SDL_strstr(state_str, "center")) align = TB_TEXT_ALIGN_CENTER; - if (SDL_strstr(state_str, "right")) align = TB_TEXT_ALIGN_RIGHT; - return state; -}*/ - -SKIN_STATE stringToState(const char *stateStr) { - SKIN_STATE state = SKIN_STATE_NONE; - if (SDL_strstr(stateStr, "all")) { - state |= SKIN_STATE_ALL; - } - if (SDL_strstr(stateStr, "disabled")) { - state |= SKIN_STATE_DISABLED; - } - if (SDL_strstr(stateStr, "focused")) { - state |= SKIN_STATE_FOCUSED; - } - if (SDL_strstr(stateStr, "pressed")) { - state |= SKIN_STATE_PRESSED; - } - if (SDL_strstr(stateStr, "selected")) { - state |= SKIN_STATE_SELECTED; - } - if (SDL_strstr(stateStr, "hovered")) { - state |= SKIN_STATE_HOVERED; - } - return state; -} - -SKIN_ELEMENT_TYPE stringToType(const char *typeStr) { - if (SDL_strcmp(typeStr, "StretchBox") == 0) { - return SKIN_ELEMENT_TYPE_STRETCH_BOX; - } - if (SDL_strcmp(typeStr, "Image") == 0) { - return SKIN_ELEMENT_TYPE_IMAGE; - } - if (SDL_strcmp(typeStr, "Stretch Image") == 0) { - return SKIN_ELEMENT_TYPE_STRETCH_IMAGE; - } - if (SDL_strcmp(typeStr, "Tile") == 0) { - return SKIN_ELEMENT_TYPE_TILE; - } - if (SDL_strcmp(typeStr, "StretchBorder") == 0) { - return SKIN_ELEMENT_TYPE_STRETCH_BORDER; - } - Log::debug("Skin error: Unknown skin type!"); - return SKIN_ELEMENT_TYPE_STRETCH_BOX; -} - -TBSkinCondition::TARGET stringToTarget(const char *targetStr) { - if (SDL_strcmp(targetStr, "this") == 0) { - return TBSkinCondition::TARGET_THIS; - } - if (SDL_strcmp(targetStr, "parent") == 0) { - return TBSkinCondition::TARGET_PARENT; - } - if (SDL_strcmp(targetStr, "ancestors") == 0) { - return TBSkinCondition::TARGET_ANCESTORS; - } - if (SDL_strcmp(targetStr, "prev sibling") == 0) { - return TBSkinCondition::TARGET_PREV_SIBLING; - } - if (SDL_strcmp(targetStr, "next sibling") == 0) { - return TBSkinCondition::TARGET_NEXT_SIBLING; - } - Log::debug("Skin error: Unknown target in condition!"); - return TBSkinCondition::TARGET_THIS; -} - -TBSkinCondition::PROPERTY stringToProperty(const char *propStr) { - if (SDL_strcmp(propStr, "skin") == 0) { - return TBSkinCondition::PROPERTY_SKIN; - } - if (SDL_strcmp(propStr, "window active") == 0) { - return TBSkinCondition::PROPERTY_WINDOW_ACTIVE; - } - if (SDL_strcmp(propStr, "axis") == 0) { - return TBSkinCondition::PROPERTY_AXIS; - } - if (SDL_strcmp(propStr, "align") == 0) { - return TBSkinCondition::PROPERTY_ALIGN; - } - if (SDL_strcmp(propStr, "id") == 0) { - return TBSkinCondition::PROPERTY_ID; - } - if (SDL_strcmp(propStr, "state") == 0) { - return TBSkinCondition::PROPERTY_STATE; - } - if (SDL_strcmp(propStr, "value") == 0) { - return TBSkinCondition::PROPERTY_VALUE; - } - if (SDL_strcmp(propStr, "hover") == 0) { - return TBSkinCondition::PROPERTY_HOVER; - } - if (SDL_strcmp(propStr, "capture") == 0) { - return TBSkinCondition::PROPERTY_CAPTURE; - } - if (SDL_strcmp(propStr, "focus") == 0) { - return TBSkinCondition::PROPERTY_FOCUS; - } - return TBSkinCondition::PROPERTY_CUSTOM; -} - -// == TBSkinCondition ======================================================= - -TBSkinCondition::TBSkinCondition(TARGET target, PROPERTY prop, const TBID &customProp, const TBID &value, TEST test) - : m_target(target), m_test(test) { - m_info.prop = prop; - m_info.custom_prop = customProp; - m_info.value = value; -} - -bool TBSkinCondition::getCondition(TBSkinConditionContext &context) const { - const bool equal = context.getCondition(m_target, m_info); - return equal == (m_test == TEST_EQUAL); -} - -// == TBSkin ================================================================ - -TBSkin::TBSkin() - : m_listener(nullptr), m_color_frag(nullptr), m_default_disabled_opacity(0.3F), m_default_placeholder_opacity(0.2F), - m_default_spacing(0) { - g_renderer->addListener(this); - - // Avoid filtering artifacts at edges when we draw fragments stretched. - m_frag_manager.setAddBorder(true); -} - -bool TBSkin::load(const char *skinFile, const char *overrideSkinFile) { - if (!loadInternal(skinFile)) { - return false; - } - if ((overrideSkinFile != nullptr) && !loadInternal(overrideSkinFile)) { - return false; - } - return reloadBitmaps(); -} - -bool TBSkin::loadInternal(const char *skinFile) { - TBNode node; - if (!node.readFile(skinFile)) { - return false; - } - - TBTempBuffer skin_path; - if (!skin_path.appendPath(skinFile)) { - return false; - } - - if (node.getNode("description") != nullptr) { - // Check which DPI mode the dimension converter should use. - // The base-dpi is the dpi in which the padding, spacing (and so on) - // is specified in. If the skin supports a different DPI that is - // closer to the screen DPI, all such dimensions will be scaled. - int base_dpi = node.getValueInt("description>base-dpi", 96); - int supported_dpi = base_dpi; - if (TBNode *supported_dpi_node = node.getNode("description>supported-dpi")) { - core_assert(supported_dpi_node->getValue().isArray() || - supported_dpi_node->getValue().getInt() == base_dpi); - if (TBValueArray *arr = supported_dpi_node->getValue().getArray()) { - int screen_dpi = TBSystem::getDPI(); - int best_supported_dpi = 0; - for (int i = 0; i < arr->getLength(); i++) { - int candidate_dpi = arr->getValue(i)->getInt(); - if ((best_supported_dpi == 0) || - Abs(candidate_dpi - screen_dpi) < Abs(best_supported_dpi - screen_dpi)) { - best_supported_dpi = candidate_dpi; - } - } - supported_dpi = best_supported_dpi; - } - } - m_dim_conv.setDPI(base_dpi, supported_dpi); - } - - // Read skin constants - if (const char *color = node.getValueString("defaults>text-color", nullptr)) { - m_default_text_color.setFromString(color, SDL_strlen(color)); - } - m_default_disabled_opacity = node.getValueFloat("defaults>disabled>opacity", m_default_disabled_opacity); - m_default_placeholder_opacity = node.getValueFloat("defaults>placeholder>opacity", m_default_placeholder_opacity); - m_default_spacing = getPxFromNode(node.getNode("defaults>spacing"), m_default_spacing); - - // Iterate through all elements nodes and add skin elements or patch already - // existing elements. - TBNode *elements = node.getNode("elements"); - if (elements != nullptr) { - TBNode *n = elements->getFirstChild(); - while (n != nullptr) { - // If we have a "clone" node, clone all children from that node - // into this node. - while (TBNode *clone = n->getNode("clone")) { - n->remove(clone); - - TBNode *clone_source = elements->getNode(clone->getValue().getString()); - if (clone_source != nullptr) { - n->cloneChildren(clone_source); - } - - delete clone; - } - - // If the skin element already exist, we will call Load on it again. - // This will patch the element with any new data from the node. - TBID element_id(n->getName()); - TBSkinElement *e = getSkinElement(element_id); - if (e == nullptr) { - e = new TBSkinElement; - if (e == nullptr) { - return false; - } - m_elements.add(element_id, e); - } - - e->load(n, this, skin_path.getData()); - if (m_listener != nullptr) { - m_listener->onSkinElementLoaded(this, e, n); - } - - n = n->getNext(); - } - } - return true; -} - -void TBSkin::unloadBitmaps() { - // Unset all bitmap pointers. - TBHashTableIteratorOf it(&m_elements); - while (TBSkinElement *element = it.getNextContent()) { - element->bitmap = nullptr; - } - - // Clear all fragments and bitmaps. - m_frag_manager.clear(); - m_color_frag = nullptr; -} - -bool TBSkin::reloadBitmaps() { - unloadBitmaps(); - bool success = reloadBitmapsInternal(); - // Create all bitmaps for the bitmap fragment maps - if (success) { - success = m_frag_manager.validateBitmaps(); - } - -#ifdef TB_RUNTIME_DEBUG_INFO - Log::debug("Skin loaded using %d bitmaps.", m_frag_manager.getNumMaps()); -#endif - return success; -} - -bool TBSkin::reloadBitmapsInternal() { - // Load all bitmap files into new bitmap fragments. - TBTempBuffer filename_dst_DPI; - bool success = true; - TBHashTableIteratorOf it(&m_elements); - while (TBSkinElement *element = it.getNextContent()) { - if (!element->bitmap_file.empty()) { - core_assert(!element->bitmap); - - // FIX: dedicated_map is not needed for all backends (only deprecated fixed function GL) - bool dedicated_map = element->type == SKIN_ELEMENT_TYPE_TILE; - - // Try to load bitmap fragment in the destination DPI (F.ex "foo.png" becomes "foo@192.png") - int bitmap_dpi = m_dim_conv.getSrcDPI(); - if (m_dim_conv.needConversion()) { - m_dim_conv.getDstDPIFilename(element->bitmap_file.c_str(), &filename_dst_DPI); - element->bitmap = m_frag_manager.getFragmentFromFile(filename_dst_DPI.getData(), dedicated_map); - if (element->bitmap != nullptr) { - bitmap_dpi = m_dim_conv.getDstDPI(); - } - } - element->setBitmapDPI(m_dim_conv, bitmap_dpi); - - // If we still have no bitmap fragment, load from default file. - if (element->bitmap == nullptr) { - element->bitmap = m_frag_manager.getFragmentFromFile(element->bitmap_file.c_str(), dedicated_map); - } - - if (element->bitmap == nullptr) { - success = false; - } - } - } - // Create fragment used for color fills. Use 2x2px and inset source rect to center 0x0 - // to avoid filtering artifacts. - uint32_t data[4] = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; - m_color_frag = m_frag_manager.createNewFragment(TBID((uint32_t)0), false, 2, 2, 2, data); - if (m_color_frag == nullptr) { - return false; - } - m_color_frag->m_rect = m_color_frag->m_rect.shrink(1, 1); - return success; -} - -TBSkin::~TBSkin() { - g_renderer->removeListener(this); -} - -TBSkinElement *TBSkin::getSkinElement(const TBID &skinId) const { - if (skinId == 0U) { - return nullptr; - } - return m_elements.get(skinId); -} - -TBSkinElement *TBSkin::getSkinElementStrongOverride(const TBID &skinId, SKIN_STATE state, - TBSkinConditionContext &context) const { - if (TBSkinElement *skin_element = getSkinElement(skinId)) { - // Avoid eternal recursion when overrides refer to elements referring back. - if (skin_element->is_getting) { - return nullptr; - } - skin_element->is_getting = true; - - // Check if there's any strong overrides for this element with the given state. - TBSkinElementState *override_state = skin_element->m_strong_override_elements.getStateElement(state, context); - if (override_state != nullptr) { - if (TBSkinElement *override_element = - getSkinElementStrongOverride(override_state->element_id, state, context)) { - skin_element->is_getting = false; - return override_element; - } - } - - skin_element->is_getting = false; - return skin_element; - } - return nullptr; -} - -TBSkinElement *TBSkin::paintSkin(const TBRect &dstRect, const TBID &skinId, SKIN_STATE state, - TBSkinConditionContext &context) { - return paintSkin(dstRect, getSkinElement(skinId), state, context); -} - -TBSkinElement *TBSkin::paintSkin(const TBRect &dstRect, TBSkinElement *element, SKIN_STATE state, - TBSkinConditionContext &context) { - if ((element == nullptr) || element->is_painting) { - return nullptr; - } - - // Avoid potential endless recursion in evil skins - element->is_painting = true; - - // Return the override if we have one. - TBSkinElement *return_element = element; - -#ifdef TB_RUNTIME_DEBUG_INFO - bool paint_error_highlight = false; -#endif - - // If there's any override for this state, paint it. - TBSkinElementState *override_state = element->m_override_elements.getStateElement(state, context); - if (override_state != nullptr) { - if (TBSkinElement *used_override = paintSkin(dstRect, override_state->element_id, state, context)) { - return_element = used_override; - } else { -#ifdef TB_RUNTIME_DEBUG_INFO - paint_error_highlight = true; -#endif - Log::debug("Skin error: The skin references a missing element, or has a reference loop!"); - // Fall back to the standard skin. - override_state = nullptr; - } - } - - // If there was no override, paint the standard skin element. - if (override_state == nullptr) { - paintElement(dstRect, element); - } - - // Paint all child elements that match the state (or should be painted for all states) - if (element->m_child_elements.hasStateElements()) { - const TBSkinElementState *state_element = element->m_child_elements.getFirstElement(); - while (state_element != nullptr) { - if (state_element->isMatch(state, context)) { - paintSkin(dstRect, state_element->element_id, state_element->state & state, context); - } - state_element = state_element->getNext(); - } - } - -#ifdef TB_RUNTIME_DEBUG_INFO - // Paint ugly rectangles on invalid skin elements in debug builds. - if (paint_error_highlight) { - g_tb_skin->paintRect(dstRect.expand(1, 1), TBColor(255, 205, 0), 1); - } - if (paint_error_highlight) { - g_tb_skin->paintRect(dstRect.shrink(1, 1), TBColor(255, 0, 0), 1); - } -#endif - - element->is_painting = false; - return return_element; -} - -void TBSkin::paintSkinOverlay(const TBRect &dstRect, TBSkinElement *element, SKIN_STATE state, - TBSkinConditionContext &context) { - if ((element == nullptr) || element->is_painting) { - return; - } - - // Avoid potential endless recursion in evil skins - element->is_painting = true; - - // Paint all overlay elements that matches the state (or should be painted for all states) - const TBSkinElementState *state_element = element->m_overlay_elements.getFirstElement(); - while (state_element != nullptr) { - if (state_element->isMatch(state, context)) { - paintSkin(dstRect, state_element->element_id, state_element->state & state, context); - } - state_element = state_element->getNext(); - } - - element->is_painting = false; -} - -void TBSkin::paintElement(const TBRect &dstRect, TBSkinElement *element) { - paintElementBGColor(dstRect, element); - if (element->bitmap == nullptr) { - return; - } - if (element->type == SKIN_ELEMENT_TYPE_IMAGE) { - paintElementImage(dstRect, element); - } else if (element->type == SKIN_ELEMENT_TYPE_TILE) { - paintElementTile(dstRect, element); - } else if (element->type == SKIN_ELEMENT_TYPE_STRETCH_IMAGE || element->cut == 0) { - paintElementStretchImage(dstRect, element); - } else if (element->type == SKIN_ELEMENT_TYPE_STRETCH_BORDER) { - paintElementStretchBox(dstRect, element, false); - } else { - paintElementStretchBox(dstRect, element, true); - } -} - -TBRect TBSkin::getFlippedRect(const TBRect &srcRect, TBSkinElement *element) const { - // Turning the source rect "inside out" will flip the result when rendered. - TBRect tmp_rect = srcRect; - if (element->flip_x != 0) { - tmp_rect.x += tmp_rect.w; - tmp_rect.w = -tmp_rect.w; - } - if (element->flip_y != 0) { - tmp_rect.y += tmp_rect.h; - tmp_rect.h = -tmp_rect.h; - } - return tmp_rect; -} - -void TBSkin::paintRect(const TBRect &dstRect, const TBColor &color, int thickness) { - if (dstRect.w < thickness * 2 || dstRect.h < thickness * 2) { - paintRectFill(dstRect, color); - return; - } - // Top - paintRectFill(TBRect(dstRect.x, dstRect.y, dstRect.w, thickness), color); - // Bottom - paintRectFill(TBRect(dstRect.x, dstRect.y + dstRect.h - thickness, dstRect.w, thickness), color); - // Left - paintRectFill(TBRect(dstRect.x, dstRect.y + thickness, thickness, dstRect.h - thickness * 2), color); - // Right - paintRectFill( - TBRect(dstRect.x + dstRect.w - thickness, dstRect.y + thickness, thickness, dstRect.h - thickness * 2), color); -} - -void TBSkin::paintRectFill(const TBRect &dstRect, const TBColor &color) { - if (!dstRect.isEmpty()) { - g_renderer->drawBitmapColored(dstRect, TBRect(), color, m_color_frag); - } -} - -void TBSkin::paintElementBGColor(const TBRect &dstRect, TBSkinElement *element) { - if (element->bg_color == 0) { - return; - } - paintRectFill(dstRect, element->bg_color); -} - -void TBSkin::paintElementImage(const TBRect &dstRect, TBSkinElement *element) { - const TBRect src_rect(0, 0, element->bitmap->width(), element->bitmap->height()); - TBRect rect = dstRect.expand(element->expand, element->expand); - rect.set(rect.x + element->img_ofs_x + (rect.w - src_rect.w) * element->img_position_x / 100, - rect.y + element->img_ofs_y + (rect.h - src_rect.h) * element->img_position_y / 100, src_rect.w, - src_rect.h); - g_renderer->drawBitmap(rect, getFlippedRect(src_rect, element), element->bitmap); -} - -void TBSkin::paintElementTile(const TBRect &dstRect, TBSkinElement *element) { - const TBRect &rect = dstRect.expand(element->expand, element->expand); - g_renderer->drawBitmapTile(rect, element->bitmap->getBitmap(TB_VALIDATE_ALWAYS)); -} - -void TBSkin::paintElementStretchImage(const TBRect &dstRect, TBSkinElement *element) { - if (dstRect.isEmpty()) { - return; - } - const TBRect &rect = dstRect.expand(element->expand, element->expand); - const TBRect &src_rect = getFlippedRect(TBRect(0, 0, element->bitmap->width(), element->bitmap->height()), element); - g_renderer->drawBitmap(rect, src_rect, element->bitmap); -} - -void TBSkin::paintElementStretchBox(const TBRect &dstRect, TBSkinElement *element, bool fillCenter) { - if (dstRect.isEmpty()) { - return; - } - - TBRect rect = dstRect.expand(element->expand, element->expand); - - // Stretch the dst_cut (if rect is smaller than the skin size) - // FIX: the expand should also be stretched! - const int cut = element->cut; - int dst_cut_w = Min(cut, rect.w / 2); - int dst_cut_h = Min(cut, rect.h / 2); - const int bw = element->bitmap->width(); - const int bh = element->bitmap->height(); - - const bool has_left_right_edges = rect.h > dst_cut_h * 2; - const bool has_top_bottom_edges = rect.w > dst_cut_w * 2; - - rect = getFlippedRect(rect, element); - if (element->flip_x != 0) { - dst_cut_w = -dst_cut_w; - } - if (element->flip_y != 0) { - dst_cut_h = -dst_cut_h; - } - - // Corners - g_renderer->drawBitmap(TBRect(rect.x, rect.y, dst_cut_w, dst_cut_h), TBRect(0, 0, cut, cut), element->bitmap); - g_renderer->drawBitmap(TBRect(rect.x + rect.w - dst_cut_w, rect.y, dst_cut_w, dst_cut_h), - TBRect(bw - cut, 0, cut, cut), element->bitmap); - g_renderer->drawBitmap(TBRect(rect.x, rect.y + rect.h - dst_cut_h, dst_cut_w, dst_cut_h), - TBRect(0, bh - cut, cut, cut), element->bitmap); - g_renderer->drawBitmap(TBRect(rect.x + rect.w - dst_cut_w, rect.y + rect.h - dst_cut_h, dst_cut_w, dst_cut_h), - TBRect(bw - cut, bh - cut, cut, cut), element->bitmap); - - // Left & right edge - if (has_left_right_edges) { - g_renderer->drawBitmap(TBRect(rect.x, rect.y + dst_cut_h, dst_cut_w, rect.h - dst_cut_h * 2), - TBRect(0, cut, cut, bh - cut * 2), element->bitmap); - g_renderer->drawBitmap( - TBRect(rect.x + rect.w - dst_cut_w, rect.y + dst_cut_h, dst_cut_w, rect.h - dst_cut_h * 2), - TBRect(bw - cut, cut, cut, bh - cut * 2), element->bitmap); - } - - // Top & bottom edge - if (has_top_bottom_edges) { - g_renderer->drawBitmap(TBRect(rect.x + dst_cut_w, rect.y, rect.w - dst_cut_w * 2, dst_cut_h), - TBRect(cut, 0, bw - cut * 2, cut), element->bitmap); - g_renderer->drawBitmap( - TBRect(rect.x + dst_cut_w, rect.y + rect.h - dst_cut_h, rect.w - dst_cut_w * 2, dst_cut_h), - TBRect(cut, bh - cut, bw - cut * 2, cut), element->bitmap); - } - - // Center - if (fillCenter && has_top_bottom_edges && has_left_right_edges) { - g_renderer->drawBitmap( - TBRect(rect.x + dst_cut_w, rect.y + dst_cut_h, rect.w - dst_cut_w * 2, rect.h - dst_cut_h * 2), - TBRect(cut, cut, bw - cut * 2, bh - cut * 2), element->bitmap); - } -} - -#ifdef TB_RUNTIME_DEBUG_INFO -void TBSkin::debug() { - m_frag_manager.debug(); -} -#endif // TB_RUNTIME_DEBUG_INFO - -void TBSkin::onContextLost() { - // We could simply do: m_frag_manager.DeleteBitmaps() and then all bitmaps - // would be recreated automatically when needed. But because it's easy, - // we unload everything so we save some memory (by not keeping any image - // data around). - unloadBitmaps(); -} - -void TBSkin::onContextRestored() { - // Reload bitmaps (since we unloaded everything in OnContextLost()) - reloadBitmaps(); -} - -int TBSkin::getPxFromNode(TBNode *node, int defValue) const { - return node != nullptr ? m_dim_conv.getPxFromValue(&node->getValue(), defValue) : defValue; -} - -// == TBSkinElement ========================================================= - -TBSkinElement::TBSkinElement() - : bitmap(nullptr), cut(0), expand(0), type(SKIN_ELEMENT_TYPE_STRETCH_BOX), is_painting(false), is_getting(false), - padding_left(0), padding_top(0), padding_right(0), padding_bottom(0), width(SKIN_VALUE_NOT_SPECIFIED), - height(SKIN_VALUE_NOT_SPECIFIED), pref_width(SKIN_VALUE_NOT_SPECIFIED), pref_height(SKIN_VALUE_NOT_SPECIFIED), - min_width(SKIN_VALUE_NOT_SPECIFIED), min_height(SKIN_VALUE_NOT_SPECIFIED), max_width(SKIN_VALUE_NOT_SPECIFIED), - max_height(SKIN_VALUE_NOT_SPECIFIED), spacing(SKIN_VALUE_NOT_SPECIFIED), content_ofs_x(0), content_ofs_y(0), - img_ofs_x(0), img_ofs_y(0), img_position_x(50), img_position_y(50), flip_x(0), flip_y(0), opacity(1.F), - text_color(0, 0, 0, 0), bg_color(0, 0, 0, 0), bitmap_dpi(0) { -} - -TBSkinElement::~TBSkinElement() { -} - -int TBSkinElement::getIntrinsicMinWidth() const { - if ((bitmap != nullptr) && type == SKIN_ELEMENT_TYPE_IMAGE) { - return bitmap->width() - expand * 2; - } - // Sizes below the skin cut size would start to shrink the skin below pretty, - // so assume that's the default minimum size if it's not specified (minus expansion) - return cut * 2 - expand * 2; -} - -int TBSkinElement::getIntrinsicMinHeight() const { - if ((bitmap != nullptr) && type == SKIN_ELEMENT_TYPE_IMAGE) { - return bitmap->height() - expand * 2; - } - // Sizes below the skin cut size would start to shrink the skin below pretty, - // so assume that's the default minimum size if it's not specified (minus expansion) - return cut * 2 - expand * 2; -} - -int TBSkinElement::getIntrinsicWidth() const { - if (width != SKIN_VALUE_NOT_SPECIFIED) { - return width; - } - if (bitmap != nullptr) { - return bitmap->width() - expand * 2; - } - // FIX: We may want to check child elements etc. - return SKIN_VALUE_NOT_SPECIFIED; -} - -int TBSkinElement::getIntrinsicHeight() const { - if (height != SKIN_VALUE_NOT_SPECIFIED) { - return height; - } - if (bitmap != nullptr) { - return bitmap->height() - expand * 2; - } - // FIX: We may want to check child elements etc. - return SKIN_VALUE_NOT_SPECIFIED; -} - -void TBSkinElement::setBitmapDPI(const TBDimensionConverter &dimConv, int bitmapDpi) { - if (this->bitmap_dpi != 0) { - // We have already applied the modifications so abort. This may - // happen when we reload bitmaps without reloading the skin. - return; - } - if (dimConv.needConversion()) { - if (bitmapDpi == dimConv.getDstDPI()) { - // The bitmap was loaded in a different DPI than the base DPI so - // we must scale the bitmap properties. - expand = expand * dimConv.getDstDPI() / dimConv.getSrcDPI(); - cut = cut * dimConv.getDstDPI() / dimConv.getSrcDPI(); - } else { - // The bitmap was loaded in the base DPI and we need to scale it. - // Apply the DPI conversion to the skin element scale factor. - // FIX: For this to work well, we would need to apply scale to both - // image and all the other types of drawing too. - // scale_x = scale_x * dim_conv.getDstDPI() / dim_conv.getSrcDPI(); - // scale_y = scale_y * dim_conv.getDstDPI() / dim_conv.getSrcDPI(); - } - } - this->bitmap_dpi = bitmapDpi; -} - -bool TBSkinElement::hasState(SKIN_STATE state, TBSkinConditionContext &context) { - return (m_override_elements.getStateElement(state, context, TBSkinElementState::MATCH_RULE_ONLY_SPECIFIC_STATE) != - nullptr) || - (m_child_elements.getStateElement(state, context, TBSkinElementState::MATCH_RULE_ONLY_SPECIFIC_STATE) != - nullptr) || - (m_overlay_elements.getStateElement(state, context, TBSkinElementState::MATCH_RULE_ONLY_SPECIFIC_STATE) != - nullptr); -} - -void TBSkinElement::load(TBNode *n, TBSkin *skin, const char *skinPath) { - if (const char *bitmap = n->getValueString("bitmap", nullptr)) { - bitmap_file.clear(); - bitmap_file.append(skinPath); - bitmap_file.append(bitmap); - } - - // Note: Always read cut and expand as pixels. These values might later be - // recalculated depending on the DPI the bitmaps are available in. - cut = n->getValueInt("cut", cut); - expand = n->getValueInt("expand", expand); - bitmap_dpi = 0; - - name = n->getName(); - id.set(n->getName()); - - const TBDimensionConverter *dim_conv = skin->getDimensionConverter(); - - if (TBNode *padding_node = n->getNode("padding")) { - TBValue &val = padding_node->getValue(); - if (val.getArrayLength() == 4) { - padding_top = dim_conv->getPxFromValue(val.getArray()->getValue(0), 0); - padding_right = dim_conv->getPxFromValue(val.getArray()->getValue(1), 0); - padding_bottom = dim_conv->getPxFromValue(val.getArray()->getValue(2), 0); - padding_left = dim_conv->getPxFromValue(val.getArray()->getValue(3), 0); - } else if (val.getArrayLength() == 2) { - padding_top = padding_bottom = dim_conv->getPxFromValue(val.getArray()->getValue(0), 0); - padding_left = padding_right = dim_conv->getPxFromValue(val.getArray()->getValue(1), 0); - } else { - padding_top = padding_right = padding_bottom = padding_left = dim_conv->getPxFromValue(&val, 0); - } - } - width = skin->getPxFromNode(n->getNode("width"), width); - height = skin->getPxFromNode(n->getNode("height"), height); - pref_width = skin->getPxFromNode(n->getNode("pref-width"), pref_width); - pref_height = skin->getPxFromNode(n->getNode("pref-height"), pref_height); - min_width = skin->getPxFromNode(n->getNode("min-width"), min_width); - min_height = skin->getPxFromNode(n->getNode("min-height"), min_height); - max_width = skin->getPxFromNode(n->getNode("max-width"), max_width); - max_height = skin->getPxFromNode(n->getNode("max-height"), max_height); - spacing = skin->getPxFromNode(n->getNode("spacing"), spacing); - content_ofs_x = skin->getPxFromNode(n->getNode("content-ofs-x"), content_ofs_x); - content_ofs_y = skin->getPxFromNode(n->getNode("content-ofs-y"), content_ofs_y); - img_position_x = n->getValueInt("img-position-x", img_position_x); - img_position_y = n->getValueInt("img-position-y", img_position_y); - img_ofs_x = skin->getPxFromNode(n->getNode("img-ofs-x"), img_ofs_x); - img_ofs_y = skin->getPxFromNode(n->getNode("img-ofs-y"), img_ofs_y); - flip_x = n->getValueInt("flip-x", flip_x); - flip_y = n->getValueInt("flip-y", flip_y); - opacity = n->getValueFloat("opacity", opacity); - - if (const char *color = n->getValueString("text-color", nullptr)) { - text_color.setFromString(color, SDL_strlen(color)); - } - - if (const char *color = n->getValueString("background-color", nullptr)) { - bg_color.setFromString(color, SDL_strlen(color)); - } - - if (const char *type_str = n->getValueString("type", nullptr)) { - type = stringToType(type_str); - } - - // Create all state elements - m_override_elements.load(n->getNode("overrides")); - m_strong_override_elements.load(n->getNode("strong-overrides")); - m_child_elements.load(n->getNode("children")); - m_overlay_elements.load(n->getNode("overlays")); -} - -// == TBSkinElementState ==================================================== - -bool TBSkinElementState::isMatch(SKIN_STATE state, TBSkinConditionContext &context, MATCH_RULE rule) const { - if (rule == MATCH_RULE_ONLY_SPECIFIC_STATE && this->state == SKIN_STATE_ALL) { - return false; - } - if (((state & this->state) != 0U) || this->state == SKIN_STATE_ALL) { - for (TBSkinCondition *condition = conditions.getFirst(); condition != nullptr; - condition = condition->getNext()) { - if (!condition->getCondition(context)) { - return false; - } - } - return true; - } - return false; -} - -bool TBSkinElementState::isExactMatch(SKIN_STATE state, TBSkinConditionContext &context, MATCH_RULE rule) const { - if (rule == MATCH_RULE_ONLY_SPECIFIC_STATE && this->state == SKIN_STATE_ALL) { - return false; - } - if (state == this->state || this->state == SKIN_STATE_ALL) { - for (TBSkinCondition *condition = conditions.getFirst(); condition != nullptr; - condition = condition->getNext()) { - if (!condition->getCondition(context)) { - return false; - } - } - return true; - } - return false; -} - -// == TBSkinElementStateList ================================================== - -TBSkinElementStateList::~TBSkinElementStateList() { - while (TBSkinElementState *state = m_state_elements.getFirst()) { - m_state_elements.remove(state); - delete state; - } -} - -TBSkinElementState *TBSkinElementStateList::getStateElement(SKIN_STATE state, TBSkinConditionContext &context, - TBSkinElementState::MATCH_RULE rule) const { - // First try to get a state element with a exact match to the current state - if (TBSkinElementState *element_state = getStateElementExactMatch(state, context, rule)) { - return element_state; - } - // No exact state match. Get a state with a partly match if there is one. - TBSkinElementState *state_element = m_state_elements.getFirst(); - while (state_element != nullptr) { - if (state_element->isMatch(state, context, rule)) { - return state_element; - } - state_element = state_element->getNext(); - } - return nullptr; -} - -TBSkinElementState *TBSkinElementStateList::getStateElementExactMatch(SKIN_STATE state, TBSkinConditionContext &context, - TBSkinElementState::MATCH_RULE rule) const { - TBSkinElementState *state_element = m_state_elements.getFirst(); - while (state_element != nullptr) { - if (state_element->isExactMatch(state, context, rule)) { - return state_element; - } - state_element = state_element->getNext(); - } - return nullptr; -} - -void TBSkinElementStateList::load(TBNode *n) { - if (n == nullptr) { - return; - } - - // For each node, create a new state element. - TBNode *element_node = n->getFirstChild(); - while (element_node != nullptr) { - TBSkinElementState *state = new TBSkinElementState; - if (state == nullptr) { - return; - } - - // By default, a state element applies to all combinations of states - state->state = SKIN_STATE_ALL; - state->element_id.set(element_node->getValue().getString()); - - // Loop through all nodes, read state and create all found conditions. - for (TBNode *condition_node = element_node->getFirstChild(); condition_node != nullptr; - condition_node = condition_node->getNext()) { - if (SDL_strcmp(condition_node->getName(), "state") == 0) { - state->state = stringToState(condition_node->getValue().getString()); - } else if (SDL_strcmp(condition_node->getName(), "condition") == 0) { - TBSkinCondition::TARGET target = stringToTarget(condition_node->getValueString("target", "")); - - const char *prop_str = condition_node->getValueString("property", ""); - TBSkinCondition::PROPERTY prop = stringToProperty(prop_str); - TBID custom_prop; - if (prop == TBSkinCondition::PROPERTY_CUSTOM) { - custom_prop.set(prop_str); - } - - TBID value; - if (TBNode *value_n = condition_node->getNode("value")) { - // Set the it to number or string. If it's a state, we must first convert the - // state string to the SKIN_STATE state combo. - if (prop == TBSkinCondition::PROPERTY_STATE) { - value.set(stringToState(value_n->getValue().getString())); - } else if (value_n->getValue().isString()) { - value.set(value_n->getValue().getString()); - } else { - value.set(value_n->getValue().getInt()); - } - } - - TBSkinCondition::TEST test = TBSkinCondition::TEST_EQUAL; - if (const char *test_str = condition_node->getValueString("test", nullptr)) { - if (SDL_strcmp(test_str, "!=") == 0) { - test = TBSkinCondition::TEST_NOT_EQUAL; - } - } - - if (TBSkinCondition *condition = new TBSkinCondition(target, prop, custom_prop, value, test)) { - state->conditions.addLast(condition); - } - } - } - - // State is reado to add - m_state_elements.addLast(state); - element_node = element_node->getNext(); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_skin.h b/src/modules/ui/turbobadger/tb/tb_skin.h deleted file mode 100644 index 7f723b7ae..000000000 --- a/src/modules/ui/turbobadger/tb/tb_skin.h +++ /dev/null @@ -1,446 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_bitmap_fragment.h" -#include "tb_core.h" -#include "tb_dimension.h" -#include "tb_hashtable.h" -#include "tb_linklist.h" -#include "tb_renderer.h" -#include "tb_value.h" - -namespace tb { - -class TBNode; -class TBSkinConditionContext; - -/** Used for some values in TBSkinElement if they has not been specified in the skin. */ -#define SKIN_VALUE_NOT_SPECIFIED TB_INVALID_DIMENSION - -/** Skin state types (may be combined). - NOTE: This should exactly match WIDGET_STATE in tb_widgets.h! */ -enum SKIN_STATE { - SKIN_STATE_NONE = 0, - SKIN_STATE_DISABLED = 1, - SKIN_STATE_FOCUSED = 2, - SKIN_STATE_PRESSED = 4, - SKIN_STATE_SELECTED = 8, - SKIN_STATE_HOVERED = 16, - - SKIN_STATE_ALL = - SKIN_STATE_DISABLED | SKIN_STATE_FOCUSED | SKIN_STATE_PRESSED | SKIN_STATE_SELECTED | SKIN_STATE_HOVERED -}; -CORE_ENUM_BIT_OPERATIONS(SKIN_STATE); - -/** Type of painting that should be done for a TBSkinElement. */ -enum SKIN_ELEMENT_TYPE { - SKIN_ELEMENT_TYPE_STRETCH_BOX, ///< Default element type, cut - /// bitmap into 9 pieces "cut" wide - SKIN_ELEMENT_TYPE_STRETCH_BORDER, ///< Same as above, but dont fill the center - SKIN_ELEMENT_TYPE_STRETCH_IMAGE, ///< Scale the bitmap to the dest rect - SKIN_ELEMENT_TYPE_TILE, ///< Tile the bitmap to the dest rect - SKIN_ELEMENT_TYPE_IMAGE -}; - -/** TBSkinCondition checks if a condition is true for a given TBSkinConditionContext. - This is used to apply different state elements depending on what is currently - painting the skin. */ - -class TBSkinCondition : public TBLinkOf { -public: - /** Defines which target(s) relative to the context that should be tested for the condition. */ - enum TARGET { - TARGET_THIS, ///< The object painting the skin. - TARGET_PARENT, ///< The parent of the object painting the skin. - TARGET_ANCESTORS, ///< All ancestors of the object painting the skin. - TARGET_PREV_SIBLING, ///< The previous sibling of the object painting the skin. - TARGET_NEXT_SIBLING ///< The next sibling of the object painting the skin. - }; - /** Defines which property in the context that should be checked. */ - enum PROPERTY { - PROPERTY_SKIN, ///< The background skin id. - PROPERTY_WINDOW_ACTIVE, ///< The window is active (no value required). - PROPERTY_AXIS, ///< The axis of the content (x or y) - PROPERTY_ALIGN, ///< The alignment. - PROPERTY_ID, ///< The id. - PROPERTY_STATE, ///< The state is set. - PROPERTY_VALUE, ///< The current value (integer). - PROPERTY_HOVER, ///< Focus is on the target or any child (no value required). - PROPERTY_CAPTURE, ///< Capture is on the target or any child (no value required). - PROPERTY_FOCUS, ///< Focus is on the target or any child (no value required). - PROPERTY_CUSTOM ///< It's a property unknown to skin, that the TBSkinConditionContext might know about. - }; - - /** Defines if the condition tested should be equal or not for the condition to be true. */ - enum TEST { - TEST_EQUAL, ///< Value should be equal for condition to be true. - TEST_NOT_EQUAL ///< Value should not be equal for condition to be true. - }; - - /** Stores the information needed for checking a condition. */ - struct CONDITION_INFO { - PROPERTY prop; ///< Which property. - TBID custom_prop; ///< Which property (only if prop is PROPERTY_CUSTOM). - TBID value; ///< The value to compare. - }; - - TBSkinCondition(TARGET target, PROPERTY prop, const TBID &custom_prop, const TBID &value, TEST test); - - /** Return true if the condition is true for the given context. */ - bool getCondition(TBSkinConditionContext &context) const; - -private: - TARGET m_target; - CONDITION_INFO m_info; - TEST m_test; -}; - -/** TBSkinConditionContext checks if a condition is true. It is passed to skin painting functions - so different state elements can be applied depending on the current situation of the context. - F.ex a widget may change appearance if it's under a parent with a certain skin. */ - -class TBSkinConditionContext { -public: - virtual ~TBSkinConditionContext() { - } - /** Return true if the given target and property equals the given value. */ - virtual bool getCondition(TBSkinCondition::TARGET target, const TBSkinCondition::CONDITION_INFO &info) = 0; -}; - -/** TBSkinElementState has a skin element id that should be used if its state and condition - matches that which is being painted. -*/ - -class TBSkinElementState : public TBLinkOf { -public: - /** Defines how to match states. */ - enum MATCH_RULE { - /** States with "all" (SKIN_STATE_ALL) will also be considered a match. */ - MATCH_RULE_DEFAULT, - /** States with "all" will not be considered a match. */ - MATCH_RULE_ONLY_SPECIFIC_STATE - }; - - bool isMatch(SKIN_STATE state, TBSkinConditionContext &context, MATCH_RULE rule = MATCH_RULE_DEFAULT) const; - - bool isExactMatch(SKIN_STATE state, TBSkinConditionContext &context, MATCH_RULE rule = MATCH_RULE_DEFAULT) const; - - TBID element_id; - SKIN_STATE state; - TBLinkListAutoDeleteOf conditions; -}; - -/** List of state elements in a TBSkinElement. */ - -class TBSkinElementStateList { -public: - ~TBSkinElementStateList(); - - TBSkinElementState * - getStateElement(SKIN_STATE state, TBSkinConditionContext &context, - TBSkinElementState::MATCH_RULE rule = TBSkinElementState::MATCH_RULE_DEFAULT) const; - - TBSkinElementState * - getStateElementExactMatch(SKIN_STATE state, TBSkinConditionContext &context, - TBSkinElementState::MATCH_RULE rule = TBSkinElementState::MATCH_RULE_DEFAULT) const; - - bool hasStateElements() const { - return m_state_elements.hasLinks(); - } - const TBSkinElementState *getFirstElement() const { - return m_state_elements.getFirst(); - } - - void load(TBNode *n); - -private: - TBLinkListOf m_state_elements; -}; - -/** Skin element. - Contains a bitmap fragment (or nullptr) and info specifying how it should be painted. - Also contains padding and other look-specific widget properties. */ -class TBSkinElement { -public: - TBSkinElement(); - ~TBSkinElement(); - - // Skin properties - TBID id; ///< ID of the skin element - core::String name; ///< Name of the skin element, f.ex "TBSelectDropdown.arrow" - core::String bitmap_file; ///< File name of the bitmap (might be empty) - TBBitmapFragment *bitmap; ///< Bitmap fragment containing the graphics, or nullptr. - uint8_t cut; ///< How the bitmap should be sliced using StretchBox. - int16_t expand; ///< How much the skin should expand outside the widgets rect. - SKIN_ELEMENT_TYPE type; ///< Skin element type - bool is_painting; ///< If the skin is being painted (avoiding eternal recursing) - bool is_getting; ///< If the skin is being got (avoiding eternal recursion) - int16_t padding_left; ///< Left padding for any content in the element - int16_t padding_top; ///< Top padding for any content in the element - int16_t padding_right; ///< Right padding for any content in the element - int16_t padding_bottom; ///< Bottom padding for any content in the element - int16_t width; ///< Intrinsic width or SKIN_VALUE_NOT_SPECIFIED - int16_t height; ///< Intrinsic height or SKIN_VALUE_NOT_SPECIFIED - int16_t pref_width; ///< Preferred width or SKIN_VALUE_NOT_SPECIFIED - int16_t pref_height; ///< Preferred height or SKIN_VALUE_NOT_SPECIFIED - int16_t min_width; ///< Minimum width or SKIN_VALUE_NOT_SPECIFIED - int16_t min_height; ///< Minimum height or SKIN_VALUE_NOT_SPECIFIED - int16_t max_width; ///< Maximum width or SKIN_VALUE_NOT_SPECIFIED - int16_t max_height; ///< Maximum height or SKIN_VALUE_NOT_SPECIFIED - int16_t spacing; ///< Spacing used on layout or SKIN_VALUE_NOT_SPECIFIED. - int16_t content_ofs_x; ///< X offset of the content in the widget. - int16_t content_ofs_y; ///< Y offset of the content in the widget. - int16_t img_ofs_x; ///< X offset for type image. Relative to image position (img_position_x). - int16_t img_ofs_y; ///< Y offset for type image. Relative to image position (img_position_y). - int8_t img_position_x; ///< Horizontal position for type image. 0-100 (left to - ///< right in available space). Default 50. - int8_t img_position_y; ///< Vertical position for type image. 0-100 (top to bottom - ///< in available space). Default 50. - int8_t flip_x; ///< The skin is flipped horizontally - int8_t flip_y; ///< The skin is flipped vertically - float opacity; ///< Opacity that should be used for the whole widget (0.f - 1.f). - TBColor text_color; ///< Color of the text in the widget. - TBColor bg_color; ///< Color of the background in the widget. - int16_t bitmap_dpi; ///< The DPI of the bitmap that was loaded. - TBValue tag; ///< This value is free to use for anything. It's not used internally. - - /** Get the minimum width, or SKIN_VALUE_NOT_SPECIFIED if not specified. */ - int getMinWidth() const { - return min_width; - } - - /** Get the minimum height, or SKIN_VALUE_NOT_SPECIFIED if not specified. */ - int getMinHeight() const { - return min_height; - } - - /** Get the intrinsic minimum width. It will be calculated based on the skin properties. */ - int getIntrinsicMinWidth() const; - - /** Get the intrinsic minimum height. It will be calculated based on the skin properties. */ - int getIntrinsicMinHeight() const; - - /** Get the maximum width, or SKIN_VALUE_NOT_SPECIFIED if not specified. */ - int getMaxWidth() const { - return max_width; - } - - /** Get the maximum height, or SKIN_VALUE_NOT_SPECIFIED if not specified. */ - int getMaxHeight() const { - return max_height; - } - - /** Get the preferred width, or SKIN_VALUE_NOT_SPECIFIED if not specified. */ - int getPrefWidth() const { - return pref_width; - } - - /** Get the preferred height, or SKIN_VALUE_NOT_SPECIFIED if not specified. */ - int getPrefHeight() const { - return pref_height; - } - - /** Get the intrinsic width. If not specified using the "width" attribute, it will be - calculated based on the skin properties. If it can't be calculated it will return - SKIN_VALUE_NOT_SPECIFIED. */ - int getIntrinsicWidth() const; - - /** Get the intrinsic height. If not specified using the "height" attribute, it will be - calculated based on the skin properties. If it can't be calculated it will return - SKIN_VALUE_NOT_SPECIFIED. */ - int getIntrinsicHeight() const; - - /** Set the DPI that the bitmap was loaded in. This may modify properties - to compensate for the bitmap resolution. */ - void setBitmapDPI(const TBDimensionConverter &dimConv, int bitmapDpi); - - /** List of override elements (See TBSkin::PaintSkin) */ - TBSkinElementStateList m_override_elements; - - /** List of strong-override elements (See TBSkin::PaintSkin) */ - TBSkinElementStateList m_strong_override_elements; - - /** List of child elements (See TBSkin::PaintSkin) */ - TBSkinElementStateList m_child_elements; - - /** List of overlay elements (See TBSkin::PaintSkin) */ - TBSkinElementStateList m_overlay_elements; - - /** Check if there's a exact or partial match for the given state in either - override, child or overlay element list. - State elements with state "all" will be ignored. */ - bool hasState(SKIN_STATE state, TBSkinConditionContext &context); - - /** Return true if this element has overlay elements. */ - bool hasOverlayElements() const { - return m_overlay_elements.hasStateElements(); - } - - void load(TBNode *n, TBSkin *skin, const char *skin_path); -}; - -class TBSkinListener { -public: - virtual ~TBSkinListener() { - } - /** Called when a skin element has been loaded from the given TBNode. - NOTE: This may be called multiple times on elements that occur multiple times - in the skin or is overridden in an override skin. - This method can be used to f.ex feed custom properties into element->tag. */ - virtual void onSkinElementLoaded(TBSkin *skin, TBSkinElement *element, TBNode *node) = 0; -}; - -/** TBSkin contains a list of TBSkinElement. */ -class TBSkin : private TBRendererListener { -public: - TBSkin(); - virtual ~TBSkin(); - - /** Set the listener for this skin. */ - void setListener(TBSkinListener *listener) { - m_listener = listener; - } - TBSkinListener *getListener() const { - return m_listener; - } - - /** Load the skin file and the bitmaps it refers to. - - If override_skin_file is specified, it will also be loaded into this skin after - loading skin_file. Elements using the same name will override any previosly - read data for the same element. Known limitation: Clone can currently only - clone elements in the same file! - - Returns true on success, and all bitmaps referred to also loaded successfully. */ - bool load(const char *skinFile, const char *overrideSkinFile = nullptr); - - /** Unload all bitmaps used in this skin. */ - void unloadBitmaps(); - - /** Reload all bitmaps used in this skin. Calls UnloadBitmaps first to ensure no bitmaps - are loaded before loading new ones. */ - bool reloadBitmaps(); - - /** Get the dimension converter used for the current skin. This dimension converter - converts to px by the same factor as the skin (based on the skin DPI settings). */ - const TBDimensionConverter *getDimensionConverter() const { - return &m_dim_conv; - } - - /** Get the skin element with the given id. - Returns nullptr if there's no match. */ - TBSkinElement *getSkinElement(const TBID &skin_id) const; - - /** Get the skin element with the given id and state. - This is like calling GetSkinElement and also following any strong overrides that - match the current state (if any). See details about strong overrides in PaintSkin. - Returns nullptr if there's no match. */ - TBSkinElement *getSkinElementStrongOverride(const TBID &skin_id, SKIN_STATE state, - TBSkinConditionContext &context) const; - - /** Get the default text color for all skin elements */ - TBColor getDefaultTextColor() const { - return m_default_text_color; - } - - /** Get the default disabled opacity for all skin elements */ - float getDefaultDisabledOpacity() const { - return m_default_disabled_opacity; - } - - /** Get the default placeholder opacity for all skin elements */ - float getDefaultPlaceholderOpacity() const { - return m_default_placeholder_opacity; - } - - /** Get the default layout spacing in pixels. */ - int getDefaultSpacing() const { - return m_default_spacing; - } - - /** Paint the skin at dst_rect. - - Strong override elements: - -Strong override elements are like override elements, but they don't only apply - when painting. They also override padding and other things that might affect - the layout of the widget having the skin set. - - Override elements: - -If there is a override element with the exact matching state, it will paint - the override *instead* if the base skin. If no exact match was found, it will - check for a partial match and paint that *instead* of the base skin. - - Child elements: - -It will paint *all* child elements that match the current state ("all" can be specified - as state so it will always be painted). The elements are painted in the order they are - specified in the skin. - - Special elements: - -There's some special generic skin elements used by TBWidget (see TBWidget::setSkinBg) - - Overlay elements: - -Overlay elements are painted separately, from PaintSkinOverlay (when all sibling - widgets has been painted). As with child elements, all overlay elements that match - the current state will be painted in the order they are specified in the skin. - - Return the skin element used (after following override elements), - or nullptr if no skin element was found matching the skin_id. */ - TBSkinElement *paintSkin(const TBRect &dstRect, const TBID &skinId, SKIN_STATE state, - TBSkinConditionContext &context); - - /** Paint the skin at dst_rect. Just like the PaintSkin above, but takes a specific - skin element instead of looking it up from the id. */ - TBSkinElement *paintSkin(const TBRect &dst_rect, TBSkinElement *element, SKIN_STATE state, - TBSkinConditionContext &context); - - /** Paint the overlay elements for the given skin element and state. */ - void paintSkinOverlay(const TBRect &dst_rect, TBSkinElement *element, SKIN_STATE state, - TBSkinConditionContext &context); - - /** Paint a rectangle outline inside dst_rect with the given thickness and color. */ - void paintRect(const TBRect &dst_rect, const TBColor &color, int thickness); - - /** Paint a filled rectangle with the given color. */ - void paintRectFill(const TBRect &dst_rect, const TBColor &color); - -#ifdef TB_RUNTIME_DEBUG_INFO - /** Render the skin bitmaps on screen, to analyze fragment positioning. */ - void debug(); -#endif - - /** Get the fragment manager. */ - TBBitmapFragmentManager *getFragmentManager() { - return &m_frag_manager; - } - - // Implementing TBRendererListener - virtual void onContextLost(); - virtual void onContextRestored(); - -private: - friend class TBSkinElement; - TBSkinListener *m_listener; - TBHashTableAutoDeleteOf m_elements; ///< All skin elements for this skin. - TBBitmapFragmentManager m_frag_manager; ///< Fragment manager - TBDimensionConverter m_dim_conv; ///< Dimension converter - TBColor m_default_text_color; ///< Default text color for all skin elements - TBBitmapFragment *m_color_frag; ///< Used for painting single color. - float m_default_disabled_opacity; ///< Disabled opacity - float m_default_placeholder_opacity; ///< Placeholder opacity - int16_t m_default_spacing; ///< Default layout spacing - bool loadInternal(const char *skin_file); - bool reloadBitmapsInternal(); - void paintElement(const TBRect &dst_rect, TBSkinElement *element); - void paintElementBGColor(const TBRect &dst_rect, TBSkinElement *element); - void paintElementImage(const TBRect &dst_rect, TBSkinElement *element); - void paintElementTile(const TBRect &dst_rect, TBSkinElement *element); - void paintElementStretchImage(const TBRect &dst_rect, TBSkinElement *element); - void paintElementStretchBox(const TBRect &dstRect, TBSkinElement *element, bool fillCenter); - TBRect getFlippedRect(const TBRect &src_rect, TBSkinElement *element) const; - int getPxFromNode(TBNode *node, int def_value) const; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_skin_util.cpp b/src/modules/ui/turbobadger/tb/tb_skin_util.cpp deleted file mode 100644 index 47c2fabec..000000000 --- a/src/modules/ui/turbobadger/tb/tb_skin_util.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file - */ - -#include "tb_skin_util.h" - -namespace tb { - -static int getFadeoutSize(int scrolledDistance, int fadeoutLength) { - // Make it appear gradually - // float factor = scrolled_distance / 10.f; - // factor = Clamp(factor, 0.5f, 1); - // return (int)(fadeout_length * factor); - return scrolledDistance > 0 ? fadeoutLength : 0; -} - -void drawEdgeFadeout(const TBRect &dstRect, const TBID &skinX, const TBID &skinY, int left, int top, int right, - int bottom) { - if (TBSkinElement *skin = g_tb_skin->getSkinElement(skinX)) { - if (skin->bitmap != nullptr) { - const int bw = skin->bitmap->width(); - const int bh = skin->bitmap->height(); - int dw; - if ((dw = getFadeoutSize(left, bw)) > 0) { - g_renderer->drawBitmap(TBRect(dstRect.x, dstRect.y, dw, dstRect.h), TBRect(0, 0, bw, bh), skin->bitmap); - } - if ((dw = getFadeoutSize(right, bw)) > 0) { - g_renderer->drawBitmap(TBRect(dstRect.x + dstRect.w - dw, dstRect.y, dw, dstRect.h), - TBRect(bw, 0, -bw, bh), skin->bitmap); - } - } - } - if (TBSkinElement *skin = g_tb_skin->getSkinElement(skinY)) { - if (skin->bitmap != nullptr) { - const int bw = skin->bitmap->width(); - const int bh = skin->bitmap->height(); - int dh; - if ((dh = getFadeoutSize(top, bh)) > 0) { - g_renderer->drawBitmap(TBRect(dstRect.x, dstRect.y, dstRect.w, dh), TBRect(0, 0, bw, bh), skin->bitmap); - } - if ((dh = getFadeoutSize(bottom, bh)) > 0) { - g_renderer->drawBitmap(TBRect(dstRect.x, dstRect.y + dstRect.h - dh, dstRect.w, dh), - TBRect(0, bh, bw, -bh), skin->bitmap); - } - } - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_skin_util.h b/src/modules/ui/turbobadger/tb/tb_skin_util.h deleted file mode 100644 index 33354ed5d..000000000 --- a/src/modules/ui/turbobadger/tb/tb_skin_util.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_skin.h" - -namespace tb { - -/** Draw fade out skin elements at the edges of dst_rect if needed. - It indicates to the user that there is hidden content. - left, top, right, bottom specifies the (positive) distance scrolled - from the limit. */ -void drawEdgeFadeout(const TBRect &dstRect, const TBID &skinX, const TBID &skinY, int left, int top, int right, - int bottom); - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_sort.h b/src/modules/ui/turbobadger/tb/tb_sort.h deleted file mode 100644 index 5b218c4cf..000000000 --- a/src/modules/ui/turbobadger/tb/tb_sort.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include - -namespace tb { - -template -static void insertion_sort(TYPE *elements, size_t elementcount, CONTEXT context, - int (*cmp)(CONTEXT context, const TYPE *a, const TYPE *b)) { - size_t i; - size_t j; - for (i = 1; i < elementcount; i++) { - TYPE value = elements[i]; - for (j = i; j > 0 && cmp(context, &value, &elements[j - 1]) < 0; j--) { - elements[j] = elements[j - 1]; - } - elements[j] = value; - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_str.cpp b/src/modules/ui/turbobadger/tb/tb_str.cpp deleted file mode 100644 index 640f0f40d..000000000 --- a/src/modules/ui/turbobadger/tb/tb_str.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file - */ - -#include "tb_str.h" -#include "core/Assert.h" - -namespace tb { - -const char *stristr(const char *arg1, const char *arg2) { - const char *a; - const char *b; - - for (; *arg1 != 0; arg1++) { - a = arg1; - b = arg2; - while (SDL_toupper(*a++) == SDL_toupper(*b++)) { - if (*b == 0) { - return arg1; - } - } - } - return nullptr; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_str.h b/src/modules/ui/turbobadger/tb/tb_str.h deleted file mode 100644 index 007a9a284..000000000 --- a/src/modules/ui/turbobadger/tb/tb_str.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_types.h" -#include "core/String.h" -#include - -namespace tb { - -/** Some useful C-like functions that's missing in the standard. */ -const char *stristr(const char *arg1, const char *arg2); - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_style_edit.cpp b/src/modules/ui/turbobadger/tb/tb_style_edit.cpp deleted file mode 100644 index b46ea838c..000000000 --- a/src/modules/ui/turbobadger/tb/tb_style_edit.cpp +++ /dev/null @@ -1,2013 +0,0 @@ -/** - * @file - */ - -#include "tb_style_edit.h" -#include "core/Assert.h" -#include "tb_font_renderer.h" -#include "tb_style_edit_content.h" -#include "tb_system.h" -#include "tb_tempbuffer.h" -#include "tb_widgets_common.h" -#include "utf8/utf8.h" - -namespace tb { - -#if 0 // Enable for some graphical debugging -#define TMPDEBUG(expr) expr -#define nTMPDEBUG(expr) -#else -#define TMPDEBUG(expr) -#define nTMPDEBUG(expr) expr -#endif - -const int TAB_SPACE = 4; - -const char *special_char_newln = "¶"; // 00B6 PILCROW SIGN -const char *special_char_space = "·"; // 00B7 MIDDLE DOT -const char *special_char_tab = "»"; // 00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -const char *special_char_password = "•"; // 2022 BULLET - -static bool is_space(int8_t c) { - switch (c) { - case ' ': - return true; - } - return false; -} - -static bool is_linebreak(int8_t c) { - switch (c) { - case '\n': - case '\r': - case 0: - return true; - } - return false; -} - -static bool is_wordbreak(int8_t c) { - switch (c) { - case 0: - case '\n': - case '\r': - case '\t': - case '\"': - case '\'': - case '/': - case '\\': - case '[': - case ']': - case '{': - case '}': - case '(': - case ')': - case '>': - case '<': - case '-': - case '+': - case '*': - case ',': - case '.': - case ';': - case ':': - case '&': - case '|': - case '#': - case '!': - case '=': - case '^': - case '~': - case '?': - case '@': - case '$': - return true; - } - return is_space(c); -} - -/** Check if no line wrapping is allowed before the character at the given offset. - The string must be null terminated. */ -static bool is_never_break_before(const char *str, int ofs) { - switch (str[ofs]) { - case '\n': - case '\r': - case ' ': - case '-': - case '.': - case ',': - case ':': - case ';': - case '!': - case '?': - case ')': - case ']': - case '}': - case '>': - return true; - case '\'': - case '"': - // Simple test if it's the first quote in a word surrounded by space. - return ofs > 0 && !is_space(str[ofs - 1]); - default: - return false; - } -} - -/** Check if no line wrapping is allowed after the character at the given offset. - The string must be null terminated. */ -static bool is_never_break_after(const char *str, int ofs) { - switch (str[ofs]) { - case '(': - case '[': - case '{': - case '<': - case '@': - case '$': - return true; - case '\'': - case '"': - // Simple test if it's the last quote in a word surrounded by space. - return !is_space(str[ofs + 1]); - default: - return false; - } -} - -static bool getNextFragment(const char *text, TBTextFragmentContentFactory *contentFactory, int *fragLen, - bool *isEmbed) { - if (text[0] == '\t') { - *fragLen = 1; - return text[1] != 0; - } - if (text[0] == 0) // happens when not setting text and maby when setting "" - { - *fragLen = 0; - return false; - } - if (text[0] == '\r' || text[0] == '\n') { - int len = (text[0] == '\r' && text[1] == '\n') ? 2 : 1; - *fragLen = len; - return false; - } - if (contentFactory != nullptr) { - if (int content_len = contentFactory->getContent(text)) { - *fragLen = content_len; - *isEmbed = true; - return text[content_len] != 0; - } - } - int i = 0; - while (!is_wordbreak(text[i])) { - i++; - } - if (i == 0) { - if (is_wordbreak(text[i])) { - i++; - } - } - *fragLen = i; - return text[i] != 0; -} - -// == TBSelection ================================================== - -TBSelection::TBSelection(TBStyleEdit *styledit) : styledit(styledit) { -} - -void TBSelection::correctOrder() { - if (start.block == stop.block && start.ofs == stop.ofs) { - selectNothing(); - } else { - if ((start.block == stop.block && start.ofs > stop.ofs) || - (start.block != stop.block && start.block->ypos > stop.block->ypos)) { - TBTextOfs tmp = start; - start = stop; - stop = tmp; - } - } -} - -void TBSelection::copyToClipboard() { - if (isSelected()) { - core::String text; - if (getText(text)) { - TBClipboard::setText(text.c_str()); - } - } -} - -void TBSelection::invalidate() const { - TBBlock *block = start.block; - while (block != nullptr) { - block->invalidate(); - if (block == stop.block) { - break; - } - block = block->getNext(); - } -} - -void TBSelection::select(const TBTextOfs &newStart, const TBTextOfs &newStop) { - invalidate(); - start.set(newStart); - stop.set(newStop); - correctOrder(); - invalidate(); -} - -void TBSelection::select(const TBPoint &from, const TBPoint &to) { - invalidate(); - styledit->caret.place(from); - start.set(styledit->caret.pos); - styledit->caret.place(to); - stop.set(styledit->caret.pos); - correctOrder(); - invalidate(); - styledit->caret.updateWantedX(); -} - -void TBSelection::select(int globOfsFrom, int globOfsTo) { - TBTextOfs ofs1; - TBTextOfs ofs2; - ofs1.setGlobalOfs(styledit, globOfsFrom); - ofs2.setGlobalOfs(styledit, globOfsTo); - select(ofs1, ofs2); -} - -void TBSelection::selectToCaret(TBBlock *oldCaretBlock, int32_t oldCaretOfs) { - invalidate(); - if (start.block == nullptr) { - start.set(oldCaretBlock, oldCaretOfs); - stop.set(styledit->caret.pos); - } else { - if (start.block == oldCaretBlock && start.ofs == oldCaretOfs) { - start.set(styledit->caret.pos); - } else { - stop.set(styledit->caret.pos); - } - } - correctOrder(); - invalidate(); -} - -void TBSelection::selectAll() { - start.set(styledit->blocks.getFirst(), 0); - stop.set(styledit->blocks.getLast(), styledit->blocks.getLast()->str_len); - invalidate(); -} - -void TBSelection::selectNothing() { - invalidate(); - start.set(nullptr, 0); - stop.set(nullptr, 0); -} - -bool TBSelection::isBlockSelected(const TBBlock *block) const { - if (!isSelected()) { - return false; - } - return block->ypos >= start.block->ypos && block->ypos <= stop.block->ypos; -} - -bool TBSelection::isFragmentSelected(const TBBlock *block, TBTextFragment *elm) const { - if (!isSelected()) { - return false; - } - if (start.block == stop.block) { - if (block != start.block) { - return false; - } - return start.ofs < elm->ofs + elm->len && stop.ofs >= elm->ofs; - } - if (block->ypos > start.block->ypos && block->ypos < stop.block->ypos) { - return true; - } - if (block->ypos == start.block->ypos && elm->ofs + elm->len > start.ofs) { - return true; - } - if (block->ypos == stop.block->ypos && elm->ofs < stop.ofs) { - return true; - } - return false; -} - -bool TBSelection::isSelected() const { - return start.block != nullptr; -} - -void TBSelection::removeContent() { - if (!isSelected()) { - return; - } - styledit->beginLockScrollbars(); - if (start.block == stop.block) { - if (!styledit->undoredo.applying) { - styledit->undoredo.commit(styledit, start.getGlobalOfs(styledit), stop.ofs - start.ofs, - start.block->str.c_str() + start.ofs, false); - } - start.block->removeContent(start.ofs, stop.ofs - start.ofs); - } else { - // Remove text in first block - TBTempBuffer commit_string; - int32_t start_gofs = 0; - if (!styledit->undoredo.applying) { - start_gofs = start.getGlobalOfs(styledit); - commit_string.append(start.block->str.c_str() + start.ofs, start.block->str_len - start.ofs); - } - start.block->removeContent(start.ofs, start.block->str_len - start.ofs); - - // Remove text in all block in between start and stop - TBBlock *block = start.block->getNext(); - while (block != stop.block) { - if (!styledit->undoredo.applying) { - commit_string.append(block->str.c_str(), block->str_len); - } - - TBBlock *next = block->getNext(); - styledit->blocks.doDelete(block); - block = next; - } - - // Remove text in last block - if (!styledit->undoredo.applying) { - commit_string.append(stop.block->str.c_str(), stop.ofs); - styledit->undoredo.commit(styledit, start_gofs, commit_string.getAppendPos(), commit_string.getData(), - false); - } - stop.block->removeContent(0, stop.ofs); - } - stop.block->merge(); - start.block->merge(); - styledit->caret.place(start.block, start.ofs); - styledit->caret.updateWantedX(); - selectNothing(); - styledit->endLockScrollbars(); -} - -bool TBSelection::getText(core::String &text) const { - if (!isSelected()) { - text.clear(); - return true; - } - if (start.block == stop.block) { - text.append(start.block->str.c_str() + start.ofs, stop.ofs - start.ofs); - } else { - TBTempBuffer buf; - buf.append(start.block->str.c_str() + start.ofs, start.block->str_len - start.ofs); - TBBlock *block = start.block->getNext(); - while (block != stop.block) { - buf.append(block->str.c_str(), block->str_len); - block = block->getNext(); - } - // FIX: Add methods to change data owner from temp buffer to string! - buf.append(stop.block->str.c_str(), stop.ofs); - text = core::String((char *)buf.getData(), buf.getAppendPos()); - } - return true; -} - -// == TBTextOfs ========================================================================= - -int32_t TBTextOfs::getGlobalOfs(TBStyleEdit *se) const { - int32_t gofs = 0; - TBBlock *b = se->blocks.getFirst(); - while ((b != nullptr) && b != block) { - gofs += b->str_len; - b = b->getNext(); - } - gofs += ofs; - return gofs; -} - -bool TBTextOfs::setGlobalOfs(TBStyleEdit *se, int32_t gofs) { - TBBlock *b = se->blocks.getFirst(); - while (b != nullptr) { - int b_len = b->str_len; - if (gofs <= b_len) { - block = b; - ofs = gofs; - return true; - } - gofs -= b_len; - b = b->getNext(); - } - core_assert(!"out of range! not a valid global offset!"); - return false; -} - -// == TBCaret ============================================================================ - -TBCaret::TBCaret(TBStyleEdit *styledit) - : styledit(styledit), x(0), y(0), width(2), height(0), wanted_x(0), on(false), prefer_first(true) { -} - -void TBCaret::invalidate() { - if (styledit->listener != nullptr) { - styledit->listener->invalidate(TBRect(x - styledit->scroll_x, y - styledit->scroll_y, width, height)); - } -} - -void TBCaret::updatePos() { - invalidate(); - TBTextFragment *fragment = getFragment(); - x = fragment->xpos + fragment->getCharX(pos.block, styledit->font, pos.ofs - fragment->ofs); - y = fragment->ypos + pos.block->ypos; - height = fragment->getHeight(pos.block, styledit->font); - if (height == 0) { - // If we don't have height, we're probably inside a style switch embed. - y = fragment->line_ypos + pos.block->ypos; - height = fragment->line_height; - } - invalidate(); -} - -bool TBCaret::move(bool forward, bool word) { - // Make it stay on the same line if it reach the wrap point. - prefer_first = forward; - if (this->styledit->packed.password_on) { - word = false; - } - - int len = pos.block->str_len; - if (word && !(forward && pos.ofs == len) && !(!forward && pos.ofs == 0)) { - const char *str = pos.block->str.c_str(); - if (forward) { - if (is_linebreak(str[pos.ofs])) { - pos.ofs++; - } else if (is_wordbreak(str[pos.ofs])) { - while (pos.ofs < len && is_wordbreak(str[pos.ofs]) && !is_linebreak(str[pos.ofs])) { - pos.ofs++; - } - } else { - while (pos.ofs < len && !is_wordbreak(str[pos.ofs])) { - pos.ofs++; - } - while (pos.ofs < len && is_space(str[pos.ofs])) { - pos.ofs++; - } - } - } else if (pos.ofs > 0) { - while (pos.ofs > 0 && is_space(str[pos.ofs - 1])) { - pos.ofs--; - } - if (pos.ofs > 0 && is_wordbreak(str[pos.ofs - 1])) { - while (pos.ofs > 0 && is_wordbreak(str[pos.ofs - 1])) { - pos.ofs--; - } - } else { - while (pos.ofs > 0 && !is_wordbreak(str[pos.ofs - 1])) { - pos.ofs--; - } - } - } - } else { - if (forward && pos.ofs >= pos.block->str_len && (pos.block->getNext() != nullptr)) { - pos.block = pos.block->getNext(); - pos.ofs = 0; - } else if (!forward && pos.ofs <= 0 && (pos.block->prev != nullptr)) { - pos.block = pos.block->getPrev(); - pos.ofs = pos.block->str_len; - } else { - int i = pos.ofs; - if (forward) { - utf8::move_inc(pos.block->str.c_str(), &i, pos.block->str_len); - } else { - utf8::move_dec(pos.block->str.c_str(), &i); - } - pos.ofs = i; - } - } - return place(pos.block, pos.ofs, true, forward); -} - -bool TBCaret::place(const TBPoint &point) { - TBBlock *block = styledit->findBlock(point.y); - TBTextFragment *fragment = block->findFragment(point.x, point.y - block->ypos); - int ofs = fragment->ofs + fragment->getCharOfs(block, styledit->font, point.x - fragment->xpos); - - if (place(block, ofs)) { - if (getFragment() != fragment) { - prefer_first = !prefer_first; - place(block, ofs); - } - return true; - } - return false; -} - -void TBCaret::place(TB_CARET_POS pos) { - if (pos == TB_CARET_POS_BEGINNING) { - place(styledit->blocks.getFirst(), 0); - } else if (pos == TB_CARET_POS_END) { - place(styledit->blocks.getLast(), styledit->blocks.getLast()->str_len); - } -} - -bool TBCaret::place(TBBlock *block, int ofs, bool allowSnap, bool snapForward) { - if (block != nullptr) { - while ((block->getNext() != nullptr) && ofs > block->str_len) { - ofs -= block->str_len; - block = block->getNext(); - } - while ((block->prev != nullptr) && ofs < 0) { - block = block->getPrev(); - ofs += block->str_len; - } - if (ofs < 0) { - ofs = 0; - } - if (ofs > block->str_len) { - ofs = block->str_len; - } - - // Avoid being inside linebreak - if (allowSnap) { - TBTextFragment *fragment = block->findFragment(ofs); - if (ofs > fragment->ofs && fragment->isBreak()) { - if (snapForward && (block->getNext() != nullptr)) { - block = block->getNext(); - ofs = 0; - } else { - ofs = fragment->ofs; - } - } - } - } - - bool changed = (pos.block != block || pos.ofs != ofs); - pos.set(block, ofs); - - if (block != nullptr) { - updatePos(); - } - - return changed; -} - -void TBCaret::avoidLineBreak() { - TBTextFragment *fragment = getFragment(); - if (pos.ofs > fragment->ofs && fragment->isBreak()) { - pos.ofs = fragment->ofs; - } - updatePos(); -} - -void TBCaret::paint(int32_t translateX, int32_t translateY) { - // if (on && !(styledit->select_state && styledit->selection.isSelected())) - if (on || (styledit->select_state != 0)) { - styledit->listener->drawCaret(TBRect(translateX + x, translateY + y, width, height)); - } -} - -void TBCaret::resetBlink() { - styledit->listener->caretBlinkStop(); - on = true; - styledit->listener->caretBlinkStart(); -} - -void TBCaret::updateWantedX() { - wanted_x = x; -} - -TBTextFragment *TBCaret::getFragment() { - return pos.block->findFragment(pos.ofs, prefer_first); -} - -void TBCaret::switchBlock(bool second) { -} - -void TBCaret::setGlobalOfs(int32_t gofs, bool allowSnap, bool snapForward) { - TBTextOfs ofs; - if (ofs.setGlobalOfs(styledit, gofs)) { - place(ofs.block, ofs.ofs, allowSnap, snapForward); - } -} - -// == TBTextProps ======================================================================= - -void TBTextProps::reset(const TBFontDescription &fontDesc, const TBColor &textColor) { - core_assert(next_index == 0); - while (list.getNumItems() > 0) { - delete list.get(0); - list.remove(0); - } - next_index = 0; - base.font_desc = fontDesc; - base.text_color = textColor; - base.underline = false; - data = &base; -} - -TBTextProps::Data *TBTextProps::push() { - if (next_index >= list.getNumItems()) { - Data *data = new Data; - if (data == nullptr) { - return nullptr; - } - if (!list.add(data)) { - delete data; - return nullptr; - } - } - Data *next = list.get(next_index++); - *next = *data; - data = next; - return data; -} - -void TBTextProps::pop() { - if (next_index == 0) { - return; // Unballanced or we previously got OOM. - } - next_index--; - data = next_index > 0 ? list.get(next_index - 1) : &base; -} - -TBFontFace *TBTextProps::getFont() const { - return g_font_manager->getFontFace(data->font_desc); -} - -// ============================================================================ - -TBBlock::TBBlock(TBStyleEdit *styledit) - : styledit(styledit), ypos(0), height(0), align(styledit->align), line_width_max(0), str_len(0), syntax_data(0) { -} - -TBBlock::~TBBlock() { - clear(); -} - -void TBBlock::clear() { - fragments.deleteAll(); -} - -void TBBlock::set(const char *newstr, int32_t len) { - str = core::String(newstr, len); - str_len = len; - split(); - layout(true, true); -} - -void TBBlock::setAlign(TB_TEXT_ALIGN align) { - if (this->align == align) { - return; - } - this->align = align; - layout(false, false); -} - -int32_t TBBlock::insertText(int32_t ofs, const char *text, int32_t len, bool allowLineRecurse) { - styledit->beginLockScrollbars(); - int first_line_len = len; - for (int i = 0; i < len; i++) { - if (text[i] == '\r' || text[i] == '\n') { - first_line_len = i; - // Include the line break too but not for single lines - if (!styledit->packed.multiline_on) { - break; - } - if (text[i] == '\r' && text[i + 1] == '\n') { - first_line_len++; - } - first_line_len++; - break; - } - } - - int32_t inserted_len = first_line_len; - str.insert(ofs, text, first_line_len); - str_len += first_line_len; - - split(); - layout(true, true); - - // Add the rest which was after the linebreak. - if (allowLineRecurse && styledit->packed.multiline_on) { - // Instead of recursively calling InsertText, we will loop through them all here - TBBlock *next_block = getNext(); - const char *next_line_ptr = &text[first_line_len]; - int remaining = len - first_line_len; - while (remaining > 0) { - if (next_block == nullptr) { - next_block = new TBBlock(styledit); - styledit->blocks.addLast(next_block); - } - int consumed = next_block->insertText(0, next_line_ptr, remaining, false); - next_line_ptr += consumed; - inserted_len += consumed; - remaining -= consumed; - next_block = next_block->getNext(); - } - } - styledit->endLockScrollbars(); - return inserted_len; -} - -void TBBlock::removeContent(int32_t ofs, int32_t len) { - if (len == 0) { - return; - } - str.erase(ofs, len); - str_len -= len; - layout(true, true); -} - -void TBBlock::split() { - int32_t len = str_len; - int brlen = - 1; // FIX: skip ending newline fragment but not if there is several newlines and check for singleline newline. - if (len > 1 && str.c_str()[len - 2] == '\r' && str.c_str()[len - 1] == '\n') { - brlen++; - } - len -= brlen; - for (int i = 0; i < len; i++) { - if (is_linebreak(str.c_str()[i])) { - TBBlock *block = new TBBlock(styledit); - if (block == nullptr) { - return; - } - styledit->blocks.addAfter(block, this); - - if (i < len - 1 && str.c_str()[i] == '\r' && str.c_str()[i + 1] == '\n') { - i++; - } - i++; - - len = len + brlen - i; - block->set(str.c_str() + i, len); - str.erase(i, len); - str_len -= len; - break; - } - } -} - -void TBBlock::merge() { - TBBlock *next_block = getNext(); - if ((next_block != nullptr) && !fragments.getLast()->isBreak()) { - str.append(getNext()->str); - str_len = str.size(); - - styledit->blocks.doDelete(next_block); - - height = 0; // Ensure that Layout propagate height to remaining blocks. - layout(true, true); - } -} - -int32_t TBBlock::calculateTabWidth(TBFontFace *font, int32_t xpos) const { - int tabsize = font->getStringWidth("x", 1) * TAB_SPACE; - int p2 = int(xpos / tabsize) * tabsize + tabsize; - return p2 - xpos; -} - -int32_t TBBlock::calculateStringWidth(TBFontFace *font, const char *str, int len) const { - if (styledit->packed.password_on) { - // Convert the length in number or characters, since that's what matters for password width. - len = utf8::count_characters(str, len); - return font->getStringWidth(special_char_password, SDL_strlen(special_char_password)) * len; - } - return font->getStringWidth(str, len); -} - -int32_t TBBlock::calculateLineHeight(TBFontFace *font) const { - return font->getHeight(); -} - -int32_t TBBlock::calculateBaseline(TBFontFace *font) const { - return font->getAscent(); -} - -int TBBlock::getStartIndentation(TBFontFace *font, int firstLineLen) const { - // Lines beginning with whitespace or list points, should - // indent to the same as the beginning when wrapped. - int indentation = 0; - int i = 0; - while (i < firstLineLen) { - const char *current_str = str.c_str() + i; - UCS4 uc = utf8::decode_next(str.c_str(), &i, firstLineLen); - switch (uc) { - case '\t': - indentation += calculateTabWidth(font, indentation); - continue; - case ' ': - case '-': - case '*': - indentation += calculateStringWidth(font, current_str, 1); - continue; - case 0x2022: // BULLET - indentation += calculateStringWidth(font, current_str, 3); - continue; - } - break; - } - return indentation; -} - -void TBBlock::layout(bool updateFragments, bool propagateHeight) { - // Create fragments from the word fragments - if (updateFragments || (fragments.getFirst() == nullptr)) { - clear(); - - int ofs = 0; - const char *text = str.c_str(); - while (true) { - int frag_len; - bool is_embed = false; - bool more = getNextFragment(&text[ofs], styledit->packed.styling_on ? styledit->content_factory : nullptr, - &frag_len, &is_embed); - - TBTextFragment *fragment = new TBTextFragment(); - if (fragment == nullptr) { - break; - } - - fragment->init(this, ofs, frag_len); - - if (is_embed) { - fragment->content = styledit->content_factory->createFragmentContent(&text[ofs], frag_len); - } - - fragments.addLast(fragment); - ofs += frag_len; - - if (!more) { - break; - } - } - if (styledit->syntax_highlighter != nullptr) { - styledit->syntax_highlighter->onFragmentsUpdated(this); - } - } - - // Layout - - if (styledit->layout_width <= 0 && styledit->getSizeAffectsLayout()) { - // Don't layout if we have no space. This will happen when setting text - // before the widget has been layouted. We will relayout when we are resized. - return; - } - - int old_line_width_max = line_width_max; - line_width_max = 0; - int line_ypos = 0; - int first_line_indentation = 0; - TBTextFragment *first_fragment_on_line = fragments.getFirst(); - - while (first_fragment_on_line != nullptr) { - int line_width = 0; - - // Get the last fragment that should be laid out on the line while - // calculating line width and preliminary x positions for the fragments. - - TBTextFragment *last_fragment_on_line = fragments.getLast(); - if (styledit->packed.wrapping) { - // If we should wrap, search for the last allowed break point before the overflow. - TBTextFragment *allowed_last_fragment = nullptr; - - int line_xpos = first_line_indentation; - for (TBTextFragment *fragment = first_fragment_on_line; fragment != nullptr; - fragment = fragment->getNext()) { - // Give the fragment the current x. Then tab widths are calculated properly in GetWidth. - fragment->xpos = line_xpos; - int fragment_w = fragment->getWidth(this, styledit->font); - - // Check if we overflow - bool overflow = line_xpos + fragment_w > styledit->layout_width; - - if (overflow && (allowed_last_fragment != nullptr)) { - last_fragment_on_line = allowed_last_fragment; - break; - } - - // Check if this is a allowed break position - if (fragment->getAllowBreakAfter(this)) { - if ((fragment->getNext() == nullptr) || fragment->getNext()->getAllowBreakBefore(this)) { - allowed_last_fragment = fragment; - line_width = line_xpos + fragment_w; - } - } - - line_xpos += fragment_w; - } - if (allowed_last_fragment == nullptr) { - line_width = line_xpos; - } - } else { - // When wrapping is off, just measure and set pos. - line_width = first_line_indentation; - for (TBTextFragment *fragment = first_fragment_on_line; fragment != nullptr; - fragment = fragment->getNext()) { - fragment->xpos = line_width; - line_width += fragment->getWidth(this, styledit->font); - } - } - - // Commit line - Layout each fragment on the line. - - int line_height = 0; - int line_baseline = 0; - TBTextFragment *fragment = first_fragment_on_line; - while (fragment != nullptr) { - line_height = Max(fragment->getHeight(this, styledit->font), line_height); - line_baseline = Max(fragment->getBaseline(this, styledit->font), line_baseline); - - // These positions are not final. Will be adjusted below. - fragment->ypos = line_ypos; - - if (fragment == last_fragment_on_line) { - break; - } - fragment = fragment->getNext(); - } - - // Adjust the position of fragments on the line - now when we know the line totals. - // x change because of alignment, y change because of fragment baseline vs line baseline. - - int32_t xofs = 0; - if (align == TB_TEXT_ALIGN_RIGHT) { - xofs = styledit->layout_width - line_width; - } else if (align == TB_TEXT_ALIGN_CENTER) { - xofs = (styledit->layout_width - line_width) / 2; - } - - int adjusted_line_height = line_height; - fragment = first_fragment_on_line; - while (fragment != nullptr) { - // The fragment need to know these later. - fragment->line_ypos = line_ypos; - fragment->line_height = line_height; - - // Adjust the position - fragment->ypos += line_baseline - fragment->getBaseline(this, styledit->font); - fragment->xpos += xofs; - - // We now know the final position so update content. - fragment->updateContentPos(this); - - // Total line height may now have changed a bit. - adjusted_line_height = - Max(adjusted_line_height, line_baseline - fragment->getBaseline(this, styledit->font) + - fragment->getHeight(this, styledit->font)); - - if (fragment == last_fragment_on_line) { - break; - } - fragment = fragment->getNext(); - } - - // Update line_height set on fragments if needed - if (line_height != adjusted_line_height) { - for (fragment = first_fragment_on_line; fragment != last_fragment_on_line->getNext(); - fragment = fragment->getNext()) { - fragment->line_height = adjusted_line_height; - } - } - - line_width_max = Max(line_width_max, line_width); - - // This was the first line so calculate the indentation to use for the other lines. - if (styledit->packed.wrapping && first_fragment_on_line == fragments.getFirst()) { - first_line_indentation = - getStartIndentation(styledit->font, last_fragment_on_line->ofs + last_fragment_on_line->len); - } - - // Consume line - - line_ypos += adjusted_line_height; - - first_fragment_on_line = last_fragment_on_line->getNext(); - } - - ypos = getPrev() != nullptr ? getPrev()->ypos + getPrev()->height : 0; - setSize(old_line_width_max, line_width_max, line_ypos, propagateHeight); - - invalidate(); -} - -void TBBlock::setSize(int32_t oldW, int32_t newW, int32_t newH, bool propagateHeight) { - // Later: could optimize with Scroll here. - int32_t dh = newH - height; - height = newH; - if (dh != 0 && propagateHeight) { - TBBlock *block = getNext(); - while (block != nullptr) { - block->ypos = block->getPrev()->ypos + block->getPrev()->height; - block->invalidate(); - block = block->getNext(); - } - } - - // Update content_width and content_height - // content_width can only be calculated in constant time if we grow larger. - // If we shrink our width and where equal to content_width, we don't know - // how wide the widest block is and we set a flag to update it when needed. - - if (!styledit->packed.wrapping && !styledit->packed.multiline_on) { - styledit->content_width = newW; - } else if (newW > styledit->content_width) { - styledit->content_width = newW; - } else if (newW < oldW && oldW == styledit->content_width) { - styledit->packed.calculate_content_width_needed = 1; - } - - styledit->content_height = styledit->blocks.getLast()->ypos + styledit->blocks.getLast()->height; - - if ((styledit->listener != nullptr) && styledit->packed.lock_scrollbars_counter == 0 && propagateHeight) { - styledit->listener->updateScrollbars(); - } -} - -TBTextFragment *TBBlock::findFragment(int32_t ofs, bool preferFirst) const { - TBTextFragment *fragment = fragments.getFirst(); - while (fragment != nullptr) { - if (preferFirst && ofs <= fragment->ofs + fragment->len) { - return fragment; - } - if (!preferFirst && ofs < fragment->ofs + fragment->len) { - return fragment; - } - fragment = fragment->getNext(); - } - return fragments.getLast(); -} - -TBTextFragment *TBBlock::findFragment(int32_t x, int32_t y) const { - TBTextFragment *fragment = fragments.getFirst(); - while (fragment != nullptr) { - if (y < fragment->line_ypos + fragment->line_height) { - if (x < fragment->xpos + fragment->getWidth(this, styledit->font)) { - return fragment; - } - if ((fragment->getNext() != nullptr) && fragment->getNext()->line_ypos > fragment->line_ypos) { - return fragment; - } - } - fragment = fragment->getNext(); - } - return fragments.getLast(); -} - -void TBBlock::invalidate() const { - if (styledit->listener != nullptr) { - styledit->listener->invalidate(TBRect(0, -styledit->scroll_y + ypos, styledit->layout_width, height)); - } -} - -void TBBlock::buildSelectionRegion(int32_t translateX, int32_t translateY, TBTextProps *props, TBRegion &bgRegion, - TBRegion &fgRegion) { - if (!styledit->selection.isBlockSelected(this)) { - return; - } - - TBPaintProps paint_props; - paint_props.block = this; - paint_props.props = props; - paint_props.translate_x = translateX; - paint_props.translate_y = translateY + ypos; - - TBTextFragment *fragment = fragments.getFirst(); - while (fragment != nullptr) { - fragment->buildSelectionRegion(&paint_props, bgRegion, fgRegion); - fragment = fragment->getNext(); - } -} - -void TBBlock::paint(int32_t translateX, int32_t translateY, TBTextProps *props) { - TMPDEBUG(styledit->listener->DrawRect(TBRect(translate_x, translate_y + ypos, styledit->layout_width, height), - TBColor(255, 200, 0, 128))); - - TBPaintProps paint_props; - paint_props.block = this; - paint_props.props = props; - paint_props.translate_x = translateX; - paint_props.translate_y = translateY + ypos; - - if (styledit->syntax_highlighter != nullptr) { - styledit->syntax_highlighter->onPaintBlock(&paint_props); - } - - TBTextFragment *fragment = fragments.getFirst(); - while (fragment != nullptr) { - if (styledit->syntax_highlighter != nullptr) { - styledit->syntax_highlighter->onBeforePaintFragment(&paint_props, fragment); - } - - fragment->paint(&paint_props); - - if (styledit->syntax_highlighter != nullptr) { - styledit->syntax_highlighter->onAfterPaintFragment(&paint_props, fragment); - } - - fragment = fragment->getNext(); - } -} - -// == TBTextFragment ========================================================================= - -TBTextFragment::~TBTextFragment() { - delete content; -} - -void TBTextFragment::init(const TBBlock *block, uint16_t ofs, uint16_t len) { - this->ofs = ofs; - this->len = len; - m_packed.is_break = str(block)[0] == '\r' || str(block)[0] == '\n'; - m_packed.is_space = is_space(str(block)[0]); - m_packed.is_tab = str(block)[0] == '\t'; -} - -void TBTextFragment::updateContentPos(const TBBlock *block) { - if (content != nullptr) { - content->updatePos(block, xpos, ypos + block->ypos); - } -} - -void TBTextFragment::buildSelectionRegion(const TBPaintProps *props, TBRegion &bgRegion, TBRegion &fgRegion) { - const TBBlock *block = props->block; - if (!block->styledit->selection.isFragmentSelected(block, this)) { - return; - } - - const int x = props->translate_x + xpos; - const int y = props->translate_y + ypos; - TBFontFace *font = props->props->getFont(); - - if (content != nullptr) { - // Selected embedded content should add to the foreground region. - fgRegion.includeRect(TBRect(x, y, getWidth(block, font), getHeight(block, font))); - return; - } - - // Selected text should add to the backgroud region. - TBSelection *sel = &block->styledit->selection; - - int sofs1 = sel->start.block == block ? sel->start.ofs : 0; - int sofs2 = sel->stop.block == block ? sel->stop.ofs : block->str_len; - sofs1 = Max(sofs1, (int)ofs); - sofs2 = Min(sofs2, (int)(ofs + len)); - - int s1x = getStringWidth(block, font, block->str.c_str() + ofs, sofs1 - ofs); - int s2x = getStringWidth(block, font, block->str.c_str() + sofs1, sofs2 - sofs1); - - bgRegion.includeRect(TBRect(x + s1x, y, s2x, getHeight(block, font))); -} - -void TBTextFragment::paint(const TBPaintProps *props) { - TBStyleEditListener *listener = props->block->styledit->listener; - - const int x = props->translate_x + xpos; - const int y = props->translate_y + ypos; - const TBColor color = props->props->data->text_color; - TBFontFace *font = props->props->getFont(); - TBBlock *block = props->block; - - if (content != nullptr) { - content->paint(props, this); - return; - } - TMPDEBUG( - listener->DrawRect(TBRect(x, y, getWidth(block, font), getHeight(block, font)), TBColor(255, 255, 255, 128))); - - if (block->styledit->packed.password_on) { - const int len = SDL_strlen(special_char_password); - int cw = block->calculateStringWidth(font, special_char_password, len); - int num_char = utf8::count_characters(str(block), len); - for (int i = 0; i < num_char; i++) { - listener->drawString(x + i * cw, y, font, color, special_char_password, len); - } - } else if (block->styledit->packed.show_whitespace) { - if (isTab()) { - listener->drawString(x, y, font, color, special_char_tab, SDL_strlen(special_char_tab)); - } else if (isBreak()) { - listener->drawString(x, y, font, color, special_char_newln, SDL_strlen(special_char_newln)); - } else if (isSpace()) { - listener->drawString(x, y, font, color, special_char_space, SDL_strlen(special_char_space)); - } else { - listener->drawString(x, y, font, color, str(block), len); - } - } else if (!isTab() && !isBreak() && !isSpace()) { - listener->drawString(x, y, font, color, str(block), len); - } - - if (props->props->data->underline) { - int line_h = font->getHeight() / 16; - line_h = Max(line_h, 1); - listener->drawRectFill(TBRect(x, y + getBaseline(block, font) + 1, getWidth(block, font), line_h), color); - } -} - -void TBTextFragment::click(const TBBlock *block, int button, uint32_t modifierkeys) { - if (content != nullptr) { - content->click(block, this, button, modifierkeys); - } -} - -int32_t TBTextFragment::getWidth(const TBBlock *block, TBFontFace *font) { - if (m_packed.is_width_valid) { - return m_packed.width; - } - int32_t width = 0; - if (content != nullptr) { - width = content->getWidth(block, font, this); - } else if (isBreak()) { - width = 0; - } else if (isTab()) { - width = block->calculateTabWidth(font, xpos); - } else { - width = block->calculateStringWidth(font, block->str.c_str() + ofs, len); - } - if ((((uint32_t)width) & WIDTH_CACHE_MASK) == (uint32_t)width) { - m_packed.is_width_valid = 1; - m_packed.width = (uint32_t)width; - } - return width; -} - -int32_t TBTextFragment::getHeight(const TBBlock *block, TBFontFace *font) { - if (content != nullptr) { - return content->getHeight(block, font, this); - } - return block->calculateLineHeight(font); -} - -int32_t TBTextFragment::getBaseline(const TBBlock *block, TBFontFace *font) { - if (content != nullptr) { - return content->getBaseline(block, font, this); - } - return block->calculateBaseline(font); -} - -int32_t TBTextFragment::getCharX(const TBBlock *block, TBFontFace *font, int32_t ofs) { - core_assert(ofs >= 0 && ofs <= len); - - if (isEmbedded() || isTab()) { - return ofs == 0 ? 0 : getWidth(block, font); - } - if (isBreak()) { - return 0; - } - - return block->calculateStringWidth(font, block->str.c_str() + this->ofs, ofs); -} - -int32_t TBTextFragment::getCharOfs(const TBBlock *block, TBFontFace *font, int32_t x) { - if (isEmbedded() || isTab()) { - return x > getWidth(block, font) / 2 ? 1 : 0; - } - if (isBreak()) { - return 0; - } - - const char *str = block->str.c_str() + ofs; - int i = 0; - while (i < len) { - int pos = i; - utf8::move_inc(str, &i, len); - int last_char_len = i - pos; - // Always measure from the beginning of the fragment because of eventual kerning & text shaping etc. - int width_except_last_char = block->calculateStringWidth(font, str, i - last_char_len); - int width = block->calculateStringWidth(font, str, i); - if (x < width - (width - width_except_last_char) / 2) { - return pos; - } - } - return len; -} - -int32_t TBTextFragment::getStringWidth(const TBBlock *block, TBFontFace *font, const char *str, int len) { - if (len == 0) { - return 0; - } - if (len == this->len) { - return getWidth(block, font); - } - if (isTab()) { - return block->calculateTabWidth(font, xpos); - } - if (isBreak()) { - return 8; - } - return block->calculateStringWidth(font, str, len); -} - -bool TBTextFragment::getAllowBreakBefore(const TBBlock *block) const { - if (content != nullptr) { - return content->getAllowBreakBefore(block); - } - return (len != 0U) && !is_never_break_before(block->str.c_str(), ofs); -} - -bool TBTextFragment::getAllowBreakAfter(const TBBlock *block) const { - if (content != nullptr) { - return content->getAllowBreakAfter(block); - } - return (len != 0U) && !is_never_break_after(block->str.c_str(), ofs + len - 1); -} - -// ============================================================================ - -TBStyleEdit::TBStyleEdit() - : listener(nullptr), content_factory(&default_content_factory), syntax_highlighter(nullptr), layout_width(0), - layout_height(0), content_width(0), content_height(0), caret(nullptr), selection(nullptr), scroll_x(0), - scroll_y(0), select_state(0), mousedown_fragment(nullptr), font(nullptr), align(TB_TEXT_ALIGN_LEFT), - packed_init(0) { - caret.styledit = this; - selection.styledit = this; - TMPDEBUG(packed.show_whitespace = true); - - font_desc = g_font_manager->getDefaultFontDescription(); - font = g_font_manager->getFontFace(font_desc); - -#ifdef TB_TARGET_WINDOWS - packed.win_style_br = 1; -#endif - packed.selection_on = 1; - - clear(); -} - -TBStyleEdit::~TBStyleEdit() { - listener->caretBlinkStop(); - clear(false); -} - -void TBStyleEdit::setListener(TBStyleEditListener *listener) { - this->listener = listener; -} - -void TBStyleEdit::setContentFactory(TBTextFragmentContentFactory *contentFactory) { - if (contentFactory != nullptr) { - this->content_factory = contentFactory; - } else { - this->content_factory = &default_content_factory; - } -} - -void TBStyleEdit::setSyntaxHighlighter(TBSyntaxHighlighter *syntaxHighlighter) { - this->syntax_highlighter = syntaxHighlighter; - reformat(true); -} - -void TBStyleEdit::setFont(const TBFontDescription &fontDesc) { - if (this->font_desc == fontDesc) { - return; - } - this->font_desc = fontDesc; - font = g_font_manager->getFontFace(fontDesc); - reformat(true); -} - -void TBStyleEdit::clear(bool initNew) { - undoredo.clear(true, true); - selection.selectNothing(); - - if (initNew && (blocks.getFirst() != nullptr) && isEmpty()) { - return; - } - - for (TBBlock *block = blocks.getFirst(); block != nullptr; block = block->getNext()) { - block->invalidate(); - } - blocks.deleteAll(); - - if (initNew) { - blocks.addLast(new TBBlock(this)); - blocks.getFirst()->set("", 0); - } - - caret.place(blocks.getFirst(), 0); - caret.updateWantedX(); -} - -void TBStyleEdit::scrollIfNeeded(bool x, bool y) { - if (layout_width <= 0 || layout_height <= 0) { - return; // This is likely during construction before layout. - } - - int32_t newx = scroll_x; - int32_t newy = scroll_y; - if (x) { - if (caret.x - scroll_x < 0) { - newx = caret.x; - } - if (caret.x + caret.width - scroll_x > layout_width) { - newx = caret.x + caret.width - layout_width; - } - } - if (y) { - if (caret.y - scroll_y < 0) { - newy = caret.y; - } - if (caret.y + caret.height - scroll_y > layout_height) { - newy = caret.y + caret.height - layout_height; - } - } - setScrollPos(newx, newy); -} - -void TBStyleEdit::setScrollPos(int32_t x, int32_t y) { - x = Min(x, getContentWidth() - layout_width); - y = Min(y, getContentHeight() - layout_height); - x = Max(x, 0); - y = Max(y, 0); - if (!packed.multiline_on) { - y = 0; - } - int dx = scroll_x - x; - int dy = scroll_y - y; - if ((dx != 0) || (dy != 0)) { - scroll_x = x; - scroll_y = y; - listener->scroll(dx, dy); - } -} - -void TBStyleEdit::beginLockScrollbars() { - packed.lock_scrollbars_counter++; -} - -void TBStyleEdit::endLockScrollbars() { - packed.lock_scrollbars_counter--; - if ((listener != nullptr) && packed.lock_scrollbars_counter == 0) { - listener->updateScrollbars(); - } -} - -void TBStyleEdit::setLayoutSize(int32_t width, int32_t height, bool isVirtualReformat) { - if (width == layout_width && height == layout_height) { - return; - } - - bool doReformat = layout_width != width; - layout_width = width; - layout_height = height; - - if (doReformat && getSizeAffectsLayout()) { - reformat(false); - } - - caret.updatePos(); - caret.updateWantedX(); - - if (!isVirtualReformat) { - setScrollPos(scroll_x, scroll_y); ///< Trig a bounds check (scroll if outside) - } -} - -bool TBStyleEdit::getSizeAffectsLayout() const { - return packed.wrapping || align != TB_TEXT_ALIGN_LEFT; -} - -void TBStyleEdit::reformat(bool updateFragments) { - int ypos = 0; - beginLockScrollbars(); - TBBlock *block = blocks.getFirst(); - while (block != nullptr) { - // Update ypos directly instead of using "propagate_height" since propagating - // would iterate forward through all remaining blocks and we're going to visit - // them all anyway. - block->ypos = ypos; - block->layout(updateFragments, false); - ypos += block->height; - block = block->getNext(); - } - endLockScrollbars(); - listener->invalidate(TBRect(0, 0, layout_width, layout_height)); -} - -int32_t TBStyleEdit::getContentWidth() { - if (packed.calculate_content_width_needed) { - packed.calculate_content_width_needed = 0; - content_width = 0; - TBBlock *block = blocks.getFirst(); - while (block != nullptr) { - content_width = Max(content_width, block->line_width_max); - block = block->getNext(); - } - } - return content_width; -} - -int32_t TBStyleEdit::getContentHeight() const { - return content_height; -} - -void TBStyleEdit::paint(const TBRect &rect, const TBFontDescription &fontDesc, const TBColor &textColor) { - text_props.reset(fontDesc, textColor); - - // Find the first visible block - TBBlock *first_visible_block = blocks.getFirst(); - while (first_visible_block != nullptr) { - if (first_visible_block->ypos + first_visible_block->height - scroll_y >= 0) { - break; - } - first_visible_block = first_visible_block->getNext(); - } - - // Get the selection region for all visible blocks - TBRegion bg_region; - TBRegion fg_region; - if (selection.isSelected()) { - TBBlock *block = first_visible_block; - while (block != nullptr) { - if (block->ypos - scroll_y > rect.y + rect.h) { - break; - } - block->buildSelectionRegion(-scroll_x, -scroll_y, &text_props, bg_region, fg_region); - block = block->getNext(); - } - - // Paint bg selection - for (int i = 0; i < bg_region.getNumRects(); i++) { - listener->drawTextSelectionBg(bg_region.getRect(i)); - } - } - - // Paint the content - TBBlock *block = first_visible_block; - while (block != nullptr) { - if (block->ypos - scroll_y > rect.y + rect.h) { - break; - } - block->paint(-scroll_x, -scroll_y, &text_props); - block = block->getNext(); - } - - // Paint fg selection - for (int i = 0; i < fg_region.getNumRects(); i++) { - listener->drawTextSelectionBg(fg_region.getRect(i)); - } - - // Paint caret - caret.paint(-scroll_x, -scroll_y); -} - -void TBStyleEdit::insertBreak() { - if (!packed.multiline_on) { - return; - } - - const char *new_line_str = "\n"; - - // If we stand at the end and don't have any ending break, we're standing at the last line and - // should insert breaks twice. One to end the current line, and one for the new empty line. - if (caret.pos.ofs == caret.pos.block->str_len && !caret.pos.block->fragments.getLast()->isBreak()) { - new_line_str = "\n\n"; - } - - insertText(new_line_str); - - caret.avoidLineBreak(); - if (caret.pos.block->getNext() != nullptr) { - caret.place(caret.pos.block->getNext(), 0); - } -} - -void TBStyleEdit::insertText(const char *text, bool afterLast, bool clearUndoRedo) { - const int len = SDL_strlen(text); - selection.removeContent(); - - if (afterLast) { - caret.place(blocks.getLast(), blocks.getLast()->str_len, false); - } - - int32_t len_inserted = caret.pos.block->insertText(caret.pos.ofs, text, len, true); - if (clearUndoRedo) { - undoredo.clear(true, true); - } else { - undoredo.commit(this, caret.getGlobalOfs(), len_inserted, text, true); - } - - caret.place(caret.pos.block, caret.pos.ofs + len, false); - caret.updatePos(); - caret.updateWantedX(); -} - -TBBlock *TBStyleEdit::findBlock(int32_t y) const { - TBBlock *block = blocks.getFirst(); - while (block != nullptr) { - if (y < block->ypos + block->height) { - return block; - } - block = block->getNext(); - } - return blocks.getLast(); -} - -bool TBStyleEdit::keyDown(int key, SPECIAL_KEY specialKey, MODIFIER_KEYS modifierkeys) { - if (select_state != 0) { - return false; - } - - bool handled = true; - bool move_caret = specialKey == TB_KEY_LEFT || specialKey == TB_KEY_RIGHT || specialKey == TB_KEY_UP || - specialKey == TB_KEY_DOWN || specialKey == TB_KEY_HOME || specialKey == TB_KEY_END || - specialKey == TB_KEY_PAGE_UP || specialKey == TB_KEY_PAGE_DOWN; - - if (((modifierkeys & TB_SHIFT) == 0U) && move_caret) { - selection.selectNothing(); - } - - TBTextOfs old_caret_pos = caret.pos; - TBTextFragment *old_caret_elm = caret.getFragment(); - - if ((specialKey == TB_KEY_UP || specialKey == TB_KEY_DOWN) && ((modifierkeys & TB_CTRL) != 0U)) { - int32_t line_height = old_caret_pos.block->calculateLineHeight(font); - int32_t new_y = scroll_y + (specialKey == TB_KEY_UP ? -line_height : line_height); - setScrollPos(scroll_x, new_y); - } else if (specialKey == TB_KEY_LEFT) { - caret.move(false, (modifierkeys & TB_CTRL) != 0U); - } else if (specialKey == TB_KEY_RIGHT) { - caret.move(true, (modifierkeys & TB_CTRL) != 0U); - } else if (specialKey == TB_KEY_UP) { - handled = caret.place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos - 1)); - } else if (specialKey == TB_KEY_DOWN) { - handled = caret.place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos + - old_caret_elm->line_height + 1)); - } else if (specialKey == TB_KEY_PAGE_UP) { - caret.place(TBPoint(caret.wanted_x, caret.y - layout_height)); - } else if (specialKey == TB_KEY_PAGE_DOWN) { - caret.place(TBPoint(caret.wanted_x, caret.y + layout_height + old_caret_elm->line_height)); - } else if (specialKey == TB_KEY_HOME && ((modifierkeys & TB_CTRL) != 0U)) { - caret.place(TBPoint(0, 0)); - } else if (specialKey == TB_KEY_END && ((modifierkeys & TB_CTRL) != 0U)) { - caret.place(TBPoint(32000, blocks.getLast()->ypos + blocks.getLast()->height)); - } else if (specialKey == TB_KEY_HOME) { - caret.place(TBPoint(0, caret.y)); - } else if (specialKey == TB_KEY_END) { - caret.place(TBPoint(32000, caret.y)); - } else if (key == '8' && ((modifierkeys & TB_CTRL) != 0U)) { - packed.show_whitespace = !packed.show_whitespace; - listener->invalidate(TBRect(0, 0, layout_width, layout_height)); - } else if (!packed.read_only && (specialKey == TB_KEY_DELETE || specialKey == TB_KEY_BACKSPACE)) { - if (!selection.isSelected()) { - caret.move(specialKey == TB_KEY_DELETE, (modifierkeys & TB_CTRL) != 0U); - selection.selectToCaret(old_caret_pos.block, old_caret_pos.ofs); - } - selection.removeContent(); - } else if (!packed.read_only && ((modifierkeys & TB_SHIFT) == 0U) && - (specialKey == TB_KEY_TAB && packed.multiline_on)) { - insertText("\t", 1); - } else if (!packed.read_only && (specialKey == TB_KEY_ENTER && packed.multiline_on) && - ((modifierkeys & TB_CTRL) == 0U)) { - insertBreak(); - } else if (!packed.read_only && ((key != 0) && ((modifierkeys & TB_CTRL) == 0U)) && specialKey != TB_KEY_ENTER) { - char utf8[8]; - const int len = utf8::encode(key, utf8); - utf8[len] = '\0'; - insertText(utf8); - } else { - handled = false; - } - - if (((modifierkeys & TB_SHIFT) != 0U) && move_caret) { - selection.selectToCaret(old_caret_pos.block, old_caret_pos.ofs); - } - - if (!(specialKey == TB_KEY_UP || specialKey == TB_KEY_DOWN || specialKey == TB_KEY_PAGE_UP || - specialKey == TB_KEY_PAGE_DOWN)) { - caret.updateWantedX(); - } - - caret.resetBlink(); - - // Hooks - if (!move_caret && handled) { - invokeOnChange(); - } - if (specialKey == TB_KEY_ENTER && ((modifierkeys & TB_CTRL) == 0U)) { - if (listener->onEnter()) { - handled = true; - } - } - if (handled) { - scrollIfNeeded(); - } - - return handled; -} - -void TBStyleEdit::cut() { - if (packed.password_on) { - return; - } - copy(); - keyDown(0, TB_KEY_DELETE, TB_MODIFIER_NONE); -} - -void TBStyleEdit::copy() { - if (packed.password_on) { - return; - } - selection.copyToClipboard(); -} - -void TBStyleEdit::paste() { - core::String text; - if (TBClipboard::hasText() && TBClipboard::getText(text)) { - insertText(text.c_str(), text.size()); - scrollIfNeeded(true, true); - invokeOnChange(); - } -} - -void TBStyleEdit::del() { - if (selection.isSelected()) { - selection.removeContent(); - invokeOnChange(); - } -} - -void TBStyleEdit::undo() { - if (canUndo()) { - undoredo.undo(this); - invokeOnChange(); - } -} - -void TBStyleEdit::redo() { - if (canRedo()) { - undoredo.redo(this); - invokeOnChange(); - } -} - -bool TBStyleEdit::mouseDown(const TBPoint &point, int button, int clicks, MODIFIER_KEYS modifierkeys, bool touch) { - if (button != 1) { - return false; - } - - if (touch) { - mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y); - } else if (packed.selection_on) { - // if (modifierkeys & P_SHIFT) // Select to new caretpos - //{ - //} - // else // Start selection - { - mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y); - selection.selectNothing(); - - // clicks is 1 to infinite, and here we support only doubleclick, so make it either single or double. - select_state = ((clicks - 1) % 2) + 1; - - mouseMove(point); - - if (caret.pos.block != nullptr) { - mousedown_fragment = - caret.pos.block->findFragment(mousedown_point.x, mousedown_point.y - caret.pos.block->ypos); - } - } - caret.resetBlink(); - } - return true; -} - -bool TBStyleEdit::mouseUp(const TBPoint &point, int button, MODIFIER_KEYS modifierkeys, bool touch) { - if (button != 1) { - return false; - } - - if (touch && !TBWidget::cancel_click) { - selection.selectNothing(); - caret.place(mousedown_point); - caret.updateWantedX(); - caret.resetBlink(); - } - - select_state = 0; - if ((caret.pos.block != nullptr) && !TBWidget::cancel_click) { - TBTextFragment *fragment = - caret.pos.block->findFragment(point.x + scroll_x, point.y + scroll_y - caret.pos.block->ypos); - if ((fragment != nullptr) && fragment == mousedown_fragment) { - fragment->click(caret.pos.block, button, modifierkeys); - } - } - return true; -} - -bool TBStyleEdit::mouseMove(const TBPoint &point) { - if (select_state != 0) { - TBPoint p(point.x + scroll_x, point.y + scroll_y); - selection.select(mousedown_point, p); - - if (select_state == 2) { - bool has_initial_selection = selection.isSelected(); - - if (has_initial_selection) { - caret.place(selection.start.block, selection.start.ofs); - } - caret.move(false, true); - selection.start.set(caret.pos); - - if (has_initial_selection) { - caret.place(selection.stop.block, selection.stop.ofs); - } - caret.move(true, true); - selection.stop.set(caret.pos); - - selection.correctOrder(); - caret.updateWantedX(); - } - return true; - } - return false; -} - -void TBStyleEdit::focus(bool focus) { - if (focus) { - listener->caretBlinkStart(); - } else { - listener->caretBlinkStop(); - } - - caret.on = focus; - caret.invalidate(); - selection.invalidate(); -} - -bool TBStyleEdit::setText(const char *text, TB_CARET_POS pos) { - return setText(text, SDL_strlen(text), pos); -} - -bool TBStyleEdit::setText(const char *text, int textLen, TB_CARET_POS pos) { - if ((text == nullptr) || (*text == 0)) { - clear(true); - caret.updateWantedX(); - scrollIfNeeded(true, true); - return true; - } - - clear(true); - blocks.getFirst()->insertText(0, text, textLen, true); - - caret.place(blocks.getFirst(), 0); - caret.updateWantedX(); - scrollIfNeeded(true, false); - - if (pos == TB_CARET_POS_END) { - caret.place(blocks.getLast(), blocks.getLast()->str_len); - } - - invokeOnChange(); - return true; -} - -bool TBStyleEdit::getText(core::String &text) { - TBSelection tmp_selection(this); - tmp_selection.selectAll(); - return tmp_selection.getText(text); -} - -void TBStyleEdit::invokeOnChange() { - listener->onChange(); - if (syntax_highlighter != nullptr) { - syntax_highlighter->onChange(this); - } -} - -bool TBStyleEdit::isEmpty() const { - return blocks.getFirst() == blocks.getLast() && blocks.getFirst()->str.empty(); -} - -void TBStyleEdit::setAlign(TB_TEXT_ALIGN align) { - this->align = align; - // Call SetAlign on all blocks currently selected, or the block of the current caret position. - TBBlock *start = selection.isSelected() ? selection.start.block : caret.pos.block; - TBBlock *stop = selection.isSelected() ? selection.stop.block : caret.pos.block; - while ((start != nullptr) && start != stop->getNext()) { - start->setAlign(align); - start = start->getNext(); - } -} - -void TBStyleEdit::setMultiline(bool multiline) { - packed.multiline_on = multiline; -} - -void TBStyleEdit::setStyling(bool styling) { - packed.styling_on = styling; -} - -void TBStyleEdit::setReadOnly(bool readonly) { - packed.read_only = readonly; -} - -void TBStyleEdit::setSelection(bool selection) { - packed.selection_on = selection; -} - -void TBStyleEdit::setPassword(bool password) { - if (packed.password_on == static_cast(password)) { - return; - } - packed.password_on = password; - reformat(true); -} - -void TBStyleEdit::setWrapping(bool wrapping) { - if (packed.wrapping == static_cast(wrapping)) { - return; - } - packed.wrapping = wrapping; - reformat(false); -} - -// == TBUndoRedoStack ================================================== - -TBUndoRedoStack::~TBUndoRedoStack() { - clear(true, true); -} - -void TBUndoRedoStack::undo(TBStyleEdit *styledit) { - if (undos.getNumItems() == 0) { - return; - } - TBUndoEvent *e = undos.remove(undos.getNumItems() - 1); - redos.add(e); - apply(styledit, e, true); -} - -void TBUndoRedoStack::redo(TBStyleEdit *styledit) { - if (redos.getNumItems() == 0) { - return; - } - TBUndoEvent *e = redos.remove(redos.getNumItems() - 1); - undos.add(e); - apply(styledit, e, false); -} - -void TBUndoRedoStack::apply(TBStyleEdit *styledit, TBUndoEvent *e, bool reverse) { - applying = true; - if (e->insert == reverse) { - styledit->selection.selectNothing(); - styledit->caret.setGlobalOfs(e->gofs, false); - core_assert(TBTextOfs(styledit->caret.pos).getGlobalOfs(styledit) == e->gofs); - - TBTextOfs start = styledit->caret.pos; - styledit->caret.setGlobalOfs(e->gofs + e->text.size(), false); - core_assert(TBTextOfs(styledit->caret.pos).getGlobalOfs(styledit) == e->gofs + (int32_t)e->text.size()); - - styledit->selection.select(start, styledit->caret.pos); - styledit->selection.removeContent(); - } else { - styledit->selection.selectNothing(); - styledit->caret.setGlobalOfs(e->gofs, true, true); - styledit->insertText(e->text.c_str()); - int text_len = e->text.size(); - if (text_len > 1) { - styledit->selection.select(e->gofs, e->gofs + text_len); - } - } - styledit->scrollIfNeeded(true, true); - applying = false; -} - -void TBUndoRedoStack::clear(bool clearUndo, bool clearRedo) { - core_assert(!applying); - if (clearUndo) { - undos.deleteAll(); - } - if (clearRedo) { - redos.deleteAll(); - } -} - -TBUndoEvent *TBUndoRedoStack::commit(TBStyleEdit *styledit, int32_t gofs, int32_t len, const char *text, bool insert) { - if (applying || styledit->packed.read_only) { - return nullptr; - } - clear(false, true); - - // If we're inserting a single character, check if we want to append it to the previous event. - if (insert && (undos.getNumItems() != 0)) { - int num_char = utf8::count_characters(text, len); - TBUndoEvent *e = undos[undos.getNumItems() - 1]; - if (num_char == 1 && e->insert && e->gofs + (int32_t)e->text.size() == gofs) { - // Appending a space to other space(s) should append - if ((text[0] == ' ' && (strpbrk(e->text.c_str(), "\r\n") == nullptr)) || - // But non spaces should not - (strpbrk(e->text.c_str(), " \r\n") == nullptr)) { - e->text.append(text, len); - return e; - } - } - } - - // Create a new event - if (TBUndoEvent *e = new TBUndoEvent()) { - e->gofs = gofs; - e->text = core::String(text, len); - e->insert = insert; - undos.add(e); - return e; - } - - // OOM - clear(true, true); - return nullptr; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_style_edit.h b/src/modules/ui/turbobadger/tb/tb_style_edit.h deleted file mode 100644 index b7f59275d..000000000 --- a/src/modules/ui/turbobadger/tb/tb_style_edit.h +++ /dev/null @@ -1,516 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_linklist.h" -#include "tb_list.h" -#include "tb_widgets_common.h" - -namespace tb { - -class TBStyleEdit; -class TBBlock; -class TBTextFragment; -class TBTextFragmentContent; -class TBTextFragmentContentFactory; - -/** Listener for TBStyleEdit. Implement in the enviorment the TBStyleEdit should render its content. */ -class TBStyleEditListener { -public: - virtual ~TBStyleEditListener() { - } - - virtual void onChange(){}; - virtual bool onEnter() { - return false; - }; - virtual void invalidate(const TBRect &rect) = 0; - virtual void drawString(int32_t x, int32_t y, TBFontFace *font, const TBColor &color, const char *str, - int32_t len) = 0; - virtual void drawRect(const TBRect &rect, const TBColor &color) = 0; - virtual void drawRectFill(const TBRect &rect, const TBColor &color) = 0; - virtual void drawTextSelectionBg(const TBRect &rect) = 0; - virtual void drawContentSelectionFg(const TBRect &rect) = 0; - virtual void drawCaret(const TBRect &rect) = 0; - virtual void scroll(int32_t dx, int32_t dy) = 0; - virtual void updateScrollbars() = 0; - virtual void caretBlinkStart() = 0; - virtual void caretBlinkStop() = 0; -}; - -/** Creates TBTextFragmentContent if the sequence of text matches known content. */ - -class TBTextFragmentContentFactory { -public: - virtual ~TBTextFragmentContentFactory() { - } - /** Should return then length of the text that represents content - that can be created by this factory, or 0 there's no match with any content. - - F.ex if we can create content for @c <u> it should return 3 if that is the beginning of - text. That length will be consumed from the text output for the created content. */ - virtual int getContent(const char *text); - - /** Create content for a string previosly consumed by calling GetContent. */ - virtual TBTextFragmentContent *createFragmentContent(const char *text, int text_len); -}; - -class TBTextOfs { -public: - TBTextOfs() : block(nullptr), ofs(0) { - } - TBTextOfs(TBBlock *block, int32_t ofs) : block(block), ofs(ofs) { - } - - void set(TBBlock *newBlock, int32_t newOfs) { - block = newBlock; - ofs = newOfs; - } - void set(const TBTextOfs &pos) { - block = pos.block; - ofs = pos.ofs; - } - - int32_t getGlobalOfs(TBStyleEdit *se) const; - bool setGlobalOfs(TBStyleEdit *se, int32_t gofs); - -public: - TBBlock *block; - int32_t ofs; -}; - -/** Handles the selected text in a TBStyleEdit. */ - -class TBSelection { -public: - TBSelection(TBStyleEdit *styledit); - void invalidate() const; - void select(const TBTextOfs &newStart, const TBTextOfs &newStop); - void select(const TBPoint &from, const TBPoint &to); - void select(int globOfsFrom, int globOfsTo); - void selectToCaret(TBBlock *oldCaretBlock, int32_t oldCaretOfs); - void selectAll(); - void selectNothing(); - void correctOrder(); - void copyToClipboard(); - bool isBlockSelected(const TBBlock *block) const; - bool isFragmentSelected(const TBBlock *block, TBTextFragment *elm) const; - bool isSelected() const; - void removeContent(); - bool getText(core::String &text) const; - -public: - TBStyleEdit *styledit; - TBTextOfs start, stop; -}; - -enum TB_CARET_POS { TB_CARET_POS_BEGINNING, TB_CARET_POS_END }; - -/** The caret in a TBStyleEdit. */ -class TBCaret { -public: - TBCaret(TBStyleEdit *styledit); - void invalidate(); - void updatePos(); - bool move(bool forward, bool word); - bool place(const TBPoint &point); - bool place(TBBlock *block, int ofs, bool allowSnap = true, bool snapForward = false); - void place(TB_CARET_POS pos); - void avoidLineBreak(); - void paint(int32_t translateX, int32_t translateY); - void resetBlink(); - void updateWantedX(); - - int32_t getGlobalOfs() const { - return pos.getGlobalOfs(styledit); - } - void setGlobalOfs(int32_t gofs, bool allowSnap = true, bool snapForward = false); - - TBTextFragment *getFragment(); - -private: - void switchBlock(bool second); - -public: - TBStyleEdit *styledit; - int32_t x, y; ///< Relative to the styledit - int32_t width; - int32_t height; - int32_t wanted_x; - bool on; - bool prefer_first; - TBTextOfs pos; -}; - -/** TBTextProps is a stack of properties used during layout & paint of TBStyleEdit. */ - -class TBTextProps { -public: - class Data { - public: - TBFontDescription font_desc; - TBColor text_color; - bool underline = false; - }; - TBTextProps() { - } - ~TBTextProps() { - while (list.getNumItems() > 0) { - delete list.get(0); - list.remove(0); - } - } - - void reset(const TBFontDescription &fontDesc, const TBColor &textColor); - Data *push(); - void pop(); - - /** Get the font face from the current font description. */ - TBFontFace *getFont() const; - -public: - int next_index = 0; - TBListOf list; - Data base; - Data *data = nullptr; -}; - -/** TBPaintProps holds paint related data during paint of TBStyleEdit. */ - -class TBPaintProps { -public: - TBBlock *block; - TBTextProps *props; - int32_t translate_x; - int32_t translate_y; -}; - -/** A block of text (a line, that might be wrapped) */ - -class TBBlock : public TBLinkOf { -public: - TBBlock(TBStyleEdit *styledit); - ~TBBlock(); - - void clear(); - void set(const char *newstr, int32_t len); - void setAlign(TB_TEXT_ALIGN align); - - int32_t insertText(int32_t ofs, const char *text, int32_t len, bool allow_line_recurse); - void removeContent(int32_t ofs, int32_t len); - - /** Check if this block contains extra line breaks and split into new blocks if it does. */ - void split(); - - /** Check if we've lost the ending break on this block and if so merge it with the next block. */ - void merge(); - - /** Layout the block. To be called when the text has changed or the layout width has changed. - @param updateFragments Should be true if the text has been changed (will recreate elements). - @param propagateHeight If true, all following blocks will be moved if the height changed. */ - void layout(bool updateFragments, bool propagateHeight); - - /** Update the size of this block. If propagate_height is true, all following blocks will be - moved if the height changed. */ - void setSize(int32_t oldW, int32_t newW, int32_t newH, bool propagateHeight); - - TBTextFragment *findFragment(int32_t ofs, bool prefer_first = false) const; - TBTextFragment *findFragment(int32_t x, int32_t y) const; - - int32_t calculateStringWidth(TBFontFace *font, const char *str, int len) const; - int32_t calculateTabWidth(TBFontFace *font, int32_t xpos) const; - int32_t calculateLineHeight(TBFontFace *font) const; - int32_t calculateBaseline(TBFontFace *font) const; - - void invalidate() const; - void buildSelectionRegion(int32_t translateX, int32_t translateY, TBTextProps *props, TBRegion &bgRegion, - TBRegion &fgRegion); - void paint(int32_t translateX, int32_t translateY, TBTextProps *props); - -public: - TBStyleEdit *styledit; - TBLinkListOf fragments; - - int32_t ypos; - int16_t height; - int8_t align; - int line_width_max; - - core::String str; - int32_t str_len; - uint32_t syntax_data; ///< Free to use in any way from TBSyntaxHighlighter subclasses - -private: - int getStartIndentation(TBFontFace *font, int first_line_len) const; -}; - -/** Event in the TBUndoRedoStack. Each insert or remove change is stored as a TBUndoEvent, but they may also be merged - * when appropriate. */ - -class TBUndoEvent { -public: - int32_t gofs; - core::String text; - bool insert; -}; - -/** Keeps track of all TBUndoEvents used for undo and redo functionality. */ - -class TBUndoRedoStack { -public: - TBUndoRedoStack() : applying(false) { - } - ~TBUndoRedoStack(); - - void undo(TBStyleEdit *styledit); - void redo(TBStyleEdit *styledit); - void clear(bool clearUndo, bool clearRedo); - - TBUndoEvent *commit(TBStyleEdit *styledit, int32_t gofs, int32_t len, const char *text, bool insert); - -public: - TBListOf undos; - TBListOf redos; - bool applying; - -private: - void apply(TBStyleEdit *styledit, TBUndoEvent *e, bool reverse); -}; - -/** TBSyntaxHighlighter can be subclassed to give syntax highlighting on TBStyleEdit - without altering the text (without inserting style markup) */ -class TBSyntaxHighlighter { -public: - virtual ~TBSyntaxHighlighter() { - } - - /** Called when all fragments has been updated in the given block and syntax info should - be updated. syntax_data can be stored in TBBlock and TBTextFragment */ - virtual void onFragmentsUpdated(TBBlock *block) { - } - - /** Called after any change in TBStyleEdit when all blocks that changed have been updated. */ - virtual void onChange(TBStyleEdit *styledit) { - } - - /** Called before painting each block */ - virtual void onPaintBlock(const TBPaintProps *props) { - } - - /** Called before painting each fragment */ - virtual void onBeforePaintFragment(const TBPaintProps *props, TBTextFragment *fragment) { - } - - /** Called after painting each fragment */ - virtual void onAfterPaintFragment(const TBPaintProps *props, TBTextFragment *fragment) { - } -}; - -/** The textfragment baseclass for TBStyleEdit. - - TODO: This object is allocated on vast amounts and need - to shrink in size. Remove all cached positioning - and implement a fragment traverser (for TBBlock). - Also allocate fragments in chunks. */ - -class TBTextFragment : public TBLinkOf { -public: - TBTextFragment(TBTextFragmentContent *content = nullptr) - : xpos(0), ypos(0), ofs(0), len(0), line_ypos(0), line_height(0), m_packed_init(0), content(content) { - } - ~TBTextFragment(); - - void init(const TBBlock *block, uint16_t ofs, uint16_t len); - - void updateContentPos(const TBBlock *block); - - void buildSelectionRegion(const TBPaintProps *props, TBRegion &bgRegion, TBRegion &fgRegion); - void paint(const TBPaintProps *props); - void click(const TBBlock *block, int button, uint32_t modifierkeys); - - bool isText() const { - return !isEmbedded(); - } - bool isEmbedded() const { - return content != nullptr; - } - bool isBreak() const { - return m_packed.is_break != 0; - } - bool isSpace() const { - return m_packed.is_space != 0; - } - bool isTab() const { - return m_packed.is_tab != 0; - } - - int32_t getCharX(const TBBlock *block, TBFontFace *font, int32_t ofs); - int32_t getCharOfs(const TBBlock *block, TBFontFace *font, int32_t x); - - /** Get the stringwidth. Handles passwordmode, tab, linebreaks etc automatically. */ - int32_t getStringWidth(const TBBlock *block, TBFontFace *font, const char *str, int len); - - bool getAllowBreakBefore(const TBBlock *block) const; - bool getAllowBreakAfter(const TBBlock *block) const; - - const char *str(const TBBlock *block) const { - return block->str.c_str() + ofs; - } - - int32_t getWidth(const TBBlock *block, TBFontFace *font); - int32_t getHeight(const TBBlock *block, TBFontFace *font); - int32_t getBaseline(const TBBlock *block, TBFontFace *font); - -public: - int16_t xpos, ypos; - uint16_t ofs, len; - uint16_t line_ypos, line_height; - union { - struct { - uint32_t is_break : 1; ///< Fragment is hard line break - uint32_t is_space : 1; ///< Fragment is white space - uint32_t is_tab : 1; ///< Fragment is tab - uint32_t syntax_data : 10; ///< Free to use in any way from TBSyntaxHighlighter subclasses - uint32_t width : 11; ///< width cache. Bit number need to match shift in WIDTH_CACHE_MASK. - uint32_t is_width_valid : 1; ///< width cache is set - } m_packed; - uint32_t m_packed_init; - }; - static const uint32_t WIDTH_CACHE_MASK = (1 << 11) - 1; - TBTextFragmentContent *content; -}; - -/** Edit and formats TBTextFragment's. It handles the text in a TBStyleEditView. */ - -class TBStyleEdit { -public: - TBStyleEdit(); - virtual ~TBStyleEdit(); - - void setListener(TBStyleEditListener *listener); - void setContentFactory(TBTextFragmentContentFactory *content_factory); - void setSyntaxHighlighter(TBSyntaxHighlighter *syntax_highlighter); - - void setFont(const TBFontDescription &font_desc); - - void paint(const TBRect &rect, const TBFontDescription &fontDesc, const TBColor &textColor); - bool keyDown(int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys); - bool mouseDown(const TBPoint &point, int button, int clicks, MODIFIER_KEYS modifierkeys, bool touch); - bool mouseUp(const TBPoint &point, int button, MODIFIER_KEYS modifierkeys, bool touch); - bool mouseMove(const TBPoint &point); - void focus(bool focus); - - void clear(bool init_new = true); - bool setText(const char *text, TB_CARET_POS pos = TB_CARET_POS_BEGINNING); - bool setText(const char *text, int text_len, TB_CARET_POS pos = TB_CARET_POS_BEGINNING); - bool getText(core::String &text); - bool isEmpty() const; - - /** Set the default text alignment and all currently selected blocks, - or the block of the current caret position if nothing is selected. */ - void setAlign(TB_TEXT_ALIGN align); - void setMultiline(bool multiline = true); - void setStyling(bool styling = true); - void setReadOnly(bool readonly = true); - void setSelection(bool selection = true); - void setPassword(bool password = true); - void setWrapping(bool wrapping = true); - - void cut(); - void copy(); - void paste(); - void del(); - - void undo(); - void redo(); - bool canUndo() const { - return undoredo.undos.getNumItems() != 0; - } - bool canRedo() const { - return undoredo.redos.getNumItems() != 0; - } - - void insertText(const char *text, bool afterLast = false, - bool clearUndoRedo = false); - void appendText(const char *text, bool clearUndoRedo = false) { - insertText(text, true, clearUndoRedo); - } - void insertBreak(); - - TBBlock *findBlock(int32_t y) const; - - void scrollIfNeeded(bool x = true, bool y = true); - void setScrollPos(int32_t x, int32_t y); - void setLayoutSize(int32_t width, int32_t height, bool is_virtual_reformat); - void reformat(bool update_fragments); - - int32_t getContentWidth(); - int32_t getContentHeight() const; - - int32_t getOverflowX() const { - return Max(content_width - layout_width, 0); - } - int32_t getOverflowY() const { - return Max(content_height - layout_height, 0); - } - -public: - TBStyleEditListener *listener; - TBTextFragmentContentFactory default_content_factory; - TBTextFragmentContentFactory *content_factory; - TBSyntaxHighlighter *syntax_highlighter; - int32_t layout_width; - int32_t layout_height; - int32_t content_width; - int32_t content_height; - - TBLinkListOf blocks; - - TBCaret caret; - TBSelection selection; - TBUndoRedoStack undoredo; - TBTextProps text_props; - - int32_t scroll_x; - int32_t scroll_y; - - int8_t select_state; - TBPoint mousedown_point; - TBTextFragment *mousedown_fragment; - - /** DEPRECATED! This will be removed when using different fonts is properly supported! */ - TBFontFace *font; - TBFontDescription font_desc; - - TB_TEXT_ALIGN align; - union { - struct { - uint32_t multiline_on : 1; - uint32_t styling_on : 1; - uint32_t read_only : 1; - uint32_t selection_on : 1; - uint32_t show_whitespace : 1; - uint32_t password_on : 1; - uint32_t wrapping : 1; - uint32_t calculate_content_width_needed : 1; ///< If content_width needs to be updated next GetContentWidth- - uint32_t lock_scrollbars_counter : 5; ///< Incremental counter for if UpdateScrollbar should be prohibited. - } packed; - uint32_t packed_init; - }; - - /** Call BeginLockScrollbars & EndLockScrollbars around a scope which does lots of changes, - to prevent UpdateScrollbar from happening for each block (May cause recalculation of - content_width by iterating through all blocks) */ - void beginLockScrollbars(); - void endLockScrollbars(); - - /** Return true if changing layout_width and layout_height requires relayouting. */ - bool getSizeAffectsLayout() const; - - void invokeOnChange(); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_style_edit_content.cpp b/src/modules/ui/turbobadger/tb/tb_style_edit_content.cpp deleted file mode 100644 index 3aebe504b..000000000 --- a/src/modules/ui/turbobadger/tb/tb_style_edit_content.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file - */ - -#include "tb_style_edit_content.h" -#include "core/Assert.h" -#include "tb_style_edit.h" - -namespace tb { - -int TBTextFragmentContentFactory::getContent(const char *text) { - if (text[0] == '<') { - if (text[1] == '<') { - return 0; - } - int i = 0; - while (text[i] != '>' && text[i] > 31) { - i++; - } - if (text[i] == '>') { - i++; - return i; - } - } - return 0; -} - -TBTextFragmentContent *TBTextFragmentContentFactory::createFragmentContent(const char *text, int textLen) { - if (SDL_strncmp(text, "
", textLen) == 0) { - return new TBTextFragmentContentHR(100, 2); - } - if (SDL_strncmp(text, "", textLen) == 0) { - return new TBTextFragmentContentUnderline(); - } - if (SDL_strncmp(text, "translate_x + fragment->xpos; - int y = props->translate_y + fragment->ypos; - - int w = props->block->styledit->layout_width * width_in_percent / 100; - x += (props->block->styledit->layout_width - w) / 2; - - TBStyleEditListener *listener = props->block->styledit->listener; - listener->drawRectFill(TBRect(x, y, w, height), props->props->data->text_color); -} - -int32_t TBTextFragmentContentHR::getWidth(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return Max(block->styledit->layout_width, 0); -} - -int32_t TBTextFragmentContentHR::getHeight(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return height; -} - -void TBTextFragmentContentUnderline::paint(const TBPaintProps *props, TBTextFragment *fragment) { - if (TBTextProps::Data *data = props->props->push()) - data->underline = true; -} - -void TBTextFragmentContentTextColor::paint(const TBPaintProps *props, TBTextFragment *fragment) { - if (TBTextProps::Data *data = props->props->push()) - data->text_color = color; -} - -void TBTextFragmentContentStylePop::paint(const TBPaintProps *props, TBTextFragment *fragment) { - props->props->pop(); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_style_edit_content.h b/src/modules/ui/turbobadger/tb/tb_style_edit_content.h deleted file mode 100644 index aea39329e..000000000 --- a/src/modules/ui/turbobadger/tb/tb_style_edit_content.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_color.h" - -namespace tb { - - -class TBTextFragment; -class TBBlock; -class TBPaintProps; -class TBFontFace; - -/** Content for a non-text TBTextFragment. */ -class TBTextFragmentContent { -public: - virtual ~TBTextFragmentContent() { - } - - /** Update the position of the content, relative to the first line of text (no scrolling applied). */ - virtual void updatePos(const TBBlock *block, int x, int y) { - } - - virtual void paint(const TBPaintProps *props, TBTextFragment *fragment) { - } - virtual void click(const TBBlock *block, TBTextFragment *fragment, int button, uint32_t modifierkeys) { - } - virtual int32_t getWidth(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return 0; - } - virtual int32_t getHeight(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return 0; - } - virtual int32_t getBaseline(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment) { - return getHeight(block, font, fragment); - } - virtual bool getAllowBreakBefore(const TBBlock *block) { - return true; - } - virtual bool getAllowBreakAfter(const TBBlock *block) { - return true; - } - - /** Get type of fragment content. All standard fragments return 0. */ - virtual uint32_t getType() { - return 0; - } -}; - -/** A horizontal line for TBStyleEdit. */ -class TBTextFragmentContentHR : public TBTextFragmentContent { -public: - TBTextFragmentContentHR(int32_t widthInPercent, int32_t height); - - virtual void paint(const TBPaintProps *props, TBTextFragment *fragment); - virtual int32_t getWidth(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment); - virtual int32_t getHeight(const TBBlock *block, TBFontFace *font, TBTextFragment *fragment); - -private: - int32_t width_in_percent, height; -}; - -/** Fragment content that enables underline in a TBStyleEdit */ -class TBTextFragmentContentUnderline : public TBTextFragmentContent { -public: - TBTextFragmentContentUnderline() { - } - virtual void paint(const TBPaintProps *props, TBTextFragment *fragment); - virtual bool getAllowBreakBefore(const TBBlock *block) { - return true; - } - virtual bool getAllowBreakAfter(const TBBlock *block) { - return false; - } -}; - -/** Fragment content that changes color in a TBStyleEdit */ -class TBTextFragmentContentTextColor : public TBTextFragmentContent { -public: - TBColor color; - TBTextFragmentContentTextColor(const TBColor &color) : color(color) { - } - virtual void paint(const TBPaintProps *props, TBTextFragment *fragment); - virtual bool getAllowBreakBefore(const TBBlock *block) { - return true; - } - virtual bool getAllowBreakAfter(const TBBlock *block) { - return false; - } -}; - -/** Fragment content that ends a change of style in a TBStyleEdit */ -class TBTextFragmentContentStylePop : public TBTextFragmentContent { -public: - virtual void paint(const TBPaintProps *props, TBTextFragment *fragment); - virtual bool getAllowBreakBefore(const TBBlock *block) { - return false; - } - virtual bool getAllowBreakAfter(const TBBlock *block) { - return true; - } -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_system.cpp b/src/modules/ui/turbobadger/tb/tb_system.cpp deleted file mode 100644 index 79a129a13..000000000 --- a/src/modules/ui/turbobadger/tb/tb_system.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @file - */ - -#include "tb_system.h" - -#include "app/App.h" -#include "io/Filesystem.h" -#include "tb_msg.h" -#include "tb_types.h" -#include "video/WindowedApp.h" -#include -#include - -namespace tb { - -double TBSystem::getTimeMS() { - Uint64 freq = SDL_GetPerformanceFrequency(); - Uint64 now = SDL_GetPerformanceCounter(); - return 1000. * ((double)now / (double)freq); -} - -/** Reschedule the platform timer, or cancel it if fire_time is TB_NOT_SOON. - If fire_time is 0, it should be fired ASAP. - If force is true, it will ask the platform to schedule it again, even if - the fire_time is the same as last time. */ -void TBSystem::rescheduleTimer(double fireTime) { -} - -int TBSystem::getLongClickDelayMS() { - return 500; -} - -int TBSystem::getPanThreshold() { - return 5; -} - -int TBSystem::getPixelsPerLine() { - return 40; -} - -int TBSystem::getDPI() { -#ifdef __APPLE__ - return 72; -#else - return 96; -#endif -} - -void TBClipboard::empty() { - setText(""); -} - -bool TBClipboard::hasText() { - return SDL_HasClipboardText() != SDL_FALSE; -} - -bool TBClipboard::setText(const char *text) { - return SDL_SetClipboardText(text) == 0; -} - -bool TBClipboard::getText(core::String &text) { - if (const char *str = SDL_GetClipboardText()) { - text = str; - return true; - } - return false; -} - -class File : public TBFile { -public: - File(const io::FilePtr &file) : _file(file) { - } - - virtual long size() { - return _file->length(); - } - virtual size_t read(void *buf, size_t elemSize, size_t count) { - return _file->read(buf, elemSize, count); - } - -private: - io::FilePtr _file; -}; - -// static -TBFile *TBFile::open(const char *filename, TBFileMode mode) { - io::FilePtr f; - switch (mode) { - case MODE_READ: - f = io::filesystem()->open(filename, io::FileMode::Read); - break; - default: - break; - } - if (!f || !f->exists()) { - return nullptr; - } - return new File(f); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_system.h b/src/modules/ui/turbobadger/tb/tb_system.h deleted file mode 100644 index 5cb0c583d..000000000 --- a/src/modules/ui/turbobadger/tb/tb_system.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/Assert.h" -#include "core/Log.h" -#include "tb_core.h" -#include "tb_str.h" - -namespace tb { - -// == Platform interface =================================================== - -/** TBSystem is porting interface for the underlaying OS. */ -class TBSystem { -public: - /** Get the system time in milliseconds since some undefined epoch. */ - static double getTimeMS(); - - /** Called when the need to call TBMessageHandler::ProcessMessages has changed due to changes in the - message queue. fire_time is the new time is needs to be called. - It may be 0 which means that ProcessMessages should be called asap (but NOT from this call!) - It may also be TB_NOT_SOON which means that ProcessMessages doesn't need to be called. */ - static void rescheduleTimer(double fireTime); - - /** Get how many milliseconds it should take after a touch down event should generate a long click - event. */ - static int getLongClickDelayMS(); - - /** Get how many pixels of dragging should start panning scrollable widgets. */ - static int getPanThreshold(); - - /** Get how many pixels a typical line is: The length that should be scrolled when turning a mouse - wheel one notch. */ - static int getPixelsPerLine(); - - /** Get Dots Per Inch for the main screen. */ - static int getDPI(); -}; - -/** TBClipboard is a porting interface for the clipboard. */ -class TBClipboard { -public: - /** Empty the contents of the clipboard. */ - static void empty(); - - /** Return true if the clipboard currently contains text. */ - static bool hasText(); - - /** Set the text of the clipboard in UTF-8 format. */ - static bool setText(const char *text); - - /** Get the text from the clipboard in UTF-8 format. - Returns true on success. */ - static bool getText(core::String &text); -}; - -/** TBFile is a porting interface for file access. */ -class TBFile { -public: - enum TBFileMode { MODE_READ }; - static TBFile *open(const char *filename, TBFileMode mode); - - virtual ~TBFile() { - } - virtual long size() = 0; - virtual size_t read(void *buf, size_t elemSize, size_t count) = 0; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_tab_container.cpp b/src/modules/ui/turbobadger/tb/tb_tab_container.cpp deleted file mode 100644 index 64fa0f30f..000000000 --- a/src/modules/ui/turbobadger/tb/tb_tab_container.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @file - */ - -#include "tb_tab_container.h" -#include "core/Assert.h" - -namespace tb { - -void TBTabLayout::onChildAdded(TBWidget *child) { - if (TBButton *button = TBSafeCast(child)) { - button->setSqueezable(true); - button->setSkinBg(TBIDC("TBTabContainer.tab")); - button->setID(TBIDC("tab")); - } -} - -PreferredSize TBTabLayout::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - PreferredSize ps = TBLayout::onCalculatePreferredContentSize(constraints); - // Make sure the number of tabs doesn't grow parents. - // It is only the content that should do that. The tabs - // will scroll anyway. - if (getAxis() == AXIS_X) { - ps.min_w = Min(ps.min_w, 1); - } else { - ps.min_h = Min(ps.min_h, 1); - } - return ps; -} - -TBTabContainer::TBTabContainer() : m_need_page_update(true), m_current_page(0), m_align(TB_ALIGN_TOP) { - addChild(&m_root_layout); - // Put the tab layout on top of the content in Z order so their skin can make - // a seamless overlap over the border. Control which side they are layouted - // to by calling SetLayoutOrder. - m_root_layout.addChild(&m_content_root); - m_root_layout.addChild(&m_tab_layout); - m_root_layout.setAxis(AXIS_Y); - m_root_layout.setGravity(WIDGET_GRAVITY_ALL); - m_root_layout.setLayoutDistribution(LAYOUT_DISTRIBUTION_AVAILABLE); - m_root_layout.setLayoutOrder(LAYOUT_ORDER_TOP_TO_BOTTOM); - m_root_layout.setSkinBg(TBIDC("TBTabContainer.rootlayout")); - m_tab_layout.setLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_CENTER); - m_tab_layout.setSkinBg(TBIDC("TBTabContainer.tablayout_x")); - m_tab_layout.setLayoutPosition(LAYOUT_POSITION_RIGHT_BOTTOM); - m_content_root.setGravity(WIDGET_GRAVITY_ALL); - m_content_root.setSkinBg(TBIDC("TBTabContainer.container")); -} - -TBTabContainer::~TBTabContainer() { - m_root_layout.removeChild(&m_content_root); - m_root_layout.removeChild(&m_tab_layout); - removeChild(&m_root_layout); -} - -void TBTabContainer::setAxis(AXIS axis) { - m_root_layout.setAxis(axis); - m_tab_layout.setAxis(axis == AXIS_X ? AXIS_Y : AXIS_X); - m_tab_layout.setSkinBg(axis == AXIS_X ? TBIDC("TBTabContainer.tablayout_y") : TBIDC("TBTabContainer.tablayout_x")); -} - -void TBTabContainer::setValue(int index) { - if (index == m_current_page) { - return; - } - m_current_page = index; - - // Update the pages visibility and tabs pressed value. - index = 0; - TBWidget *page = m_content_root.getFirstChild(); - TBWidget *tab = m_tab_layout.getFirstChild(); - for (; (page != nullptr) && (tab != nullptr); page = page->getNext(), tab = tab->getNext(), index++) { - bool active = index == m_current_page; - page->setVisibility(active ? WIDGET_VISIBILITY_VISIBLE : WIDGET_VISIBILITY_INVISIBLE); - tab->setValue(active ? 1 : 0); - } -} - -int TBTabContainer::getNumPages() { - int count = 0; - for (TBWidget *tab = m_tab_layout.getFirstChild(); tab != nullptr; tab = tab->getNext()) { - count++; - } - return count; -} - -TBWidget *TBTabContainer::getCurrentPageWidget() const { - return m_content_root.getChildFromIndex(m_current_page); -} - -void TBTabContainer::setAlignment(TB_ALIGN align) { - bool horizontal = (align == TB_ALIGN_TOP || align == TB_ALIGN_BOTTOM); - bool reverse = (align == TB_ALIGN_TOP || align == TB_ALIGN_LEFT); - setAxis(horizontal ? AXIS_Y : AXIS_X); - m_root_layout.setLayoutOrder(reverse ? LAYOUT_ORDER_TOP_TO_BOTTOM : LAYOUT_ORDER_BOTTOM_TO_TOP); - m_tab_layout.setLayoutPosition(reverse ? LAYOUT_POSITION_RIGHT_BOTTOM : LAYOUT_POSITION_LEFT_TOP); - m_align = align; -} - -bool TBTabContainer::onEvent(const TBWidgetEvent &ev) { - if ((ev.type == EVENT_TYPE_CLICK || ev.type == EVENT_TYPE_POINTER_DOWN) && ev.target->getID() == TBIDC("tab") && - ev.target->getParent() == &m_tab_layout) { - int clicked_index = m_tab_layout.getIndexFromChild(ev.target); - setValue(clicked_index); - return true; - } - return false; -} - -void TBTabContainer::onProcess() { - if (m_need_page_update) { - m_need_page_update = false; - // Force update value - int current_page = m_current_page; - m_current_page = -1; - setValue(current_page); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_tab_container.h b/src/modules/ui/turbobadger/tb/tb_tab_container.h deleted file mode 100644 index 8f84d25fc..000000000 --- a/src/modules/ui/turbobadger/tb/tb_tab_container.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets_common.h" - -namespace tb { - -/** TBTabLayout is a TBLayout used in TBTabContainer to apply - some default properties on any TBButton added to it. */ -class TBTabLayout : public TBLayout { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBTabLayout, TBLayout); - - virtual void onChildAdded(TBWidget *child) override; - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override; -}; - -/** TBTabContainer - A container with tabs for multiple pages. */ -class TBTabContainer : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBTabContainer, TBWidget); - - TBTabContainer(); - ~TBTabContainer(); - - /** Set along which axis the content should layouted. - Use SetAlignment instead for more choice! Also, calling - setAxis directly does not update the current alignment. */ - virtual void setAxis(AXIS axis) override; - virtual AXIS getAxis() const override { - return m_root_layout.getAxis(); - } - - /** Set alignment of the tabs. */ - void setAlignment(TB_ALIGN align); - TB_ALIGN getAlignment() const { - return m_align; - } - - /** Set which page should be selected and visible. */ - virtual void setValue(int index) override; - virtual int getValue() const override { - return m_current_page; - } - - /** Set which page should be selected and visible. */ - void setCurrentPage(int index) { - setValue(index); - } - int getCurrentPage() { - return getValue(); - } - int getNumPages(); - - /** Return the widget that is the current page, or nullptr if none is active. */ - TBWidget *getCurrentPageWidget() const; - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onProcess() override; - - virtual TBWidget *getContentRoot() override { - return &m_content_root; - } - TBLayout *getTabLayout() { - return &m_tab_layout; - } - -protected: - TBLayout m_root_layout; - TBTabLayout m_tab_layout; - TBWidget m_content_root; - bool m_need_page_update; - int m_current_page; - TB_ALIGN m_align; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_tempbuffer.cpp b/src/modules/ui/turbobadger/tb/tb_tempbuffer.cpp deleted file mode 100644 index 55965254f..000000000 --- a/src/modules/ui/turbobadger/tb/tb_tempbuffer.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file - */ - -#include "tb_tempbuffer.h" -#include "core/Assert.h" -#include "tb_system.h" -#include - -namespace tb { - -TBTempBuffer::TBTempBuffer() : m_data(nullptr), m_data_size(0), m_append_pos(0) { -} - -TBTempBuffer::~TBTempBuffer() { - SDL_free(m_data); -} - -void TBTempBuffer::setAppendPos(int appendPos) { - core_assert(appendPos >= 0 && appendPos <= m_data_size); - m_append_pos = appendPos; -} - -bool TBTempBuffer::reserve(int size) { - if (size > m_data_size) { - char *new_data = (char *)SDL_realloc(m_data, size); - if (new_data == nullptr) { - return false; - } - m_data = new_data; - m_data_size = size; - } - return true; -} - -int TBTempBuffer::getAppendReserveSize(int neededSize) const { - // Reserve some extra memory to reduce the reserve calls. - neededSize *= 2; - return neededSize < 32 ? 32 : neededSize; -} - -bool TBTempBuffer::append(const char *data, int size) { - if (m_append_pos + size > m_data_size && !reserve(getAppendReserveSize(m_append_pos + size))) { - return false; - } - SDL_memcpy(m_data + m_append_pos, data, size); - m_append_pos += size; - return true; -} - -bool TBTempBuffer::appendSpace(int size) { - if (m_append_pos + size > m_data_size && !reserve(getAppendReserveSize(m_append_pos + size))) { - return false; - } - m_append_pos += size; - return true; -} - -bool TBTempBuffer::appendString(const core::String &str) { - // Add 1 to include the null termination in the data. - if (append(str.c_str(), str.size() + 1)) { - // Now remove the null termination from the append position - // again, so another call will append to the same string (instead of - // after the null termination of the first string) - m_append_pos--; - return true; - } - return false; -} - -bool TBTempBuffer::appendString(const char *str) { - // Add 1 to include the null termination in the data. - if (append(str, SDL_strlen(str) + 1)) { - // Now remove the null termination from the append position - // again, so another call will append to the same string (instead of - // after the null termination of the first string) - m_append_pos--; - return true; - } - return false; -} - -bool TBTempBuffer::appendPath(const char *fullPathAndFilename) { - const char *str_start = fullPathAndFilename; - while (const char *next = strpbrk(fullPathAndFilename, "\\/")) { - fullPathAndFilename = next + 1; - } - - if (str_start == fullPathAndFilename) // Filename contained no path - { - str_start = "./"; - fullPathAndFilename = str_start + 2; - } - - const int len = fullPathAndFilename - str_start; - if (reserve(m_append_pos + len + 1)) { - // Add the string, and nulltermination. - append(str_start, len); - append("", 1); - // Remove null termination from append pos again (see details in AppendString). - m_append_pos--; - return true; - } - return false; -} - -bool TBTempBuffer::appendFile(const char *filename) { - if (TBFile *file = TBFile::open(filename, TBFile::MODE_READ)) { - const size_t file_size = file->size(); - if (reserve(m_append_pos + file_size + 1) && file->read(m_data + m_append_pos, 1, file_size) == file_size) { - // Increase append position and null terminate - m_append_pos += file_size; - m_data[m_append_pos] = 0; - delete file; - return true; - } - delete file; - } - return false; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_tempbuffer.h b/src/modules/ui/turbobadger/tb/tb_tempbuffer.h deleted file mode 100644 index 3c3c607b2..000000000 --- a/src/modules/ui/turbobadger/tb/tb_tempbuffer.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/String.h" - -namespace tb { - -/** TBTempBuffer manages a buffer that will be deleted on destruction. - - The buffer size can grow by calling Reserve or Append, but it - will never shrink during the lifetime of the object. -*/ -class TBTempBuffer { -public: - TBTempBuffer(); - ~TBTempBuffer(); - - /** Make sure the buffer has at least size bytes. - Returns false on OOM. */ - bool reserve(int size); - - /** Get a pointer to the buffer data. */ - char *getData() const { - return m_data; - } - - /** Return the size of the buffer in bytes. */ - int getCapacity() const { - return m_data_size; - } - - /** Append data with size bytes at the end of the buffer and - increase the append position with the same amount. - Returns false on OOM. */ - bool append(const char *data, int size); - - /** Increase the append position with size bytes without - writing any data. This is useful if you want to write - the data later and want to make sure space is reserved. - Returns false on OOM. */ - bool appendSpace(int size); - - /** Append a null terminated string (including the null termination) - at the end of the buffer. The append position will be increased - with the length of the text (excluding the null termination) so - multiple calls will produce a concatenated null terminated string. - Returns false on OOM. */ - bool appendString(const char *str); - bool appendString(const core::String& str); - - /** Append a path without the ending filename. - The buffer will be null terminated and the append position will be - increased with the length of the path (excluding the null termination). */ - bool appendPath(const char *full_path_and_filename); - - /** Append file content at the end of the buffer. The append position will - be increased by the size of the file. It will always append null - termination (not included in append position). - Returns false of OOM or if loading failed. - */ - bool appendFile(const char *filename); - - /** Set the position (in bytes) in the buffer where Append should write. */ - void setAppendPos(int append_pos); - - /** Reset the append position to 0. */ - void resetAppendPos() { - m_append_pos = 0; - } - - /** Return the current append position in in bytes. */ - int getAppendPos() const { - return m_append_pos; - } - -private: - int getAppendReserveSize(int needed_size) const; - char *m_data; - int m_data_size; - int m_append_pos; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_toggle_container.cpp b/src/modules/ui/turbobadger/tb/tb_toggle_container.cpp deleted file mode 100644 index b14fc5e5a..000000000 --- a/src/modules/ui/turbobadger/tb/tb_toggle_container.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @file - */ - -#include "tb_toggle_container.h" -#include "tb_node_tree.h" -#include "tb_widgets_reader.h" - -namespace tb { - -TBSectionHeader::TBSectionHeader() { - setSkinBg(TBIDC("TBSectionHeader")); - setGravity(WIDGET_GRAVITY_LEFT | WIDGET_GRAVITY_RIGHT); - setToggleMode(true); -} - -bool TBSectionHeader::onEvent(const TBWidgetEvent &ev) { - if (ev.target == this && ev.type == EVENT_TYPE_CHANGED && (getParent()->getParent() != nullptr)) { - if (TBSection *section = TBSafeCast(getParent()->getParent())) { - section->getContainer()->setValue(getValue()); - - // Try to scroll the container into view when expanded - section->setPendingScrollIntoView(getValue() != 0); - } - } - return TBButton::onEvent(ev); -} - -// == TBSectionHeader ===================================== - -TBSection::TBSection() : m_pending_scroll(false) { - setGravity(WIDGET_GRAVITY_LEFT | WIDGET_GRAVITY_RIGHT); - - setSkinBg(TBIDC("TBSection"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_layout.setSkinBg(TBIDC("TBSection.layout"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - - m_toggle_container.setSkinBg(TBIDC("TBSection.container")); - m_toggle_container.setToggle(TBToggleContainer::TOGGLE_EXPANDED); - m_toggle_container.setGravity(WIDGET_GRAVITY_ALL); - m_layout.setAxis(AXIS_Y); - m_layout.setGravity(WIDGET_GRAVITY_ALL); - m_layout.setLayoutSize(LAYOUT_SIZE_AVAILABLE); - - addChild(&m_layout); - m_layout.addChild(&m_header); - m_layout.addChild(&m_toggle_container); -} - -TBSection::~TBSection() { - m_layout.removeChild(&m_toggle_container); - m_layout.removeChild(&m_header); - removeChild(&m_layout); -} - -void TBSection::setValue(int value) { - m_header.setValue(value); - m_toggle_container.setValue(value); -} - -void TBSection::onProcessAfterChildren() { - if (m_pending_scroll) { - m_pending_scroll = false; - scrollIntoViewRecursive(); - } -} - -PreferredSize TBSection::onCalculatePreferredSize(const SizeConstraints &constraints) { - PreferredSize ps = TBWidget::onCalculatePreferredContentSize(constraints); - // We should not grow larger than we are, when there's extra space available. - ps.max_h = ps.pref_h; - return ps; -} - -// == TBToggleContainer =================================== - -TBToggleContainer::TBToggleContainer() : m_toggle(TOGGLE_NOTHING), m_invert(false), m_value(0) { - setSkinBg(TBIDC("TBToggleContainer"), WIDGET_INVOKE_INFO_NO_CALLBACKS); -} - -void TBToggleContainer::setToggle(TOGGLE toggle) { - if (toggle == m_toggle) { - return; - } - - if (m_toggle == TOGGLE_EXPANDED) { - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } - - m_toggle = toggle; - updateInternal(); -} - -void TBToggleContainer::setInvert(bool invert) { - if (invert == m_invert) { - return; - } - m_invert = invert; - updateInternal(); -} - -void TBToggleContainer::setValue(int value) { - if (value == m_value) { - return; - } - m_value = value; - updateInternal(); - invalidateSkinStates(); -} - -void TBToggleContainer::updateInternal() { - bool on = getIsOn(); - switch (m_toggle) { - case TOGGLE_NOTHING: - break; - case TOGGLE_ENABLED: - setState(WIDGET_STATE_DISABLED, !on); - break; - case TOGGLE_OPACITY: - setOpacity(on ? 1.F : 0); - break; - case TOGGLE_EXPANDED: - setVisibility(on ? WIDGET_VISIBILITY_VISIBLE : WIDGET_VISIBILITY_GONE); - - // Also disable when collapsed so tab focus skips the children. - setState(WIDGET_STATE_DISABLED, !on); - break; - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_toggle_container.h b/src/modules/ui/turbobadger/tb/tb_toggle_container.h deleted file mode 100644 index c819742c5..000000000 --- a/src/modules/ui/turbobadger/tb/tb_toggle_container.h +++ /dev/null @@ -1,139 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets_common.h" - -namespace tb { - -/** TBToggleContainer is a widget that toggles a property when its value - change between 0 and 1. TOGGLE specifies what property will toggle. - This is useful f.ex to toggle a whole group of child widgets depending - on the value of some other widget. By connecting the TBToggleContainer - with a widget connection, this can happen completly automatically. */ -class TBToggleContainer : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBToggleContainer, TBWidget); - - TBToggleContainer(); - - /** Defines what should toggle when the value changes. */ - enum TOGGLE { - TOGGLE_NOTHING, ///< Nothing happens (the default) - TOGGLE_ENABLED, ///< Enabled/disabled state - TOGGLE_OPACITY, ///< Opacity 1/0 - TOGGLE_EXPANDED ///< Expanded/collapsed (In parent axis direction) - }; - - /** Set what should toggle when the value changes. */ - void setToggle(TOGGLE toggle); - TOGGLE getToggle() const { - return m_toggle; - } - - /** Set if the toggle state should be inverted. */ - void setInvert(bool invert); - bool getInvert() const { - return m_invert; - } - - /** Get the current value, after checking the invert mode. */ - bool getIsOn() const { - return m_invert ? m_value == 0 : !(m_value == 0); - } - - /** Set the value of this widget. 1 will turn on the toggle, 0 will turn it off (or - the opposite if the invert mode is set). */ - virtual void setValue(int value) override; - virtual int getValue() const override { - return m_value; - } - - virtual void onInflate(const INFLATE_INFO &info) override; - -private: - void updateInternal(); - TOGGLE m_toggle; - bool m_invert; - int m_value; -}; - -/** TBSectionHeader is just a thin wrapper for a TBButton that is in toggle - mode with the skin TBSectionHeader by default. It is used as the clickable - header in TBSection that toggles the section. */ -class TBSectionHeader : public TBButton { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSectionHeader, TBButton); - - TBSectionHeader(); - - virtual bool onEvent(const TBWidgetEvent &ev) override; -}; - -/** TBSection is a widget with a header that when clicked toggles its children - on and off (using a internal TBToggleContainer with TOGGLE_EXPANDED). - - The header is a TBSectionHeader. - - The skin names of the internal widgets are: - TBSection - This widget itself. - TBSection.layout - The layout that wraps the header and the container. - TBSection.container - The toggle container with the children that expands/collapses. -*/ - -class TBSection : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSection, TBWidget); - - TBSection(); - ~TBSection(); - - TBLayout *getLayout() { - return &m_layout; - } - TBSectionHeader *getHeader() { - return &m_header; - } - TBToggleContainer *getContainer() { - return &m_toggle_container; - } - - /** Set if the section should be scrolled into view after next layout. */ - void setPendingScrollIntoView(bool pendingScroll) { - m_pending_scroll = pendingScroll; - } - - /** Set the text of the text field. */ - virtual bool setText(const char *text) override { - return m_header.setText(text); - } - virtual bool getText(core::String &text) override { - return m_header.getText(text); - } - using TBWidget::getText; ///< Make all versions in base class available. - - virtual void setValue(int value) override; - virtual int getValue() const override { - return m_toggle_container.getValue(); - } - - virtual TBWidget *getContentRoot() override { - return m_toggle_container.getContentRoot(); - } - virtual void onProcessAfterChildren() override; - - virtual PreferredSize onCalculatePreferredSize(const SizeConstraints &constraints) override; - -private: - TBLayout m_layout; - TBSectionHeader m_header; - TBToggleContainer m_toggle_container; - bool m_pending_scroll; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_types.h b/src/modules/ui/turbobadger/tb/tb_types.h deleted file mode 100644 index 6d56f4122..000000000 --- a/src/modules/ui/turbobadger/tb/tb_types.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include - -#include -#include - -namespace tb { - -template T Max(const T &left, const T &right) { - return left > right ? left : right; -} - -template T Min(const T &left, const T &right) { - return left < right ? left : right; -} - -template T Abs(const T &value) { - return value < 0 ? -value : value; -} - -template T Clamp(const T &value, const T &min, const T &max) { - return (value > max) ? max : ((value < min) ? min : value); -} - -/** Returns value clamped to min and max. If max is greater than min, - max will be clipped to min. */ -template T ClampClipMax(const T &value, const T &min, const T &max) { - return (value > max) ? (max > min ? max : min) : ((value < min) ? min : value); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_value.cpp b/src/modules/ui/turbobadger/tb/tb_value.cpp deleted file mode 100644 index 0e2d8285b..000000000 --- a/src/modules/ui/turbobadger/tb/tb_value.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/** - * @file - */ - -#include "tb_value.h" -#include "core/Assert.h" -#include "core/StringUtil.h" -#include "tb_object.h" -#include "tb_str.h" -#include - -namespace tb { - -// FIX: ## Floating point string conversions might be locale dependant. Force "." as decimal! - -char *next_token(char *&str, const char *delim) { - str += strspn(str, delim); - if (*str == 0) { - return nullptr; - } - char *token = str; - str += strcspn(str, delim); - if (*str != 0) { - *str++ = '\0'; - } - return token; -} - -bool is_start_of_number(const char *str) { - if (*str == '-') { - str++; - } - if (*str == '.') { - str++; - } - return *str >= '0' && *str <= '9'; -} - -bool contains_non_trailing_space(const char *str) { - if (const char *p = SDL_strstr(str, " ")) { - while (*p == ' ') { - p++; - } - return *p != '\0'; - } - return false; -} - -bool is_number_only(const char *s) { - if ((s == nullptr) || *s == 0 || *s == ' ') { - return false; - } - char *p; - SDL_strtod(s, &p); - while (*p == ' ') { - p++; - } - return *p == '\0'; -} - -bool is_number_float(const char *str) { - while (*str != 0) { - if (*str++ == '.') { - return true; - } - } - return false; -} - -TBValueArray::TBValueArray() { -} - -TBValueArray::~TBValueArray() { -} - -TBValue *TBValueArray::addValue() { - TBValue *v; - if (((v = new TBValue()) != nullptr) && m_list.add(v)) { - return v; - } - delete v; - return nullptr; -} - -TBValue *TBValueArray::getValue(int index) { - if (index >= 0 && index < m_list.getNumItems()) { - return m_list[index]; - } - return nullptr; -} - -TBValueArray *TBValueArray::clone(TBValueArray *source) { - TBValueArray *new_arr = new TBValueArray; - if (new_arr == nullptr) { - return nullptr; - } - for (int i = 0; i < source->m_list.getNumItems(); i++) { - TBValue *new_val = new_arr->addValue(); - if (new_val == nullptr) { - delete new_arr; - return nullptr; - } - new_val->copy(*source->getValue(i)); - } - return new_arr; -} - -TBValue::TBValue() : m_packed_init(0) { -} - -TBValue::TBValue(const TBValue &value) : m_packed_init(0) { - copy(value); -} - -TBValue::TBValue(TYPE type) : m_packed_init(0) { - switch (type) { - case TYPE_NULL: - setNull(); - break; - case TYPE_STRING: - setString("", SET_AS_STATIC); - break; - case TYPE_FLOAT: - setFloat(0); - break; - case TYPE_INT: - setInt(0); - break; - case TYPE_OBJECT: - setObject(nullptr); - break; - case TYPE_ARRAY: - if (TBValueArray *arr = new TBValueArray()) { - setArray(arr, SET_TAKE_OWNERSHIP); - } - break; - default: - core_assert(!"Not implemented!"); - } -} - -TBValue::TBValue(int value) : m_packed_init(0) { - setInt(value); -} - -TBValue::TBValue(float value) : m_packed_init(0) { - setFloat(value); -} - -TBValue::TBValue(const char *value, SET set) : m_packed_init(0) { - setString(value, set); -} - -TBValue::TBValue(TBTypedObject *object) : m_packed_init(0) { - setObject(object); -} - -TBValue::~TBValue() { - setNull(); -} - -void TBValue::takeOver(TBValue &sourceValue) { - if (sourceValue.m_packed.type == TYPE_STRING) { - setString(sourceValue.val_str, sourceValue.m_packed.allocated ? SET_TAKE_OWNERSHIP : SET_NEW_COPY); - } else if (sourceValue.m_packed.type == TYPE_ARRAY) { - setArray(sourceValue.val_arr, sourceValue.m_packed.allocated ? SET_TAKE_OWNERSHIP : SET_NEW_COPY); - } else { - *this = sourceValue; - } - sourceValue.m_packed.type = TYPE_NULL; -} - -void TBValue::copy(const TBValue &sourceValue) { - if (sourceValue.m_packed.type == TYPE_STRING) { - setString(sourceValue.val_str, SET_NEW_COPY); - } else if (sourceValue.m_packed.type == TYPE_ARRAY) { - setArray(sourceValue.val_arr, SET_NEW_COPY); - } else if (sourceValue.m_packed.type == TYPE_OBJECT) { - core_assert(!"We can't copy objects! The value will be nulled!"); - setObject(nullptr); - } else { - setNull(); - this->m_packed_init = sourceValue.m_packed_init; - this->val_obj = sourceValue.val_obj; - } -} - -void TBValue::setNull() { - if (m_packed.allocated) { - if (m_packed.type == TYPE_STRING) { - SDL_free(val_str); - } else if (m_packed.type == TYPE_OBJECT) { - delete val_obj; - } else if (m_packed.type == TYPE_ARRAY) { - delete val_arr; - } - } - m_packed.type = TYPE_NULL; -} - -void TBValue::setInt(int val) { - setNull(); - m_packed.type = TYPE_INT; - val_int = val; -} - -void TBValue::setFloat(float val) { - setNull(); - m_packed.type = TYPE_FLOAT; - val_float = val; -} - -void TBValue::setString(const char *val, SET set) { - setNull(); - m_packed.allocated = (set == SET_NEW_COPY || set == SET_TAKE_OWNERSHIP); - if (set != SET_NEW_COPY) { - val_str = const_cast(val); - m_packed.type = TYPE_STRING; - } else if ((val_str = SDL_strdup(val)) != nullptr) { - m_packed.type = TYPE_STRING; - } -} - -void TBValue::setObject(TBTypedObject *object) { - setNull(); - m_packed.type = TYPE_OBJECT; - m_packed.allocated = true; - val_obj = object; -} - -void TBValue::setArray(TBValueArray *arr, SET set) { - setNull(); - m_packed.allocated = (set == SET_NEW_COPY || set == SET_TAKE_OWNERSHIP); - if (set != SET_NEW_COPY) { - val_arr = arr; - m_packed.type = TYPE_ARRAY; - } else if ((val_arr = TBValueArray::clone(arr)) != nullptr) { - m_packed.type = TYPE_ARRAY; - } -} - -void TBValue::setFromStringAuto(const char *str, SET set) { - if (str == nullptr) { - setNull(); - } else if (is_number_only(str)) { - if (is_number_float(str)) { - setFloat(core::string::toFloat(str)); - } else { - setInt(core::string::toInt(str)); - } - } else if (is_start_of_number(str) && contains_non_trailing_space(str)) { - // If the number has nontrailing space, we'll assume a list of numbers (example: "10 -4 3.5") - setNull(); - if (TBValueArray *arr = new TBValueArray) { - core::String tmpstr = str; - char *str_next = tmpstr.c_str(); - while (char *token = next_token(str_next, ", ")) { - if (TBValue *new_val = arr->addValue()) { - new_val->setFromStringAuto(token, SET_NEW_COPY); - } - } - setArray(arr, SET_TAKE_OWNERSHIP); - } - } else if (*str == '[') { - setNull(); - if (TBValueArray *arr = new TBValueArray) { - core_assert(!"not implemented! Split out the tokenizer code above!"); - setArray(arr, SET_TAKE_OWNERSHIP); - } - } else { - setString(str, set); - return; - } - // We didn't set as string, so we might need to deal with the passed in string data. - if (set == SET_TAKE_OWNERSHIP) { - // Delete the passed in data - TBValue tmp; - tmp.setString(str, SET_TAKE_OWNERSHIP); - } -} - -int TBValue::getInt() const { - if (m_packed.type == TYPE_STRING) { - return core::string::toInt(val_str); - } - if (m_packed.type == TYPE_FLOAT) { - return (int)val_float; - } - return m_packed.type == TYPE_INT ? val_int : 0; -} - -float TBValue::getFloat() const { - if (m_packed.type == TYPE_STRING) { - return core::string::toFloat(val_str); - } - if (m_packed.type == TYPE_INT) { - return (float)val_int; - } - return m_packed.type == TYPE_FLOAT ? val_float : 0; -} - -const char *TBValue::getString() { - if (m_packed.type == TYPE_INT) { - char tmp[32]; - SDL_snprintf(tmp, sizeof(tmp), "%d", val_int); - setString(tmp, SET_NEW_COPY); - } else if (m_packed.type == TYPE_FLOAT) { - char tmp[32]; - SDL_snprintf(tmp, sizeof(tmp), "%f", val_float); - setString(tmp, SET_NEW_COPY); - } else if (m_packed.type == TYPE_OBJECT) { - return val_obj != nullptr ? val_obj->getClassName() : ""; - } - return m_packed.type == TYPE_STRING ? val_str : ""; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_value.h b/src/modules/ui/turbobadger/tb/tb_value.h deleted file mode 100644 index 7cfeb5f73..000000000 --- a/src/modules/ui/turbobadger/tb/tb_value.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_list.h" -#include "core/String.h" - -namespace tb { - -class TBValue; -class TBTypedObject; - -/** Return true if the given string starts with a number. - Ex: 100, -.2, 1.0E-8, 5px will all return true. */ -bool is_start_of_number(const char *str); - -/** Returns true if the given string contains space that - is not at the end of the string. */ -bool contains_non_trailing_space(const char *str); - -/** Return true if the string can be represented as a number. - It ignores trailing white space. - Ex: 100, -.2 will return true. - Ex: 1.0E-8, 5px will return false. */ -bool is_number_only(const char *str); - -/** Return true if the given number string is a float number. - Should only be called when you've verified it's a number with is_number(). */ -bool is_number_float(const char *str); - -/** TBValueArray is an array of TBValue */ -class TBValueArray { -public: - TBValueArray(); - ~TBValueArray(); - TBValue *addValue(); - TBValue *getValue(int index); - static TBValueArray *clone(TBValueArray *source); - int getLength() const { - return m_list.getNumItems(); - } - -private: - TBListAutoDeleteOf m_list; -}; - -/** TBValue holds value of a specific type. - In addition to NULL, string, float, integer, it may also contain an array - of attributes (TBValueArray), or an object (derived from TBTypedObject). - - When getting the value as a different type from what it is, it may convert - its internal representation to that type. Exceptions are for array and - object, which will return 0 when getting as numbers, or "" or object name - when getting as string. */ -class TBValue { -public: - /** The current type of the value. - It may change when using a getter of a different type. */ - enum TYPE { TYPE_NULL, TYPE_STRING, TYPE_FLOAT, TYPE_INT, TYPE_OBJECT, TYPE_ARRAY }; - - /** How to deal with the dynamic memory when setting string and array. */ - enum SET { - SET_NEW_COPY, ///< A new copy of the data will be made. - SET_TAKE_OWNERSHIP, ///< The data passed in will be stored and freed. - SET_AS_STATIC ///< The data passed in will be stored but never freed. - }; - - TBValue(); - TBValue(const TBValue &value); - TBValue(TYPE type); - - TBValue(int value); - TBValue(float value); - TBValue(const char *value, SET set = SET_NEW_COPY); - TBValue(TBTypedObject *object); - - ~TBValue(); - - /** Take over ownership of content of source_value. - Note: -If source_value has string or array that are set with SET_AS_STATIC, it will make new copies of those. - -value will be nulled on source_value after this call. */ - void takeOver(TBValue &source_value); - - /** Copy the content of source_value to this value. - Note: This value will become TYPE_NULL if source_value holds an owned object. We can't copy objects. */ - void copy(const TBValue &source_value); - - void setNull(); - void setInt(int val); - void setFloat(float val); - - /** Set the passed in string */ - void setString(const char *val, SET set); - - /** Set the passed in string */ - void setString(const core::String& val, SET set) { - setString(val.c_str(), set); - } - - /** Set the passed in object. Takes the ownership of the object! */ - void setObject(TBTypedObject *object); - - /** Set the passed in array */ - void setArray(TBValueArray *arr, SET set); - - /** Set the value either as a string, number or array of numbers, depending of the string syntax. */ - void setFromStringAuto(const char *str, SET set); - - int getInt() const; - float getFloat() const; - const char *getString(); - TBTypedObject *getObject() const { - return isObject() ? val_obj : nullptr; - } - TBValueArray *getArray() const { - return isArray() ? val_arr : nullptr; - } - - TYPE getType() const { - return (TYPE)m_packed.type; - } - bool isNull() const { - return m_packed.type == TYPE_NULL; - } - bool isString() const { - return m_packed.type == TYPE_STRING; - } - bool isFloat() const { - return m_packed.type == TYPE_FLOAT; - } - bool isInt() const { - return m_packed.type == TYPE_INT; - } - bool isObject() const { - return m_packed.type == TYPE_OBJECT; - } - bool isArray() const { - return m_packed.type == TYPE_ARRAY; - } - int getArrayLength() const { - return isArray() ? val_arr->getLength() : 0; - } - - const TBValue &operator=(const TBValue &val) { - copy(val); - return *this; - } - -private: - union { - float val_float; - int val_int; - char *val_str; - TBTypedObject *val_obj; - TBValueArray *val_arr; - }; - union { - struct { - uint32_t type : 8; - uint32_t allocated : 1; - } m_packed; - uint32_t m_packed_init; - }; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widget_skin_condition_context.cpp b/src/modules/ui/turbobadger/tb/tb_widget_skin_condition_context.cpp deleted file mode 100644 index a7cb1eae8..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widget_skin_condition_context.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file - */ - -#include "tb_widget_skin_condition_context.h" -#include "tb_tab_container.h" -#include "tb_widgets_common.h" -#include "tb_window.h" - -namespace tb { - -bool TBWidgetSkinConditionContext::getCondition(TBSkinCondition::TARGET target, - const TBSkinCondition::CONDITION_INFO &info) { - switch (target) { - case TBSkinCondition::TARGET_THIS: - return getCondition(m_widget, info); - case TBSkinCondition::TARGET_PARENT: - return (m_widget->getParent() != nullptr) && getCondition(m_widget->getParent(), info); - case TBSkinCondition::TARGET_ANCESTORS: { - TBWidget *widget = m_widget->getParent(); - while (widget != nullptr) { - if (getCondition(widget, info)) { - return true; - } - widget = widget->getParent(); - } - return false; - } - case TBSkinCondition::TARGET_PREV_SIBLING: - return (m_widget->getPrev() != nullptr) && getCondition(m_widget->getPrev(), info); - case TBSkinCondition::TARGET_NEXT_SIBLING: - return (m_widget->getNext() != nullptr) && getCondition(m_widget->getNext(), info); - } - return false; -} - -bool TBWidgetSkinConditionContext::getCondition(TBWidget *widget, const TBSkinCondition::CONDITION_INFO &info) { - switch (info.prop) { - case TBSkinCondition::PROPERTY_SKIN: - return widget->getSkinBg() == info.value; - case TBSkinCondition::PROPERTY_WINDOW_ACTIVE: - if (TBWindow *window = widget->getParentWindow()) { - return window->isActive(); - } - return false; - case TBSkinCondition::PROPERTY_AXIS: - return TBID(widget->getAxis() == AXIS_X ? "x" : "y") == info.value; - case TBSkinCondition::PROPERTY_ALIGN: - if (TBTabContainer *tc = TBSafeCast(widget)) { - TBID widget_align; - if (tc->getAlignment() == TB_ALIGN_LEFT) { - widget_align = TBIDC("left"); - } else if (tc->getAlignment() == TB_ALIGN_TOP) { - widget_align = TBIDC("top"); - } else if (tc->getAlignment() == TB_ALIGN_RIGHT) { - widget_align = TBIDC("right"); - } else if (tc->getAlignment() == TB_ALIGN_BOTTOM) { - widget_align = TBIDC("bottom"); - } - return widget_align == info.value; - } - return false; - case TBSkinCondition::PROPERTY_ID: - return widget->getID() == info.value; - case TBSkinCondition::PROPERTY_STATE: - return !((widget->getAutoState() & info.value) == 0U); - case TBSkinCondition::PROPERTY_VALUE: - return widget->getValue() == (int)info.value; - case TBSkinCondition::PROPERTY_HOVER: - return (TBWidget::hovered_widget != nullptr) && widget->isAncestorOf(TBWidget::hovered_widget); - case TBSkinCondition::PROPERTY_CAPTURE: - return (TBWidget::captured_widget != nullptr) && widget->isAncestorOf(TBWidget::captured_widget); - case TBSkinCondition::PROPERTY_FOCUS: - return (TBWidget::focused_widget != nullptr) && widget->isAncestorOf(TBWidget::focused_widget); - case TBSkinCondition::PROPERTY_CUSTOM: - return widget->getCustomSkinCondition(info); - } - return false; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widget_skin_condition_context.h b/src/modules/ui/turbobadger/tb/tb_widget_skin_condition_context.h deleted file mode 100644 index 63bdf8c18..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widget_skin_condition_context.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_skin.h" -#include "tb_widgets.h" - -namespace tb { - -/** Check if a condition is true for a widget when painting a skin. */ - -class TBWidgetSkinConditionContext : public TBSkinConditionContext { -public: - TBWidgetSkinConditionContext(TBWidget *widget) : m_widget(widget) { - } - virtual ~TBWidgetSkinConditionContext() { - } - virtual bool getCondition(TBSkinCondition::TARGET target, const TBSkinCondition::CONDITION_INFO &info); - -private: - bool getCondition(TBWidget *widget, const TBSkinCondition::CONDITION_INFO &info); - TBWidget *m_widget; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widget_value.cpp b/src/modules/ui/turbobadger/tb/tb_widget_value.cpp deleted file mode 100644 index af03a9e61..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widget_value.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/** - * @file - */ - -#include "tb_widget_value.h" -#include "tb_widgets.h" - -namespace tb { - -void TBWidgetValueConnection::connect(TBWidgetValue *value, TBWidget *widget) { - unconnect(); - m_widget = widget; - m_value = value; - m_value->m_connections.addLast(this); - m_value->syncToWidget(m_widget); -} - -void TBWidgetValueConnection::unconnect() { - if (m_value != nullptr) { - m_value->m_connections.remove(this); - } - m_value = nullptr; - m_widget = nullptr; -} - -void TBWidgetValueConnection::syncFromWidget(TBWidget *sourceWidget) { - if (m_value != nullptr) { - m_value->setFromWidget(sourceWidget); - } -} - -TBWidgetValue::TBWidgetValue(const TBID &name, TBValue::TYPE type) : m_name(name), m_value(type), m_syncing(false) { -} - -TBWidgetValue::~TBWidgetValue() { - while (m_connections.getFirst() != nullptr) { - m_connections.getFirst()->unconnect(); - } -} - -void TBWidgetValue::setFromWidget(TBWidget *sourceWidget) { - if (m_syncing) { - return; // We ended up here because syncing is in progress. - } - - // Get the value in the format - core::String text; - switch (m_value.getType()) { - case TBValue::TYPE_STRING: - if (!sourceWidget->getText(text)) { - return; - } - m_value.setString(text, TBValue::SET_NEW_COPY); - break; - case TBValue::TYPE_NULL: - case TBValue::TYPE_INT: - m_value.setInt(sourceWidget->getValue()); - break; - case TBValue::TYPE_FLOAT: - // FIX: TBValue should use double instead of float? - m_value.setFloat((float)sourceWidget->getValueDouble()); - break; - default: - core_assert(!"Unsupported value type!"); - } - - syncToWidgets(sourceWidget); -} - -bool TBWidgetValue::syncToWidgets(TBWidget *excludeWidget) { - // FIX: Assign group to each value. Currently we only have one global group. - g_value_group.invokeOnValueChanged(this); - - bool fail = false; - TBLinkListOf::Iterator iter = m_connections.iterateForward(); - while (TBWidgetValueConnection *connection = iter.getAndStep()) { - if (connection->m_widget != excludeWidget) { - fail |= !syncToWidget(connection->m_widget); - } - } - return !fail; -} - -bool TBWidgetValue::syncToWidget(TBWidget *dstWidget) { - if (m_syncing) { - return true; // We ended up here because syncing is in progress. - } - - m_syncing = true; - bool ret = true; - switch (m_value.getType()) { - case TBValue::TYPE_STRING: - ret = dstWidget->setText(m_value.getString()); - break; - case TBValue::TYPE_NULL: - case TBValue::TYPE_INT: - dstWidget->setValue(m_value.getInt()); - break; - case TBValue::TYPE_FLOAT: - // FIX: TBValue should use double instead of float? - dstWidget->setValueDouble(m_value.getFloat()); - break; - default: - core_assert(!"Unsupported value type!"); - } - m_syncing = false; - return ret; -} - -void TBWidgetValue::setInt(int value) { - m_value.setInt(value); - syncToWidgets(nullptr); -} - -bool TBWidgetValue::setText(const char *text) { - m_value.setString(text, TBValue::SET_NEW_COPY); - return syncToWidgets(nullptr); -} - -void TBWidgetValue::setDouble(double value) { - // FIX: TBValue should use double instead of float? - m_value.setFloat((float)value); - syncToWidgets(nullptr); -} - -// == TBValueGroup ================================================================================ - -/*extern*/ TBValueGroup g_value_group; - -TBWidgetValue *TBValueGroup::createValueIfNeeded(const TBID &name, TBValue::TYPE type) { - if (TBWidgetValue *val = getValue(name)) { - return val; - } - if (TBWidgetValue *val = new TBWidgetValue(name, type)) { - if (m_values.add(name, val)) { - return val; - } - { delete val; } - } - return nullptr; -} - -void TBValueGroup::invokeOnValueChanged(const TBWidgetValue *value) { - TBLinkListOf::Iterator iter = m_listeners.iterateForward(); - while (TBValueGroupListener *listener = iter.getAndStep()) { - listener->onValueChanged(this, value); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widget_value.h b/src/modules/ui/turbobadger/tb/tb_widget_value.h deleted file mode 100644 index eb0969fb0..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widget_value.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_hashtable.h" -#include "tb_id.h" -#include "tb_linklist.h" -#include "tb_value.h" - -namespace tb { - -class TBWidget; -class TBWidgetValue; -class TBValueGroup; - -/** TBWidgetValueConnection maintains a connection between TBWidget and TBWidgetValue. */ - -class TBWidgetValueConnection : public TBLinkOf { -public: - TBWidgetValueConnection() { - } - ~TBWidgetValueConnection() { - unconnect(); - } - - /** Connect the value and widget. */ - void connect(TBWidgetValue *value, TBWidget *mWidget); - - /** Unconnect the value and widget if it is connected. */ - void unconnect(); - - /** Synchronize the value of the widget to the TBWidgetValue and all other - connected widgets. */ - void syncFromWidget(TBWidget *source_widget); - -private: - friend class TBWidgetValue; - TBWidgetValue *m_value = nullptr; - TBWidget *m_widget = nullptr; -}; - -/** TBWidgetValue stores a TBValue that will be synchronized with all widgets connected to it. - It has a TBID name, that can be used to identify this value within its TBValueGroup. - - It will synchronize with widgets when any of the connected widgets change and trig the - EVENT_TYPE_CHANGED event, and when the value is changed with any of the setters here. - - The synchronization with widgets is done through the generic TBWidget setters/getters, - TBWidget::setValue/getValue/setValueDouble/getValueDouble/getText/setText. - - The type that is synchronized is determined by the TBValue::TYPE specified in the - constructor. - - Note: The type that is synchronized changes if you request it in a different format! -*/ -class TBWidgetValue { -public: - TBWidgetValue(const TBID &name, TBValue::TYPE type = TBValue::TYPE_INT); - ~TBWidgetValue(); - - /** Set integer value and sync to connected widgets. */ - void setInt(int value); - - /** Set text value and sync to connected widgets. */ - bool setText(const char *text); - - /** Set double value and sync to connected widgets. */ - void setDouble(double value); - - /** Set the value from the given widget. Using the current format type.*/ - void setFromWidget(TBWidget *source_widget); - - /** Get value as integer. */ - int getInt() const { - return m_value.getInt(); - } - - /** Get value as text. Return false on fail. */ - bool getText(core::String &text) { - text = m_value.getString(); - return true; - } - - /** Get value as text. */ - core::String getText() { - core::String text; - getText(text); - return text; - } - - /** Get the value as double. */ - double getDouble() const { - return m_value.getFloat(); - } - - /** Get the TBValue used to store the value. */ - const TBValue &getValue() const { - return m_value; - } - - /** Get the name id. */ - TBID getName() const { - return m_name; - } - -private: - friend class TBWidgetValueConnection; - TBID m_name; - TBValue m_value; - TBLinkListOf m_connections; - bool m_syncing; - - bool syncToWidget(TBWidget *dst_widget); - bool syncToWidgets(TBWidget *exclude_widget); -}; - -/** Listener that will be notified when any of the values in a TBValueGroup is changed. */ -class TBValueGroupListener : public TBLinkOf { -public: - virtual ~TBValueGroupListener() { - if (linklist != nullptr) { - linklist->remove(this); - } - } - - /** Called when a value has changed and all widgets connected to it has been updated. */ - virtual void onValueChanged(const TBValueGroup *group, const TBWidgetValue *value) = 0; -}; - -/** TBValueGroup is a collection of widget values (TBWidgetValue) that can be fetched - by name (using a TBID). It also keeps a list of TBValueGroupListener that listens to - changes to any of the values. */ -class TBValueGroup { -public: - /** Create a TBWidgetValue with the given name if it does not already exist. - Returns nullptr if out of memory. */ - TBWidgetValue *createValueIfNeeded(const TBID &name, TBValue::TYPE type = TBValue::TYPE_INT); - - /** Get the TBWidgetValue with the given name, or nullptr if no match is found. */ - TBWidgetValue *getValue(const TBID &name) const { - return m_values.get(name); - } - - /** Add listener to this group. It will be removed automatically when deleted, - but can also be removed by RemoveListener. */ - void addListener(TBValueGroupListener *listener) { - m_listeners.addLast(listener); - } - - /** Remove listener from this group. */ - void removeListener(TBValueGroupListener *listener) { - m_listeners.remove(listener); - } - -private: - friend class TBWidgetValue; - void invokeOnValueChanged(const TBWidgetValue *value); - - TBHashTableAutoDeleteOf m_values; ///< Hash table of values - TBLinkListOf m_listeners; ///< List of listeners -}; - -/** The global value group. */ -extern TBValueGroup g_value_group; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets.cpp b/src/modules/ui/turbobadger/tb/tb_widgets.cpp deleted file mode 100644 index 316c1adbd..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets.cpp +++ /dev/null @@ -1,1813 +0,0 @@ -/** - * @file - */ - -#include "tb_widgets.h" -#include "core/Assert.h" -#include "command/CommandHandler.h" -#include "tb_font_renderer.h" -#include "tb_renderer.h" -#include "tb_scroller.h" -#include "tb_system.h" -#include "tb_widget_skin_condition_context.h" -#include "tb_widgets_common.h" -#include "tb_widgets_listener.h" -#include "tb_window.h" -#ifdef TB_ALWAYS_SHOW_EDIT_FOCUS -#include "tb_editfield.h" -#endif // TB_ALWAYS_SHOW_EDIT_FOCUS - -namespace tb { - -// static data -TBWidget *TBWidget::hovered_widget = nullptr; -TBWidget *TBWidget::captured_widget = nullptr; -TBWidget *TBWidget::focused_widget = nullptr; -int TBWidget::pointer_down_widget_x = 0; -int TBWidget::pointer_down_widget_y = 0; -int TBWidget::pointer_move_widget_x = 0; -int TBWidget::pointer_move_widget_y = 0; -bool TBWidget::cancel_click = false; -bool TBWidget::update_widget_states = true; -bool TBWidget::update_skin_states = true; -bool TBWidget::show_focus_state = false; - -static TBHashTableAutoDeleteOf s_touch_info; - -TBWidget::TOUCH_INFO *TBWidget::getTouchInfo(uint32_t id) { - return s_touch_info.get(id); -} - -static TBWidget::TOUCH_INFO *newTouchInfo(uint32_t id) { - core_assert(!s_touch_info.get(id)); - TBWidget::TOUCH_INFO *ti = new TBWidget::TOUCH_INFO; - memset(ti, 0, sizeof(TBWidget::TOUCH_INFO)); - s_touch_info.add(id, ti); - return ti; -} - -static void DeleteTouchInfo(uint32_t id) { - s_touch_info.deleteKey(id); -} - -// == TBLongClickTimer ================================================================== - -/** One shot timer for long click event */ -class TBLongClickTimer : private TBMessageHandler { -public: - TBLongClickTimer(TBWidget *widget, BUTTON_TYPE type) : m_widget(widget), m_type(type) { - postMessageDelayed(TBIDC("TBLongClickTimer"), nullptr, TBSystem::getLongClickDelayMS()); - } - virtual void onMessageReceived(TBMessage *msg) { - core_assert(msg->message == TBIDC("TBLongClickTimer")); - m_widget->maybeInvokeLongClickOrContextMenu(m_type); - } - -private: - TBWidget *m_widget; - BUTTON_TYPE m_type; -}; - -// == TBWidget::PaintProps ============================================================== - -TBWidget::PaintProps::PaintProps() { - // Set the default properties, used for the root widgets - // calling InvokePaint. The base values for all inheritance. - text_color = g_tb_skin->getDefaultTextColor(); -} - -// == TBWidget ========================================================================== - -TBWidget::TBWidget() - : m_parent(nullptr), m_opacity(1.F), m_state(WIDGET_STATE_NONE), m_gravity(WIDGET_GRAVITY_DEFAULT), - m_layout_params(nullptr), m_scroller(nullptr), m_long_click_timer(nullptr), m_packed_init(0) { -#ifdef TB_RUNTIME_DEBUG_INFO - last_measure_time = 0; - last_layout_time = 0; -#endif // TB_RUNTIME_DEBUG_INFO -} - -TBWidget::~TBWidget() { - core_assert(!m_parent); ///< A widget must be removed from parent before deleted - m_packed.is_dying = true; - - // Unreference from pointer capture - if (this == hovered_widget) { - hovered_widget = nullptr; - } - if (this == captured_widget) { - captured_widget = nullptr; - } - if (this == focused_widget) { - focused_widget = nullptr; - } - - // Unreference from touch info - TBHashTableIteratorOf it(&s_touch_info); - while (TOUCH_INFO *ti = it.getNextContent()) { - if (this == ti->hovered_widget) { - ti->hovered_widget = nullptr; - } - if (this == ti->captured_widget) { - ti->captured_widget = nullptr; - } - } - - TBWidgetListener::invokeWidgetDelete(this); - deleteAllChildren(); - - delete m_scroller; - delete m_layout_params; - - stopLongClickTimer(); - - core_assert(!m_listeners.hasLinks()); // There's still listeners added to this widget! -} - -void TBWidget::setRect(const TBRect &rect) { - if (m_rect.equals(rect)) { - return; - } - - TBRect old_rect = m_rect; - m_rect = rect; - - if (old_rect.w != m_rect.w || old_rect.h != m_rect.h) { - onResized(old_rect.w, old_rect.h); - } - - invalidate(); -} - -void TBWidget::invalidate() { - if (!getVisibilityCombined() && !m_rect.isEmpty()) { - return; - } - TBWidget *tmp = this; - while (tmp != nullptr) { - tmp->onInvalid(); - tmp = tmp->m_parent; - } -} - -void TBWidget::invalidateStates() { - update_widget_states = true; - invalidateSkinStates(); -} - -void TBWidget::invalidateSkinStates() { - update_skin_states = true; -} - -void TBWidget::die() { - if (m_packed.is_dying) { - return; - } - m_packed.is_dying = true; - onDie(); - if (!TBWidgetListener::invokeWidgetDying(this)) { - // No one was interested, so die immediately. - removeFromParent(); - delete this; - } -} - -TBWidget *TBWidget::getWidgetByIDInternal(const TBID &id, const TB_TYPE_ID typeId) { - if (m_id == id && ((typeId == nullptr) || isOfTypeId(typeId))) { - return this; - } - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (TBWidget *sub_child = child->getWidgetByIDInternal(id, typeId)) { - return sub_child; - } - } - return nullptr; -} - -core::String TBWidget::getTextByID(const TBID &id) { - if (TBWidget *widget = getWidgetByID(id)) { - return widget->getText(); - } - return ""; -} - -int TBWidget::getValueByID(const TBID &id) { - if (TBWidget *widget = getWidgetByID(id)) { - return widget->getValue(); - } - return 0; -} - -void TBWidget::setID(const TBID &id) { - m_id = id; - invalidateSkinStates(); -} - -void TBWidget::setStateRaw(WIDGET_STATE state) { - if (m_state == state) { - return; - } - m_state = state; - invalidate(); - invalidateSkinStates(); -} - -void TBWidget::setState(WIDGET_STATE state, bool on) { - setStateRaw(on ? m_state | state : m_state & ~state); -} - -WIDGET_STATE TBWidget::getAutoState() const { - WIDGET_STATE state = m_state; - bool add_pressed_state = !cancel_click && this == captured_widget && this == hovered_widget; - if (add_pressed_state) { - state |= WIDGET_STATE_PRESSED; - } - if (this == hovered_widget && (!m_packed.no_automatic_hover_state || add_pressed_state)) { - state |= WIDGET_STATE_HOVERED; - } - if (this == focused_widget && show_focus_state) { - state |= WIDGET_STATE_FOCUSED; - } -#ifdef TB_ALWAYS_SHOW_EDIT_FOCUS - else if (this == focused_widget && IsOfType()) - state |= WIDGET_STATE_FOCUSED; -#endif - return state; -} - -// static -void TBWidget::setAutoFocusState(bool on) { - if (show_focus_state == on) { - return; - } - show_focus_state = on; - if (focused_widget != nullptr) { - focused_widget->invalidate(); - } -} - -void TBWidget::setOpacity(float opacity) { - opacity = Clamp(opacity, 0.F, 1.F); - if (m_opacity == opacity) { - return; - } - if (opacity == 0) { // Invalidate after setting opacity 0 will do nothing. - invalidate(); - } - m_opacity = opacity; - invalidate(); -} - -void TBWidget::setVisibility(WIDGET_VISIBILITY vis) { - if (m_packed.visibility == vis) { - return; - } - - // Invalidate after making it invisible will do nothing. - if (vis != WIDGET_VISIBILITY_VISIBLE) { - invalidate(); - } - if (vis == WIDGET_VISIBILITY_GONE) { - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } - - WIDGET_VISIBILITY old_vis = getVisibility(); - m_packed.visibility = vis; - - invalidate(); - if (old_vis == WIDGET_VISIBILITY_GONE) { - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } - - onVisibilityChanged(); -} - -WIDGET_VISIBILITY TBWidget::getVisibility() const { - return static_cast(m_packed.visibility); -} - -bool TBWidget::getVisibilityCombined() const { - const TBWidget *tmp = this; - while (tmp != nullptr) { - if (tmp->getOpacity() == 0 || tmp->getVisibility() != WIDGET_VISIBILITY_VISIBLE) { - return false; - } - tmp = tmp->m_parent; - } - return true; -} - -bool TBWidget::getDisabled() const { - const TBWidget *tmp = this; - while (tmp != nullptr) { - if (tmp->getState(WIDGET_STATE_DISABLED)) { - return true; - } - tmp = tmp->m_parent; - } - return false; -} - -void TBWidget::addChild(TBWidget *child, WIDGET_Z z, WIDGET_INVOKE_INFO info) { - addChildRelative(child, z == WIDGET_Z_TOP ? WIDGET_Z_REL_AFTER : WIDGET_Z_REL_BEFORE, nullptr, info); -} - -void TBWidget::addChildRelative(TBWidget *child, WIDGET_Z_REL z, TBWidget *reference, WIDGET_INVOKE_INFO info) { - core_assert(!child->m_parent); - child->m_parent = this; - - if (reference != nullptr) { - if (z == WIDGET_Z_REL_BEFORE) { - m_children.addBefore(child, reference); - } else { - m_children.addAfter(child, reference); - } - } else // If there is no reference widget, before means first and after means last. - { - if (z == WIDGET_Z_REL_BEFORE) { - m_children.addFirst(child); - } else { - m_children.addLast(child); - } - } - - if (info == WIDGET_INVOKE_INFO_NORMAL) { - onChildAdded(child); - child->onAdded(); - TBWidgetListener::invokeWidgetAdded(this, child); - } - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - invalidate(); - invalidateSkinStates(); -} - -void TBWidget::removeChild(TBWidget *child, WIDGET_INVOKE_INFO info) { - core_assert(child->m_parent); - - if (info == WIDGET_INVOKE_INFO_NORMAL) { - // If we're not being deleted and delete the focused widget, try - // to keep the focus in this widget by moving it to the next widget. - if (!m_packed.is_dying && child == focused_widget) { - child->getEventDestination()->setFocusRecursive(); - } - - onChildRemove(child); - child->onRemove(); - TBWidgetListener::invokeWidgetRemove(this, child); - } - - m_children.remove(child); - child->m_parent = nullptr; - - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - invalidate(); - invalidateSkinStates(); -} - -void TBWidget::deleteAllChildren() { - while (TBWidget *child = getFirstChild()) { - removeChild(child); - delete child; - } -} - -void TBWidget::setZ(WIDGET_Z z) { - if (m_parent == nullptr) { - return; - } - if (z == WIDGET_Z_TOP && this == m_parent->m_children.getLast()) { - return; // Already at the top - } - if (z == WIDGET_Z_BOTTOM && this == m_parent->m_children.getFirst()) { - return; // Already at the top - } - TBWidget *parent = m_parent; - parent->removeChild(this, WIDGET_INVOKE_INFO_NO_CALLBACKS); - parent->addChild(this, z, WIDGET_INVOKE_INFO_NO_CALLBACKS); -} - -void TBWidget::setGravity(WIDGET_GRAVITY g) { - if (m_gravity == g) { - return; - } - m_gravity = g; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); -} - -void TBWidget::setSkinBg(const TBID &skinBg, WIDGET_INVOKE_INFO info) { - if (skinBg == m_skin_bg) { - return; - } - - // Set the skin and m_skin_bg_expected. During InvokeProcess, we will detect - // if any widget gets a different element due to conditions and strong override. - // If that happens, OnSkinChanged will be called and m_skin_bg_expected updated to - // match that override. - m_skin_bg = skinBg; - m_skin_bg_expected = skinBg; - - invalidate(); - invalidateSkinStates(); - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - - if (info == WIDGET_INVOKE_INFO_NORMAL) { - onSkinChanged(); - } -} - -TBSkinElement *TBWidget::getSkinBgElement() { - TBWidgetSkinConditionContext context(this); - WIDGET_STATE state = getAutoState(); - return g_tb_skin->getSkinElementStrongOverride(m_skin_bg, static_cast(state), context); -} - -TBWidget *TBWidget::findScrollableWidget(bool scrollX, bool scrollY) { - TBWidget *candidate = this; - while (candidate != nullptr) { - ScrollInfo scroll_info = candidate->getScrollInfo(); - if ((scrollX && scroll_info.canScrollX()) || (scrollY && scroll_info.canScrollY())) { - return candidate; - } - candidate = candidate->getParent(); - } - return nullptr; -} - -TBScroller *TBWidget::findStartedScroller() { - TBWidget *candidate = this; - while (candidate != nullptr) { - if ((candidate->m_scroller != nullptr) && candidate->m_scroller->isStarted()) { - return candidate->m_scroller; - } - candidate = candidate->getParent(); - } - return nullptr; -} - -TBScroller *TBWidget::getReadyScroller(bool scrollX, bool scrollY) { - if (TBScroller *scroller = findStartedScroller()) { - return scroller; - } - // We didn't have any active scroller, so create one for the nearest scrollable parent. - if (TBWidget *scrollable_widget = findScrollableWidget(scrollX, scrollY)) { - return scrollable_widget->getScroller(); - } - return nullptr; -} - -TBScroller *TBWidget::getScroller() { - if (m_scroller == nullptr) { - m_scroller = new TBScroller(this); - } - return m_scroller; -} - -void TBWidget::scrollToSmooth(int x, int y) { - ScrollInfo info = getScrollInfo(); - int dx = x - info.x; - int dy = y - info.y; - if (TBScroller *scroller = getReadyScroller(dx != 0, dy != 0)) { - scroller->onScrollBy(dx, dy, false); - } -} - -void TBWidget::scrollBySmooth(int dx, int dy) { - // Clip the values to the scroll limits, so we don't - // scroll any parents. - // int x = Clamp(info.x + dx, info.min_x, info.max_x); - // int y = Clamp(info.y + dy, info.min_y, info.max_y); - // dx = x - info.x; - // dy = y - info.y; - if ((dx == 0) && (dy == 0)) { - return; - } - - if (TBScroller *scroller = getReadyScroller(dx != 0, dy != 0)) { - scroller->onScrollBy(dx, dy, true); - } -} - -void TBWidget::scrollBy(int dx, int dy) { - ScrollInfo info = getScrollInfo(); - scrollTo(info.x + dx, info.y + dy); -} - -void TBWidget::scrollByRecursive(int &dx, int &dy) { - TBWidget *tmp = this; - while (tmp != nullptr) { - ScrollInfo old_info = tmp->getScrollInfo(); - tmp->scrollTo(old_info.x + dx, old_info.y + dy); - ScrollInfo new_info = tmp->getScrollInfo(); - dx -= new_info.x - old_info.x; - dy -= new_info.y - old_info.y; - if ((dx == 0) && (dy == 0)) { - break; - } - tmp = tmp->m_parent; - } -} - -void TBWidget::scrollIntoViewRecursive() { - TBRect scroll_to_rect = m_rect; - TBWidget *tmp = this; - while (tmp->m_parent != nullptr) { - tmp->m_parent->scrollIntoView(scroll_to_rect); - scroll_to_rect.x += tmp->m_parent->m_rect.x; - scroll_to_rect.y += tmp->m_parent->m_rect.y; - tmp = tmp->m_parent; - } -} - -void TBWidget::scrollIntoView(const TBRect &rect) { - const ScrollInfo info = getScrollInfo(); - int new_x = info.x; - int new_y = info.y; - - const TBRect visible_rect = getPaddingRect().offset(info.x, info.y); - - if (rect.y <= visible_rect.y) { - new_y = rect.y; - } else if (rect.y + rect.h > visible_rect.y + visible_rect.h) { - new_y = rect.y + rect.h - visible_rect.h; - } - - if (rect.x <= visible_rect.x) { - new_x = rect.x; - } else if (rect.x + rect.w > visible_rect.x + visible_rect.w) { - new_x = rect.x + rect.w - visible_rect.w; - } - - scrollTo(new_x, new_y); -} - -bool TBWidget::setFocus(WIDGET_FOCUS_REASON reason, WIDGET_INVOKE_INFO info) { - if (focused_widget == this) { - return true; - } - if (getDisabled() || !getIsFocusable() || !getVisibilityCombined() || getIsDying()) { - return false; - } - - // Update windows last focus - TBWindow *window = getParentWindow(); - if (window != nullptr) { - window->setLastFocus(this); - // If not active, just return. We should get focus when the window is activated. - // Exception for windows that doesn't activate. They may contain focusable widgets. - if (!window->isActive() && ((window->getSettings() & WINDOW_SETTINGS_CAN_ACTIVATE) != 0U)) { - return true; - } - } - - if (focused_widget != nullptr) { - focused_widget->invalidate(); - focused_widget->invalidateSkinStates(); - } - - TBWidgetSafePointer old_focus(focused_widget); - focused_widget = this; - - invalidate(); - invalidateSkinStates(); - - if (reason == WIDGET_FOCUS_REASON_NAVIGATION) { - scrollIntoViewRecursive(); - } - - if (info == WIDGET_INVOKE_INFO_NORMAL) { - // A lot of weird bugs could happen if people mess with focus from OnFocusChanged. - // Take some precaution and detect if it change again after OnFocusChanged(false). - if (TBWidget *old = old_focus.get()) { - // The currently focused widget still has the pressed state set by the emulated click - // (By keyboard), so unset it before we unfocus it so it's not stuck in pressed state. - if (old->m_packed.has_key_pressed_state) { - old->setState(WIDGET_STATE_PRESSED, false); - old->m_packed.has_key_pressed_state = false; - } - old->onFocusChanged(false); - } - if (old_focus.get() != nullptr) { - TBWidgetListener::invokeWidgetFocusChanged(old_focus.get(), false); - } - if ((focused_widget != nullptr) && focused_widget == this) { - focused_widget->onFocusChanged(true); - } - if ((focused_widget != nullptr) && focused_widget == this) { - TBWidgetListener::invokeWidgetFocusChanged(focused_widget, true); - } - } - return true; -} - -bool TBWidget::setFocusRecursive(WIDGET_FOCUS_REASON reason) { - // Search for a child widget that accepts focus - TBWidget *child = getFirstChild(); - while (child != nullptr) { - if (child->setFocus(WIDGET_FOCUS_REASON_UNKNOWN)) { - return true; - } - child = child->getNextDeep(this); - } - return false; -} - -bool TBWidget::moveFocus(bool forward) { - TBWidget *origin = focused_widget; - if (origin == nullptr) { - origin = this; - } - - TBWidget *root = origin->getParentWindow(); - if (root == nullptr) { - root = origin->getParentRoot(); - } - - TBWidget *current = origin; - while (current != nullptr) { - current = forward ? current->getNextDeep(root) : current->getPrevDeep(); - // Wrap around if we reach the end/beginning - if ((current == nullptr) || !root->isAncestorOf(current)) { - current = forward ? root : root->getLastLeaf(); - } - // Break if we reached the origin again (we're not finding anything else) - if (current == origin) { - break; - } - // Try to focus what we found - if ((current != nullptr) && current->setFocus(WIDGET_FOCUS_REASON_NAVIGATION)) { - return true; - } - } - return false; -} - -TBWidget *TBWidget::getNextDeep(const TBWidget *boundingAncestor) const { - if (m_children.getFirst() != nullptr) { - return getFirstChild(); - } - for (const TBWidget *widget = this; widget != boundingAncestor; widget = widget->m_parent) { - if (widget->next != nullptr) { - return widget->getNext(); - } - } - return nullptr; -} - -TBWidget *TBWidget::getPrevDeep() const { - if (prev == nullptr) { - return m_parent; - } - TBWidget *widget = getPrev(); - while (widget->m_children.getLast() != nullptr) { - widget = widget->getLastChild(); - } - return widget; -} - -TBWidget *TBWidget::getLastLeaf() const { - if (TBWidget *widget = getLastChild()) { - while (widget->getLastChild() != nullptr) { - widget = widget->getLastChild(); - } - return widget; - } - return nullptr; -} - -bool TBWidget::getIsInteractable() const { - return !(m_opacity == 0 || getIgnoreInput() || getState(WIDGET_STATE_DISABLED) || getIsDying() || - getVisibility() != WIDGET_VISIBILITY_VISIBLE); -} - -WIDGET_HIT_STATUS TBWidget::getHitStatus(int x, int y) { - if (!getIsInteractable()) { - return WIDGET_HIT_STATUS_NO_HIT; - } - if (x < 0 || y < 0) { - return WIDGET_HIT_STATUS_NO_HIT; - } - if (x >= m_rect.w || y >= m_rect.h) { - return WIDGET_HIT_STATUS_NO_HIT; - } - return WIDGET_HIT_STATUS_HIT; -} - -TBWidget *TBWidget::getWidgetAt(int x, int y, bool includeChildren) const { - int child_translation_x; - int child_translation_y; - getChildTranslation(child_translation_x, child_translation_y); - x -= child_translation_x; - y -= child_translation_y; - - TBWidget *tmp = getFirstChild(); - TBWidget *last_match = nullptr; - while (tmp != nullptr) { - WIDGET_HIT_STATUS hit_status = tmp->getHitStatus(x - tmp->m_rect.x, y - tmp->m_rect.y); - if (hit_status != 0U) { - if (includeChildren && hit_status != WIDGET_HIT_STATUS_HIT_NO_CHILDREN) { - last_match = tmp->getWidgetAt(x - tmp->m_rect.x, y - tmp->m_rect.y, includeChildren); - if (last_match == nullptr) { - last_match = tmp; - } - } else { - last_match = tmp; - } - } - tmp = tmp->getNext(); - } - return last_match; -} - -TBWidget *TBWidget::getChildFromIndex(int index) const { - int i = 0; - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (i++ == index) { - return child; - } - } - return nullptr; -} - -int TBWidget::getIndexFromChild(TBWidget *child) const { - core_assert(child->getParent() == this); - int i = 0; - for (TBWidget *tmp = getFirstChild(); tmp != nullptr; tmp = tmp->getNext(), i++) { - if (tmp == child) { - return i; - } - } - return -1; ///< Should not happen! -} - -bool TBWidget::isAncestorOf(TBWidget *otherWidget) const { - while (otherWidget != nullptr) { - if (otherWidget == this) { - return true; - } - otherWidget = otherWidget->m_parent; - } - return false; -} - -bool TBWidget::isEventDestinationFor(TBWidget *otherWidget) const { - while (otherWidget != nullptr) { - if (otherWidget == this) { - return true; - } - otherWidget = otherWidget->getEventDestination(); - } - return false; -} - -TBWidget *TBWidget::getParentRoot() { - TBWidget *tmp = this; - while (tmp->m_parent != nullptr) { - tmp = tmp->m_parent; - } - return tmp; -} - -TBWindow *TBWidget::getParentWindow() { - TBWidget *tmp = this; - while ((tmp != nullptr) && !tmp->isOfType()) { - tmp = tmp->m_parent; - } - return static_cast(tmp); -} - -void TBWidget::addListener(TBWidgetListener *listener) { - m_listeners.addLast(listener); -} - -void TBWidget::removeListener(TBWidgetListener *listener) { - m_listeners.remove(listener); -} - -bool TBWidget::hasListener(TBWidgetListener *listener) const { - return m_listeners.containsLink(listener); -} - -void TBWidget::onPaintChildren(const PaintProps &paintProps) { - if (m_children.getFirst() == nullptr) { - return; - } - - // Translate renderer with child translation - int child_translation_x; - int child_translation_y; - getChildTranslation(child_translation_x, child_translation_y); - g_renderer->translate(child_translation_x, child_translation_y); - - TBRect clip_rect = g_renderer->getClipRect(); - - // Invoke paint on all children that are in the current visible rect. - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (clip_rect.intersects(child->m_rect)) { - child->invokePaint(paintProps); - } - } - - // Invoke paint of overlay elements on all children that are in the current visible rect. - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (clip_rect.intersects(child->m_rect) && child->getVisibility() == WIDGET_VISIBILITY_VISIBLE) { - TBSkinElement *skin_element = child->getSkinBgElement(); - if ((skin_element != nullptr) && skin_element->hasOverlayElements()) { - // Update the renderer with the widgets opacity - WIDGET_STATE state = child->getAutoState(); - float old_opacity = g_renderer->getOpacity(); - float opacity = old_opacity * child->calculateOpacityInternal(state, skin_element); - if (opacity > 0) { - g_renderer->setOpacity(opacity); - - TBWidgetSkinConditionContext context(child); - g_tb_skin->paintSkinOverlay(child->m_rect, skin_element, static_cast(state), context); - - g_renderer->setOpacity(old_opacity); - } - } - } - } - - // Draw generic focus skin if the focused widget is one of the children, and the skin - // doesn't have a skin state for focus which would already be painted. - if ((focused_widget != nullptr) && focused_widget->m_parent == this) { - TBWidgetSkinConditionContext context(focused_widget); - TBSkinElement *skin_element = focused_widget->getSkinBgElement(); - if ((skin_element == nullptr) || !skin_element->hasState(SKIN_STATE_FOCUSED, context)) { - WIDGET_STATE state = focused_widget->getAutoState(); - if ((state & SKIN_STATE_FOCUSED) != 0) { - g_tb_skin->paintSkin(focused_widget->m_rect, TBIDC("generic_focus"), static_cast(state), - context); - } - } - } - - g_renderer->translate(-child_translation_x, -child_translation_y); -} - -void TBWidget::onResized(int oldW, int oldH) { - int dw = m_rect.w - oldW; - int dh = m_rect.h - oldH; - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (child->getVisibility() == WIDGET_VISIBILITY_GONE) { - continue; - } - TBRect rect = child->m_rect; - if (((child->m_gravity & WIDGET_GRAVITY_LEFT) != 0U) && ((child->m_gravity & WIDGET_GRAVITY_RIGHT) != 0U)) { - rect.w += dw; - } else if ((child->m_gravity & WIDGET_GRAVITY_RIGHT) != 0U) { - rect.x += dw; - } - if (((child->m_gravity & WIDGET_GRAVITY_TOP) != 0U) && ((child->m_gravity & WIDGET_GRAVITY_BOTTOM) != 0U)) { - rect.h += dh; - } else if ((child->m_gravity & WIDGET_GRAVITY_BOTTOM) != 0U) { - rect.y += dh; - } - child->setRect(rect); - } -} - -void TBWidget::onInflateChild(TBWidget *child) { - if (child->getVisibility() == WIDGET_VISIBILITY_GONE) { - return; - } - - // If the child pull towards only one edge (per axis), stick to that edge - // and use the preferred size. Otherwise fill up all available space. - TBRect padding_rect = getPaddingRect(); - TBRect child_rect = padding_rect; - WIDGET_GRAVITY gravity = child->getGravity(); - bool fill_x = ((gravity & WIDGET_GRAVITY_LEFT) != 0U) && ((gravity & WIDGET_GRAVITY_RIGHT) != 0U); - bool fill_y = ((gravity & WIDGET_GRAVITY_TOP) != 0U) && ((gravity & WIDGET_GRAVITY_BOTTOM) != 0U); - if (!fill_x || !fill_y) { - PreferredSize ps = child->getPreferredSize(); - if (!fill_x) { - child_rect.w = ps.pref_w; - if ((gravity & WIDGET_GRAVITY_RIGHT) != 0U) { - child_rect.x = padding_rect.x + padding_rect.w - child_rect.w; - } - } - if (!fill_y) { - child_rect.h = ps.pref_h; - if ((gravity & WIDGET_GRAVITY_BOTTOM) != 0U) { - child_rect.y = padding_rect.y + padding_rect.h - child_rect.h; - } - } - } - child->setRect(child_rect); -} - -TBRect TBWidget::getPaddingRect() { - TBRect padding_rect(0, 0, m_rect.w, m_rect.h); - if (TBSkinElement *e = getSkinBgElement()) { - padding_rect.x += e->padding_left; - padding_rect.y += e->padding_top; - padding_rect.w -= e->padding_left + e->padding_right; - padding_rect.h -= e->padding_top + e->padding_bottom; - } - return padding_rect; -} - -PreferredSize TBWidget::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - // The default preferred size is calculated to satisfy the children - // in the best way. Since this is the default, it's probably not a - // layout widget and children are resized purely by gravity. - - // Allow this widget a larger maximum if our gravity wants both ways, - // otherwise don't grow more than the largest child. - bool apply_max_w = !(((m_gravity & WIDGET_GRAVITY_LEFT) != 0U) && ((m_gravity & WIDGET_GRAVITY_RIGHT) != 0U)); - bool apply_max_h = !(((m_gravity & WIDGET_GRAVITY_TOP) != 0U) && ((m_gravity & WIDGET_GRAVITY_BOTTOM) != 0U)); - bool has_layouting_children = false; - PreferredSize ps; - - TBSkinElement *bg_skin = getSkinBgElement(); - int horizontal_padding = bg_skin != nullptr ? bg_skin->padding_left + bg_skin->padding_right : 0; - int vertical_padding = bg_skin != nullptr ? bg_skin->padding_top + bg_skin->padding_bottom : 0; - SizeConstraints inner_sc = constraints.constrainByPadding(horizontal_padding, vertical_padding); - - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (child->getVisibility() == WIDGET_VISIBILITY_GONE) { - continue; - } - if (!has_layouting_children) { - has_layouting_children = true; - if (apply_max_w) { - ps.max_w = 0; - } - if (apply_max_h) { - ps.max_h = 0; - } - } - PreferredSize child_ps = child->getPreferredSize(inner_sc); - ps.pref_w = Max(ps.pref_w, child_ps.pref_w); - ps.pref_h = Max(ps.pref_h, child_ps.pref_h); - ps.min_w = Max(ps.min_w, child_ps.min_w); - ps.min_h = Max(ps.min_h, child_ps.min_h); - if (apply_max_w) { - ps.max_w = Max(ps.max_w, child_ps.max_w); - } - if (apply_max_h) { - ps.max_h = Max(ps.max_h, child_ps.max_h); - } - ps.size_dependency |= child_ps.size_dependency; - } - - return ps; -} - -PreferredSize TBWidget::onCalculatePreferredSize(const SizeConstraints &constraints) { - PreferredSize ps = onCalculatePreferredContentSize(constraints); - core_assert(ps.pref_w >= ps.min_w); - core_assert(ps.pref_h >= ps.min_h); - - if (TBSkinElement *e = getSkinBgElement()) { - // Override the widgets preferences with skin attributes that has been specified. - // If not set by the widget, calculate based on the intrinsic size of the skin. - - const int skin_intrinsic_w = e->getIntrinsicWidth(); - if (e->getPrefWidth() != SKIN_VALUE_NOT_SPECIFIED) { - ps.pref_w = e->getPrefWidth(); - } else if (ps.pref_w == 0 && skin_intrinsic_w != SKIN_VALUE_NOT_SPECIFIED) { - ps.pref_w = skin_intrinsic_w; - } else { - // Grow by padding to get the preferred size from preferred content size. - ps.min_w += e->padding_left + e->padding_right; - ps.pref_w += e->padding_left + e->padding_right; - } - - const int skin_intrinsic_h = e->getIntrinsicHeight(); - if (e->getPrefHeight() != SKIN_VALUE_NOT_SPECIFIED) { - ps.pref_h = e->getPrefHeight(); - } else if (ps.pref_h == 0 && skin_intrinsic_h != SKIN_VALUE_NOT_SPECIFIED) { - ps.pref_h = skin_intrinsic_h; - } else { - // Grow by padding to get the preferred size from preferred content size. - ps.min_h += e->padding_top + e->padding_bottom; - ps.pref_h += e->padding_top + e->padding_bottom; - } - - if (e->getMinWidth() != SKIN_VALUE_NOT_SPECIFIED) { - ps.min_w = e->getMinWidth(); - } else { - ps.min_w = Max(ps.min_w, e->getIntrinsicMinWidth()); - } - - if (e->getMinHeight() != SKIN_VALUE_NOT_SPECIFIED) { - ps.min_h = e->getMinHeight(); - } else { - ps.min_h = Max(ps.min_h, e->getIntrinsicMinHeight()); - } - - if (e->getMaxWidth() != SKIN_VALUE_NOT_SPECIFIED) { - ps.max_w = e->getMaxWidth(); - } else { - ps.max_w += e->padding_left + e->padding_right; - } - - if (e->getMaxHeight() != SKIN_VALUE_NOT_SPECIFIED) { - ps.max_h = e->getMaxHeight(); - } else { - ps.max_h += e->padding_top + e->padding_bottom; - } - - // Sanitize result - ps.pref_w = Max(ps.pref_w, ps.min_w); - ps.pref_h = Max(ps.pref_h, ps.min_h); - } - return ps; -} - -PreferredSize TBWidget::getPreferredSize(const SizeConstraints &inConstraints) { - SizeConstraints constraints(inConstraints); - if (m_layout_params != nullptr) { - constraints = constraints.constrainByLayoutParams(*m_layout_params); - } - - // Returned cached result if valid and the constraints are the same. - if (m_packed.is_cached_ps_valid) { - if (m_cached_sc == constraints || - m_cached_ps.size_dependency == SIZE_DEP_NONE /*|| - // FIX: These optimizations would probably be good. Keeping - // disabled for now because it needs testing. - // If *only* width depend on height, only the height matter - (m_cached_ps.size_dependency == SIZE_DEP_WIDTH_DEPEND_ON_HEIGHT && - m_cached_sc.available_h == constraints.available_h) || - // If *only* height depend on width, only the width matter - (m_cached_ps.size_dependency == SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH && - m_cached_sc.available_w == constraints.available_w)*/) - { - return m_cached_ps; - } - } - - // Measure and save to cache - TB_IF_DEBUG_SETTING(LAYOUT_PS_DEBUGGING, last_measure_time = TBSystem::getTimeMS()); - m_packed.is_cached_ps_valid = 1; - m_cached_ps = onCalculatePreferredSize(constraints); - m_cached_sc = constraints; - - // Override the calculated ps with any specified layout parameter. - if (m_layout_params != nullptr) { -#define LP_OVERRIDE(param) \ - if (m_layout_params->param != LayoutParams::UNSPECIFIED) \ - m_cached_ps.param = m_layout_params->param; - LP_OVERRIDE(min_w); - LP_OVERRIDE(min_h); - LP_OVERRIDE(max_w); - LP_OVERRIDE(max_h); - LP_OVERRIDE(pref_w); - LP_OVERRIDE(pref_h); - - // Sanitize results - m_cached_ps.max_w = Max(m_cached_ps.max_w, m_cached_ps.min_w); - m_cached_ps.max_h = Max(m_cached_ps.max_h, m_cached_ps.min_h); - m_cached_ps.pref_w = Max(m_cached_ps.pref_w, m_cached_ps.min_w); - m_cached_ps.pref_h = Max(m_cached_ps.pref_h, m_cached_ps.min_h); - } - return m_cached_ps; -} - -void TBWidget::setLayoutParams(const LayoutParams &lp) { - if (m_layout_params == nullptr) { - m_layout_params = new LayoutParams; - } - if (m_layout_params == nullptr) { - return; - } - *m_layout_params = lp; - m_packed.is_cached_ps_valid = 0; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); -} - -void TBWidget::invalidateLayout(INVALIDATE_LAYOUT il) { - m_packed.is_cached_ps_valid = 0; - if (getVisibility() == WIDGET_VISIBILITY_GONE) { - return; - } - invalidate(); - if (il == INVALIDATE_LAYOUT_RECURSIVE && (m_parent != nullptr)) { - m_parent->invalidateLayout(il); - } -} - -void TBWidget::invokeProcess() { - invokeSkinUpdatesInternal(false); - invokeProcessInternal(); -} - -void TBWidget::invokeSkinUpdatesInternal(bool forceUpdate) { - if (!update_skin_states && !forceUpdate) { - return; - } - update_skin_states = false; - - // Check if the skin we get is different from what we expect. That might happen - // if the skin has some strong override dependant a condition that has changed. - // If that happens, call OnSkinChanged so the widget can react to that, and - // invalidate layout to apply new skin properties. - if (TBSkinElement *skin_elm = getSkinBgElement()) { - if (skin_elm->id != m_skin_bg_expected) { - onSkinChanged(); - m_skin_bg_expected = skin_elm->id; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - } - } - - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - child->invokeSkinUpdatesInternal(true); - } -} - -void TBWidget::invokeProcessInternal() { - onProcess(); - - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - child->invokeProcessInternal(); - } - - onProcessAfterChildren(); -} - -void TBWidget::invokeProcessStates(bool forceUpdate) { - if (!update_widget_states && !forceUpdate) { - return; - } - update_widget_states = false; - - onProcessStates(); - - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - child->invokeProcessStates(true); - } -} - -float TBWidget::calculateOpacityInternal(WIDGET_STATE state, TBSkinElement *skinElement) const { - float opacity = m_opacity; - if (skinElement != nullptr) { - opacity *= skinElement->opacity; - } - if ((state & WIDGET_STATE_DISABLED) != 0U) { - opacity *= g_tb_skin->getDefaultDisabledOpacity(); - } - return Clamp(opacity, 0.F, 1.F); -} - -void TBWidget::invokePaint(const PaintProps &parentPaintProps) { - // Don't paint invisible widgets - if (m_opacity == 0 || m_rect.isEmpty() || getVisibility() != WIDGET_VISIBILITY_VISIBLE) { - return; - } - - WIDGET_STATE state = getAutoState(); - TBSkinElement *skin_element = getSkinBgElement(); - - // Multiply current opacity with widget opacity, skin opacity and state opacity. - float old_opacity = g_renderer->getOpacity(); - float opacity = old_opacity * calculateOpacityInternal(state, skin_element); - if (opacity == 0) { - return; - } - - // FIX: This does not give the correct result! Must use a new render target! - g_renderer->setOpacity(opacity); - - int trns_x = m_rect.x; - int trns_y = m_rect.y; - g_renderer->translate(trns_x, trns_y); - - // Paint background skin - TBRect local_rect(0, 0, m_rect.w, m_rect.h); - TBWidgetSkinConditionContext context(this); - TBSkinElement *used_element = - g_tb_skin->paintSkin(local_rect, skin_element, static_cast(state), context); - core_assert(!!used_element == !!skin_element); - - TB_IF_DEBUG_SETTING(LAYOUT_BOUNDS, g_tb_skin->paintRect(local_rect, TBColor(255, 255, 255, 50), 1)); - - // Inherit properties from parent if not specified in the used skin for this widget. - PaintProps paint_props = parentPaintProps; - if ((used_element != nullptr) && used_element->text_color != 0) { - paint_props.text_color = used_element->text_color; - } - - // Paint content - onPaint(paint_props); - - if (used_element != nullptr) { - g_renderer->translate(used_element->content_ofs_x, used_element->content_ofs_y); - } - - // Paint children - onPaintChildren(paint_props); - -#ifdef TB_RUNTIME_DEBUG_INFO - if (TB_DEBUG_SETTING(LAYOUT_PS_DEBUGGING)) { - // Layout debug painting. Paint recently layouted widgets with red and - // recently measured widgets with yellow. - // Invalidate to keep repainting until we've timed out (so it's removed). - const double debug_time = 300; - const double now = TBSystem::getTimeMS(); - if (now < last_layout_time + debug_time) { - g_tb_skin->paintRect(local_rect, TBColor(255, 30, 30, 200), 1); - invalidate(); - } - if (now < last_measure_time + debug_time) { - g_tb_skin->paintRect(local_rect.shrink(1, 1), TBColor(255, 255, 30, 200), 1); - invalidate(); - } - } -#endif // TB_RUNTIME_DEBUG_INFO - - if (used_element != nullptr) { - g_renderer->translate(-used_element->content_ofs_x, -used_element->content_ofs_y); - } - - g_renderer->translate(-trns_x, -trns_y); - g_renderer->setOpacity(old_opacity); -} - -bool TBWidget::invokeEvent(TBWidgetEvent &ev) { - ev.target = this; - - // First call the global listener about this event. - // Who knows, maybe some listener will block the event or cause us - // to be deleted. - TBWidgetSafePointer this_widget(this); - if (TBWidgetListener::invokeWidgetInvokeEvent(this, ev)) { - return true; - } - - if (this_widget.get() == nullptr) { - return true; // We got removed so we actually handled this event. - } - - if (ev.type == EVENT_TYPE_CHANGED) { - invalidateSkinStates(); - m_connection.syncFromWidget(this); - } - - if (this_widget.get() == nullptr) { - return true; // We got removed so we actually handled this event. - } - - // Always update states after some event types. - switch (ev.type) { - case EVENT_TYPE_CLICK: - case EVENT_TYPE_LONG_CLICK: - case EVENT_TYPE_CHANGED: - case EVENT_TYPE_KEY_DOWN: - case EVENT_TYPE_KEY_UP: - invalidateStates(); - break; - default: - break; - } - - // Call onEvent on this widgets and travel up through its parents if not handled. - bool handled = false; - TBWidget *tmp = this; - while ((tmp != nullptr) && !(handled = tmp->onEvent(ev))) { - tmp = tmp->getEventDestination(); - } - return handled; -} - -void TBWidget::execute(const char* msg, ...) { - va_list args; - va_start(args, msg); - char buf[4096]; - SDL_vsnprintf(buf, sizeof(buf), msg, args); - buf[sizeof(buf) - 1] = '\0'; - - tb::TBWidgetEvent ev(tb::EVENT_TYPE_COMMAND); - ev.string = buf; - invokeEvent(ev); - - command::executeCommands(core::String(buf)); - va_end(args); -} - -void TBWidget::startLongClickTimer(BUTTON_TYPE type) { - stopLongClickTimer(); - m_long_click_timer = new TBLongClickTimer(this, type); -} - -void TBWidget::stopLongClickTimer() { - if (m_long_click_timer == nullptr) { - return; - } - delete m_long_click_timer; - m_long_click_timer = nullptr; -} - -bool TBWidget::invokePointerDown(int x, int y, int clickCount, MODIFIER_KEYS modifierkeys, BUTTON_TYPE type) { - if (captured_widget == nullptr) { - setCapturedWidget(getWidgetAt(x, y, true)); - setHoveredWidget(captured_widget, type != 0U); - // captured_button = button; - - // Hide focus when we use the pointer, if it's not on the focused widget. - if (focused_widget != captured_widget) { - setAutoFocusState(false); - } - - // Start long click timer. Only for touch events for now. - if (type == TB_TOUCH && (captured_widget != nullptr) && captured_widget->getWantLongClick()) { - captured_widget->startLongClickTimer(type); - } - - // Get the closest parent window and bring it to the top - TBWindow *window = captured_widget != nullptr ? captured_widget->getParentWindow() : nullptr; - if (window != nullptr) { - window->activate(); - } - } - if (captured_widget != nullptr) { - // Check if there's any started scroller that should be stopped. - TBWidget *tmp = captured_widget; - while (tmp != nullptr) { - if ((tmp->m_scroller != nullptr) && tmp->m_scroller->isStarted()) { - // When we touch down to stop a scroller, we don't - // want the touch to end up causing a click. - cancel_click = true; - tmp->m_scroller->stop(); - break; - } - tmp = tmp->getParent(); - } - - // Focus the captured widget or the closest - // focusable parent if it isn't focusable. - TBWidget *focus_target = captured_widget; - while (focus_target != nullptr) { - if (focus_target->setFocus(WIDGET_FOCUS_REASON_POINTER)) { - break; - } - focus_target = focus_target->m_parent; - } - } - if (captured_widget != nullptr) { - captured_widget->convertFromRoot(x, y); - pointer_move_widget_x = pointer_down_widget_x = x; - pointer_move_widget_y = pointer_down_widget_y = y; - TBWidgetEvent ev(EVENT_TYPE_POINTER_DOWN, x, y, type, modifierkeys); - ev.count = clickCount; - captured_widget->invokeEvent(ev); - - // Return true when captured instead of InvokeEvent result. If a widget is - // hit is more interesting for callers than if the event was handled or not. - return true; - } - return false; -} - -bool TBWidget::invokePointerUp(int x, int y, MODIFIER_KEYS modifierkeys, BUTTON_TYPE type) { - if (captured_widget != nullptr) { - captured_widget->convertFromRoot(x, y); - TBWidgetEvent ev_up(EVENT_TYPE_POINTER_UP, x, y, type, modifierkeys); - captured_widget->invokeEvent(ev_up); - if (captured_widget != nullptr) { - if (!cancel_click) { - if (captured_widget->getHitStatus(x, y) != WIDGET_HIT_STATUS_NO_HIT) { - TBWidgetEvent ev_click(EVENT_TYPE_CLICK, x, y, type, modifierkeys); - captured_widget->invokeEvent(ev_click); - } - } - if (captured_widget != nullptr) { // && button == captured_button - captured_widget->releaseCapture(); - } - } - - // Return true when captured instead of invokeEvent result. If a widget is - // hit is more interesting for callers than if the event was handled or not. - return true; - } - return false; -} - -void TBWidget::maybeInvokeLongClickOrContextMenu(BUTTON_TYPE type) { - stopLongClickTimer(); - if (captured_widget == this && !cancel_click && - (captured_widget->getHitStatus(pointer_move_widget_x, pointer_move_widget_y) != 0U)) { - // Invoke long click - TBWidgetEvent ev_long_click(EVENT_TYPE_LONG_CLICK, pointer_move_widget_x, pointer_move_widget_y, type, - TB_MODIFIER_NONE); - bool handled = captured_widget->invokeEvent(ev_long_click); - if (!handled) { - // Long click not handled so invoke a context menu event instead - TBWidgetEvent ev_context_menu(EVENT_TYPE_CONTEXT_MENU, pointer_move_widget_x, pointer_move_widget_y, type, - TB_MODIFIER_NONE); - handled = captured_widget->invokeEvent(ev_context_menu); - } - // If any event was handled, suppress click when releasing pointer. - if (handled) { - cancel_click = true; - } - } -} - -void TBWidget::invokePointerMove(int x, int y, MODIFIER_KEYS modifierkeys, BUTTON_TYPE type) { - const bool touch = type == TB_TOUCH; - setHoveredWidget(getWidgetAt(x, y, true), touch); - - TBWidget *target = captured_widget != nullptr ? captured_widget : hovered_widget; - if (target != nullptr) { - target->convertFromRoot(x, y); - pointer_move_widget_x = x; - pointer_move_widget_y = y; - - TBWidgetEvent ev(EVENT_TYPE_POINTER_MOVE, x, y, type, modifierkeys); - - if (target->invokeEvent(ev)) { - return; - } - - // The move event was not handled, so handle panning of scrollable widgets. - handlePanningOnMove(x, y); - } -} - -void TBWidget::handlePanningOnMove(int x, int y) { - if (captured_widget == nullptr) { - return; - } - - // Check pointer movement - const int dx = pointer_down_widget_x - x; - const int dy = pointer_down_widget_y - y; - const int threshold = TBSystem::getPanThreshold(); - const bool maybe_start_panning_x = Abs(dx) >= threshold; - const bool maybe_start_panning_y = Abs(dy) >= threshold; - - // Do panning, or attempt starting panning (we don't know if any widget is scrollable yet) - if (captured_widget->m_packed.is_panning || maybe_start_panning_x || maybe_start_panning_y) { - // The threshold is met for not invoking any long click - captured_widget->stopLongClickTimer(); - - int start_compensation_x = 0; - int start_compensation_y = 0; - if (!captured_widget->m_packed.is_panning) { - // When we start panning, deduct the extra distance caused by the - // start threshold from the delta so we don't start with a sudden jump. - int extra = threshold - 1; - if (maybe_start_panning_x) { - start_compensation_x = dx < 0 ? extra : -extra; - } - if (maybe_start_panning_y) { - start_compensation_y = dy < 0 ? extra : -extra; - } - } - - // Get any active scroller and feed it with pan actions. - TBScroller *scroller = captured_widget->getReadyScroller(dx != 0, dy != 0); - if (scroller == nullptr) { - return; - } - - int old_translation_x = 0; - int old_translation_y = 0; - captured_widget->getScrollRoot()->getChildTranslation(old_translation_x, old_translation_y); - - if (scroller->onPan(dx + start_compensation_x, dy + start_compensation_y)) { - // Scroll delta changed, so we are now panning! - captured_widget->m_packed.is_panning = true; - cancel_click = true; - - // If the captured widget (or its scroll root) has panned, we have to compensate the - // pointer down coordinates so we won't accumulate the difference the following pan. - int new_translation_x = 0; - int new_translation_y = 0; - captured_widget->getScrollRoot()->getChildTranslation(new_translation_x, new_translation_y); - pointer_down_widget_x += new_translation_x - old_translation_x + start_compensation_x; - pointer_down_widget_y += new_translation_y - old_translation_y + start_compensation_y; - } - } -} - -void TBWidget::invokePointerCancel() { - if (captured_widget != nullptr) { - captured_widget->releaseCapture(); - } -} - -bool TBWidget::invokeTouchDown(int x, int y, uint32_t id, int clickCount, MODIFIER_KEYS modifierkeys) { - if (id == 0) { - return invokePointerDown(x, y, clickCount, modifierkeys, TB_TOUCH); - } - - TOUCH_INFO *ti = newTouchInfo(id); - if (ti == nullptr) { - return false; - } - - if (ti->captured_widget == nullptr) { - ti->captured_widget = getWidgetAt(x, y, true); - } - if ((ti->captured_widget != nullptr) && !ti->captured_widget->getState(WIDGET_STATE_DISABLED)) { - ti->hovered_widget = ti->captured_widget; - } - - if (ti->captured_widget != nullptr) { - ti->captured_widget->convertFromRoot(x, y); - ti->move_widget_x = ti->down_widget_x = x; - ti->move_widget_y = ti->down_widget_y = y; - TBWidgetEvent ev(EVENT_TYPE_TOUCH_DOWN, x, y, TB_TOUCH, modifierkeys); - ev.count = clickCount; - ev.ref_id = id; - ti->captured_widget->invokeEvent(ev); - return true; - } - return false; -} - -bool TBWidget::invokeTouchUp(int x, int y, uint32_t id, MODIFIER_KEYS modifierkeys) { - if (id == 0) { - return invokePointerUp(x, y, modifierkeys, TB_TOUCH); - } - TOUCH_INFO *ti = getTouchInfo(id); - if ((ti != nullptr) && (ti->captured_widget != nullptr)) { - ti->captured_widget->convertFromRoot(x, y); - TBWidgetEvent ev(EVENT_TYPE_TOUCH_UP, x, y, TB_TOUCH, modifierkeys); - ev.ref_id = id; - ti->captured_widget->invokeEvent(ev); - DeleteTouchInfo(id); - return true; - } - return false; -} - -void TBWidget::invokeTouchMove(int x, int y, uint32_t id, MODIFIER_KEYS modifierkeys) { - if (id == 0) { - return invokePointerMove(x, y, modifierkeys, TB_TOUCH); - } - - TOUCH_INFO *ti = getTouchInfo(id); - if (ti == nullptr) { - return; - } - - ti->hovered_widget = getWidgetAt(x, y, true); - if (ti->captured_widget != nullptr) { - ti->captured_widget->convertFromRoot(x, y); - ti->move_widget_x = x; - ti->move_widget_y = y; - TBWidgetEvent ev(EVENT_TYPE_TOUCH_MOVE, x, y, TB_TOUCH, modifierkeys); - ev.ref_id = id; - if (ti->captured_widget->invokeEvent(ev)) { - return; - } - } -} - -void TBWidget::invokeTouchCancel(uint32_t id) { - if (id == 0) { - return invokePointerCancel(); - } - - TOUCH_INFO *ti = getTouchInfo(id); - if (ti != nullptr) { - if (ti->captured_widget != nullptr) { - TBWidgetEvent ev(EVENT_TYPE_TOUCH_CANCEL, 0, 0, TB_TOUCH); - ev.ref_id = id; - ti->captured_widget->invokeEvent(ev); - } - DeleteTouchInfo(id); - } -} - -bool TBWidget::invokeWheel(int x, int y, int deltaX, int deltaY, MODIFIER_KEYS modifierkeys) { - setHoveredWidget(getWidgetAt(x, y, true), true); - - TBWidget *target = captured_widget != nullptr ? captured_widget : hovered_widget; - if (target != nullptr) { - target->convertFromRoot(x, y); - pointer_move_widget_x = x; - pointer_move_widget_y = y; - TBWidgetEvent ev(EVENT_TYPE_WHEEL, x, y, TB_TOUCH, modifierkeys); - ev.delta_x = deltaX; - ev.delta_y = deltaY; - target->invokeEvent(ev); - - // Return true when we have a target instead of InvokeEvent result. If a widget is - // hit is more interesting for callers than if the event was handled or not. - return true; - } - - return false; -} - -bool TBWidget::invokeKey(int key, SPECIAL_KEY specialKey, MODIFIER_KEYS modifierkeys, bool down) { - bool handled = false; - if (focused_widget != nullptr) { - // Emulate a click on the focused widget when pressing space or enter - if ((modifierkeys == 0U) && focused_widget->getClickByKey() && !focused_widget->getDisabled() && - !focused_widget->getIsDying() && (specialKey == TB_KEY_ENTER || key == ' ')) { - // Set the pressed state while the key is down, if it - // didn't already have the pressed state. - static bool check_pressed_state = true; - static bool had_pressed_state = false; - if (down && check_pressed_state) { - had_pressed_state = focused_widget->getState(WIDGET_STATE_PRESSED); - check_pressed_state = false; - } - if (!down) { - check_pressed_state = true; - } - - if (!had_pressed_state) { - focused_widget->setState(WIDGET_STATE_PRESSED, down); - focused_widget->m_packed.has_key_pressed_state = down; - } - - // Invoke the click event - if (!down) { - TBWidgetEvent ev(EVENT_TYPE_CLICK, m_rect.w / 2, m_rect.h / 2, TB_TOUCH); - focused_widget->invokeEvent(ev); - } - handled = true; - } else { - // Invoke the key event on the focused widget - TBWidgetEvent ev(down ? EVENT_TYPE_KEY_DOWN : EVENT_TYPE_KEY_UP); - ev.key = key; - ev.special_key = specialKey; - ev.modifierkeys = modifierkeys; - handled = focused_widget->invokeEvent(ev); - } - } - - // Move focus between widgets - if (down && !handled && specialKey == TB_KEY_TAB) { - handled = moveFocus((modifierkeys & TB_SHIFT) == 0U); - - // Show the focus when we move it by keyboard - if (handled) { - setAutoFocusState(true); - } - } - return handled; -} - -void TBWidget::releaseCapture() { - if (this == captured_widget) { - setCapturedWidget(nullptr); - } -} - -void TBWidget::convertToRoot(int &x, int &y) const { - const TBWidget *tmp = this; - while (tmp->m_parent != nullptr) { - x += tmp->m_rect.x; - y += tmp->m_rect.y; - tmp = tmp->m_parent; - - if (tmp != nullptr) { - int child_translation_x; - int child_translation_y; - tmp->getChildTranslation(child_translation_x, child_translation_y); - x += child_translation_x; - y += child_translation_y; - } - } -} - -void TBWidget::convertFromRoot(int &x, int &y) const { - const TBWidget *tmp = this; - while (tmp->m_parent != nullptr) { - x -= tmp->m_rect.x; - y -= tmp->m_rect.y; - tmp = tmp->m_parent; - - if (tmp != nullptr) { - int child_translation_x; - int child_translation_y; - tmp->getChildTranslation(child_translation_x, child_translation_y); - x -= child_translation_x; - y -= child_translation_y; - } - } -} - -// static -void TBWidget::setHoveredWidget(TBWidget *widget, bool touch) { - if (TBWidget::hovered_widget == widget) { - return; - } - if ((widget != nullptr) && widget->getState(WIDGET_STATE_DISABLED)) { - return; - } - - // We may apply hover state automatically so the widget might need to be updated. - if (TBWidget::hovered_widget != nullptr) { - TBWidget::hovered_widget->invalidate(); - TBWidget::hovered_widget->invalidateSkinStates(); - } - - TBWidget::hovered_widget = widget; - - if (widget && widget->getWantCaptureOnHover()) { - setCapturedWidget(widget); - } - - if (widget && widget->getWantFocusOnHover()) { - widget->setFocus(WIDGET_FOCUS_REASON_POINTER); - } - - if (TBWidget::hovered_widget != nullptr) { - TBWidget::hovered_widget->invalidate(); - TBWidget::hovered_widget->invalidateSkinStates(); - - // Cursor based movement should set hover state automatically, but touch - // events should not (since touch doesn't really move unless pressed). - TBWidget::hovered_widget->m_packed.no_automatic_hover_state = touch; - } -} - -// static -void TBWidget::setCapturedWidget(TBWidget *widget) { - if (TBWidget::captured_widget == widget) { - return; - } - if ((widget != nullptr) && widget->getState(WIDGET_STATE_DISABLED)) { - return; - } - - if (TBWidget::captured_widget != nullptr) { - // Stop panning when capture change (most likely changing to nullptr because of InvokePointerUp) - // Notify any active scroller so it may begin scrolling. - if (TBScroller *scroller = TBWidget::captured_widget->findStartedScroller()) { - if (TBWidget::captured_widget->m_packed.is_panning) { - scroller->onPanReleased(); - } else { - scroller->stop(); - } - } - TBWidget::captured_widget->m_packed.is_panning = false; - - // We apply pressed state automatically so the widget might need to be updated. - TBWidget::captured_widget->invalidate(); - TBWidget::captured_widget->invalidateSkinStates(); - - TBWidget::captured_widget->stopLongClickTimer(); - } - cancel_click = false; - - TBWidget *old_capture = TBWidget::captured_widget; - - TBWidget::captured_widget = widget; - - if (old_capture != nullptr) { - old_capture->onCaptureChanged(false); - } - - if (TBWidget::captured_widget != nullptr) { - TBWidget::captured_widget->invalidate(); - TBWidget::captured_widget->invalidateSkinStates(); - TBWidget::captured_widget->onCaptureChanged(true); - } -} - -bool TBWidget::setFontDescription(const TBFontDescription &fontDesc) { - if (m_font_desc == fontDesc) { - return true; - } - - // Set the font description only if we have a matching font, or succeed creating one. - if (g_font_manager->hasFontFace(fontDesc)) { - m_font_desc = fontDesc; - } else if (g_font_manager->createFontFace(fontDesc) != nullptr) { - m_font_desc = fontDesc; - } else { - return false; - } - - invokeFontChanged(); - return true; -} - -void TBWidget::invokeFontChanged() { - onFontChanged(); - - // Recurse to children that inherit the font - for (TBWidget *child = getFirstChild(); child != nullptr; child = child->getNext()) { - if (child->m_font_desc.getFontFaceID() == 0) { - child->invokeFontChanged(); - } - } -} - -TBFontDescription TBWidget::getCalculatedFontDescription() const { - const TBWidget *tmp = this; - while (tmp != nullptr) { - if (tmp->m_font_desc.getFontFaceID() != 0) { - return tmp->m_font_desc; - } - tmp = tmp->m_parent; - } - return g_font_manager->getDefaultFontDescription(); -} - -TBFontFace *TBWidget::getFont() const { - return g_font_manager->getFontFace(getCalculatedFontDescription()); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets.h b/src/modules/ui/turbobadger/tb/tb_widgets.h deleted file mode 100644 index dc87eee71..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets.h +++ /dev/null @@ -1,1292 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/Common.h" -#include "tb_core.h" -#include "tb_font_desc.h" -#include "tb_geometry.h" -#include "tb_linklist.h" -#include "tb_object.h" -#include "tb_skin.h" -#include "tb_widget_value.h" - -namespace tb { - -class TBWindow; -class TBWidget; -class TBFontFace; -class TBScroller; -class TBWidgetListener; -class TBLongClickTimer; -struct INFLATE_INFO; - -// == Generic widget stuff ================================================= - -enum TB_ALIGN { - TB_ALIGN_LEFT, ///< Align to the left side - TB_ALIGN_TOP, ///< Align to the top (above) - TB_ALIGN_RIGHT, ///< Align to the right side - TB_ALIGN_BOTTOM ///< Align to the bottom (below) -}; - -enum EVENT_TYPE { - /** Click event is what should be used to trig actions in almost all cases. - - It is invoked on a widget after POINTER_UP if the pointer is still inside - its hit area. It can also be invoked by keyboard on some clickable widgets - (see TBWidget::setClickByKey). - - If panning of scrollable widgets start while the pointer is down, CLICK - won't be invoked when releasing the pointer (since that should stop panning). */ - EVENT_TYPE_CLICK, - - /** Long click event is sent when the pointer has been down for some time - without moving much. - - It is invoked on a widget that has enabled it (TBWidget::setWantLongClick - If this event isn't handled, the widget will invoke a CONTEXT_MENU event. - If any of those are handled, the CLICK event that would normally be - invoked after the pending POINTER_UP will be suppressed. */ - EVENT_TYPE_LONG_CLICK, - EVENT_TYPE_POINTER_DOWN, - EVENT_TYPE_POINTER_UP, - EVENT_TYPE_POINTER_MOVE, - EVENT_TYPE_TOUCH_DOWN, - EVENT_TYPE_TOUCH_UP, - EVENT_TYPE_TOUCH_MOVE, - EVENT_TYPE_TOUCH_CANCEL, - EVENT_TYPE_WHEEL, - - /** Invoked after changing text in a TBTextField, changing selected item - in a TBSelectList etc. Invoking this event trigs synchronization with - connected TBWidgetValue and other widgets connected to it. */ - EVENT_TYPE_CHANGED, - EVENT_TYPE_KEY_DOWN, - EVENT_TYPE_KEY_UP, - - /** Invoked by the platform when a standard keyboard shortcut is pressed. - It's called before InvokeKeyDown (EVENT_TYPE_KEY_DOWN) and if the event - is handled (returns true), the KeyDown is canceled. - The ref_id will be set to one of the following: - "cut", "copy", "paste", "selectall", "undo", "redo", "new", "open", "save". */ - EVENT_TYPE_SHORTCUT, - /** - * Invoked when a command was executed - */ - EVENT_TYPE_COMMAND, - - /** Invoked when a context menu should be opened at the event x and y coordinates. - It may be invoked automatically for a widget on long click, if nothing handles - the long click event. */ - EVENT_TYPE_CONTEXT_MENU, - - /** Invoked by the platform when one or multiple files has been dropped on - the widget. The event is guaranteed to be a TBWidgetEventFileDrop. */ - EVENT_TYPE_FILE_DROP, - - /** Custom event. Not used internally. ref_id may be used for additional type info. */ - EVENT_TYPE_CUSTOM -}; - -enum MODIFIER_KEYS { TB_MODIFIER_NONE = 0, TB_CTRL = 1, TB_SHIFT = 2, TB_ALT = 4, TB_SUPER = 8 }; -CORE_ENUM_BIT_OPERATIONS(MODIFIER_KEYS); - -enum BUTTON_TYPE { TB_LEFT = 0, TB_RIGHT = 1, TB_MIDDLE = 2, TB_TOUCH = 4, TB_UNKNOWN = 8 }; -CORE_ENUM_BIT_OPERATIONS(BUTTON_TYPE); - -enum SPECIAL_KEY { - TB_KEY_UNDEFINED = 0, - TB_KEY_UP, - TB_KEY_DOWN, - TB_KEY_LEFT, - TB_KEY_RIGHT, - TB_KEY_PAGE_UP, - TB_KEY_PAGE_DOWN, - TB_KEY_HOME, - TB_KEY_END, - TB_KEY_TAB, - TB_KEY_BACKSPACE, - TB_KEY_INSERT, - TB_KEY_DELETE, - TB_KEY_ENTER, - TB_KEY_ESC, - TB_KEY_F1, - TB_KEY_F2, - TB_KEY_F3, - TB_KEY_F4, - TB_KEY_F5, - TB_KEY_F6, - TB_KEY_F7, - TB_KEY_F8, - TB_KEY_F9, - TB_KEY_F10, - TB_KEY_F11, - TB_KEY_F12, - TB_KEY_SHIFT, - TB_KEY_ALT, - TB_KEY_CTRL, - TB_KEY_GUI, - TB_KEY_MODE -}; - -class TBWidgetEvent : public TBTypedObject { -public: - TBWidget *target; ///< The widget that invoked the event - EVENT_TYPE type; ///< Which type of event - int target_x; ///< X position in target widget. Set for all pointer events, click and wheel. - int target_y; ///< Y position in target widget. Set for all pointer events, click and wheel. - int delta_x; ///< Set for EVENT_TYPE_WHEEL. Positive is a turn right. - int delta_y; ///< Set for EVENT_TYPE_WHEEL. Positive is a turn against the user. - int count; ///< 1 for all events, but increased for POINTER_DOWN event to 2 for doubleclick, 3 for tripleclick and - ///< so on. - int key; - SPECIAL_KEY special_key; - MODIFIER_KEYS modifierkeys; - TBID ref_id; ///< Sometimes (when documented) events have a ref_id (The id that caused this event) - BUTTON_TYPE button_type; ///< Set for pointer events. True if the event is a touch event (finger or pen on screen) - ///< False if mouse or other cursor input. - const char* string = nullptr; - - TBOBJECT_SUBCLASS(TBWidgetEvent, TBTypedObject); - - TBWidgetEvent(EVENT_TYPE type) - : target(nullptr), type(type), target_x(0), target_y(0), delta_x(0), delta_y(0), count(1), key(0), - special_key(TB_KEY_UNDEFINED), modifierkeys(TB_MODIFIER_NONE), button_type(TB_UNKNOWN) { - } - - TBWidgetEvent(EVENT_TYPE type, int x, int y, BUTTON_TYPE buttonType, MODIFIER_KEYS modifierkeys = TB_MODIFIER_NONE) - : target(nullptr), type(type), target_x(x), target_y(y), delta_x(0), delta_y(0), count(1), key(0), - special_key(TB_KEY_UNDEFINED), modifierkeys(modifierkeys), button_type(buttonType) { - } - - /** The count value may be 1 to infinity. If you f.ex want to see which count it is for something - handling click and double click, call GetCountCycle(2). If you also handle triple click, call - GetCountCycle(3) and so on. That way you'll get a count that always cycle in the range you need. */ - int getCountCycle(int max) { - return ((count - 1) % max) + 1; - } - - bool isPointerEvent() const { - return type == EVENT_TYPE_POINTER_DOWN || type == EVENT_TYPE_POINTER_UP || type == EVENT_TYPE_POINTER_MOVE; - } - bool isTouchEvent() const { - return type == EVENT_TYPE_TOUCH_DOWN || type == EVENT_TYPE_TOUCH_UP || type == EVENT_TYPE_TOUCH_MOVE || - type == EVENT_TYPE_TOUCH_CANCEL; - } - bool isKeyEvent() const { - return type == EVENT_TYPE_KEY_DOWN || type == EVENT_TYPE_KEY_UP; - } - bool isAny(const tb::TBID &id) const; -}; - -/** TBWidgetEventFileDrop is a event of type EVENT_TYPE_FILE_DROP. - It contains a list of filenames of the files that was dropped. */ -class TBWidgetEventFileDrop : public TBWidgetEvent { -public: - TBListAutoDeleteOf files; - - TBOBJECT_SUBCLASS(TBWidgetEventFileDrop, TBWidgetEvent); - - TBWidgetEventFileDrop() : TBWidgetEvent(EVENT_TYPE_FILE_DROP) { - } -}; - -/** TBWidget state types (may be combined). - NOTE: This should exactly match SKIN_STATE in tb_skin.h! */ -enum WIDGET_STATE : uint8_t { - WIDGET_STATE_NONE = 0, - WIDGET_STATE_DISABLED = 1, - WIDGET_STATE_FOCUSED = 2, - WIDGET_STATE_PRESSED = 4, - WIDGET_STATE_SELECTED = 8, - WIDGET_STATE_HOVERED = 16, - - WIDGET_STATE_ALL = WIDGET_STATE_DISABLED | WIDGET_STATE_FOCUSED | WIDGET_STATE_PRESSED | WIDGET_STATE_SELECTED | - WIDGET_STATE_HOVERED, - WIDGET_STATE_ENSURESIZE = 0xFFU -}; -CORE_ENUM_BIT_OPERATIONS(WIDGET_STATE); - -/** TBWidget gravity (may be combined). - Gravity gives hints about positioning and sizing preferences. */ -enum WIDGET_GRAVITY { - WIDGET_GRAVITY_NONE = 0, - WIDGET_GRAVITY_LEFT = 1, - WIDGET_GRAVITY_RIGHT = 2, - WIDGET_GRAVITY_TOP = 4, - WIDGET_GRAVITY_BOTTOM = 8, - - WIDGET_GRAVITY_LEFT_RIGHT = WIDGET_GRAVITY_LEFT | WIDGET_GRAVITY_RIGHT, - WIDGET_GRAVITY_TOP_BOTTOM = WIDGET_GRAVITY_TOP | WIDGET_GRAVITY_BOTTOM, - WIDGET_GRAVITY_ALL = WIDGET_GRAVITY_LEFT_RIGHT | WIDGET_GRAVITY_TOP_BOTTOM, - WIDGET_GRAVITY_DEFAULT = WIDGET_GRAVITY_LEFT | WIDGET_GRAVITY_TOP, - WIDGET_GRAVITY_ENSURESIZE = 0xFFU -}; -CORE_ENUM_BIT_OPERATIONS(WIDGET_GRAVITY); - -enum AXIS { - AXIS_X, ///< Horizontal layout - AXIS_Y, ///< Vertical layout - AXIS_Z ///< Depth -}; - -/** Defines how the size in one axis depend on the other axis when a widgets size is - affected by constraints. */ -enum SIZE_DEP { - /** No dependency (Faster layout). */ - SIZE_DEP_NONE = 0, - /** The width is dependant on the height. Additional layout pass may be required. */ - SIZE_DEP_WIDTH_DEPEND_ON_HEIGHT = 1, - /** The height is dependant on the width. Additional layout pass may be required. */ - SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH = 2, - /** Both width and height are dependant on each other. Additional layout pass may - be required. */ - SIZE_DEP_BOTH = SIZE_DEP_WIDTH_DEPEND_ON_HEIGHT | SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH -}; -CORE_ENUM_BIT_OPERATIONS(SIZE_DEP); - -/** PreferredSize contains size preferences for a TBWidget. - This is calculated during layout for each widget from - the current skin, widget preferences and LayoutParams. */ - -class PreferredSize { -public: - PreferredSize() - : min_w(0), min_h(0), max_w(10000), max_h(10000), pref_w(0), pref_h(0), size_dependency(SIZE_DEP_NONE) { - } - PreferredSize(int w, int h) - : min_w(w), min_h(h), max_w(w), max_h(h), pref_w(w), pref_h(h), size_dependency(SIZE_DEP_NONE) { - } - - int min_w, min_h; ///< The minimal preferred width and height. - int max_w, max_h; ///< The maximum preferred width and height. - int pref_w, pref_h; ///< The preferred width and height. - SIZE_DEP size_dependency; ///< The size dependency when size is affected by constraints. -}; - -/** LayoutParams defines size preferences for a TBWidget that - are set on the widget to override size preferences from - skin and widget. */ -class LayoutParams { -public: - static const int UNSPECIFIED = TB_INVALID_DIMENSION; - LayoutParams() - : min_w(UNSPECIFIED), min_h(UNSPECIFIED), max_w(UNSPECIFIED), max_h(UNSPECIFIED), pref_w(UNSPECIFIED), - pref_h(UNSPECIFIED) { - } - LayoutParams(int w, int h) : min_w(w), min_h(h), max_w(w), max_h(h), pref_w(w), pref_h(h) { - } - - /** Set both min max and preferred width to the given width. */ - void setWidth(int width) { - min_w = max_w = pref_w = width; - } - - /** Set both min max and preferred height to the given height. */ - void setHeight(int height) { - min_h = max_h = pref_h = height; - } - - int min_w, min_h; ///< The minimal preferred width and height. - int max_w, max_h; ///< The maximum preferred width and height. - int pref_w, pref_h; ///< The preferred width and height. -}; - -/** Specifies size constraints used during size calculations. */ -class SizeConstraints { -public: - static const int NO_RESTRICTION = 10000; - - /** The available width and height. May be NO_RESTRICTION which is a large value. */ - int available_w, available_h; - - /** Constrain to the given width and height. */ - SizeConstraints(int w, int h) : available_w(w), available_h(h) { - } - - /** No constraints. */ - SizeConstraints() : available_w(NO_RESTRICTION), available_h(NO_RESTRICTION) { - } - - /** Return new constraints reduced by the given padding. */ - SizeConstraints constrainByPadding(int horizontalPadding, int verticalPadding) const { - return SizeConstraints(available_w == NO_RESTRICTION ? NO_RESTRICTION : available_w - horizontalPadding, - available_h == NO_RESTRICTION ? NO_RESTRICTION : available_h - verticalPadding); - } - - /** Return new constraints that are constrained by LayoutParams. */ - SizeConstraints constrainByLayoutParams(const LayoutParams &lp) const { - return SizeConstraints(constrainByLPMax(available_w, lp.min_w, lp.max_w), - constrainByLPMax(available_h, lp.min_h, lp.max_h)); - } - - bool operator==(const SizeConstraints &sc) const { - return available_w == sc.available_w && available_h == sc.available_h; - } - -private: - int constrainByLPMax(int constraint, int lpMin, int lpMax) const { - if (constraint == NO_RESTRICTION) { - return lpMax != LayoutParams::UNSPECIFIED ? lpMax : NO_RESTRICTION; - } - int ret = constraint; - if (lpMin != LayoutParams::UNSPECIFIED) { - ret = Max(ret, lpMin); - } - if (lpMax != LayoutParams::UNSPECIFIED) { - ret = Min(ret, lpMax); - } - return ret; - } -}; - -/** Defines widget z level, used with TBWidget::setZ, TBWidget::AddChild. */ -enum WIDGET_Z { - WIDGET_Z_TOP, ///< The toplevel (Visually drawn on top of everything else). - WIDGET_Z_BOTTOM ///< The bottomlevel (Visually drawn behind everything else). -}; - -/** Defines widget z level relative to another widget, used with TBWidget::AddChildRelative. */ -enum WIDGET_Z_REL { - WIDGET_Z_REL_BEFORE, ///< Before the reference widget (visually behind reference). - WIDGET_Z_REL_AFTER ///< After the reference widget (visually above reference). -}; - -/** Defines widget visibility, used with TBWidget::setVisibility. */ -enum WIDGET_VISIBILITY { - WIDGET_VISIBILITY_VISIBLE, ///< Visible (default) - WIDGET_VISIBILITY_INVISIBLE, ///< Invisible, but layouted. Interaction disabled. - WIDGET_VISIBILITY_GONE ///< Invisible and no layout. Interaction disabled. -}; - -enum WIDGET_INVOKE_INFO { WIDGET_INVOKE_INFO_NORMAL, WIDGET_INVOKE_INFO_NO_CALLBACKS }; - -enum WIDGET_FOCUS_REASON { - WIDGET_FOCUS_REASON_NAVIGATION, ///< Set focus by navigation (i.e. keyboard tab). This will - ///< scroll to the widget if needed. - WIDGET_FOCUS_REASON_POINTER, ///< Set focus by pointer (i.e. clicking) - WIDGET_FOCUS_REASON_UNKNOWN ///< Set focus by anything else. -}; - -/** Hit status return value for TBWidget::getHitStatus */ -enum WIDGET_HIT_STATUS { - WIDGET_HIT_STATUS_NO_HIT = 0, ///< The widget was not hit - WIDGET_HIT_STATUS_HIT, ///< The widget was hit, any child may be hit too. - WIDGET_HIT_STATUS_HIT_NO_CHILDREN ///< The widget was hit, no children should be hit. -}; - -/** The base TBWidget class. - Make a subclass to implement UI controls. - Each widget has a background skin (no skin specified by default) which will be used to - calculate the default size preferences and padding around the preferred content size. - - Note: When you subclass a widget, use the TBOBJECT_SUBCLASS macro to define the type - casting functions instead of implementing those manually. */ - -class TBWidget : public TBTypedObject, public TBLinkOf { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBWidget, TBTypedObject); - - TBWidget(); - virtual ~TBWidget(); - - /** Set the rect for this widget in its parent. The rect is relative to the parent widget. - The skin may expand outside this rect to draw f.ex shadows. */ - void setRect(const TBRect &rect); - inline const TBRect& getRect() const { - return m_rect; - } - - /** Set position of this widget in its parent. The position is relative to the parent widget. */ - void setPosition(const TBPoint &pos) { - setRect(TBRect(pos.x, pos.y, m_rect.w, m_rect.h)); - } - - /** Set size of this widget. */ - void setSize(int width, int height) { - int dw = width - m_rect.w; - int dh = height - m_rect.h; - TBRect rect(m_rect.x, m_rect.y, width, height); - // If the widget is anchored to an edge, move its origin appropriately - if ((m_gravity & WIDGET_GRAVITY_RIGHT) && !((m_gravity & WIDGET_GRAVITY_LEFT))) - rect.x -= dw; - if ((m_gravity & WIDGET_GRAVITY_BOTTOM) && !((m_gravity & WIDGET_GRAVITY_TOP))) - rect.y -= dh; - setRect(rect); - } - - void execute(CORE_FORMAT_STRING const char* msg, ...) CORE_PRINTF_VARARG_FUNC(2); - - /** Invalidate should be called if the widget need to be repainted, - to make sure the renderer repaints it and its children next frame. */ - void invalidate(); - - /** Call if something changes that might need other widgets to update their state. - F.ex if a action availability changes, some widget might have to become enabled/disabled. - Calling this will result in a later call to OnProcessStates(). - This is done automatically for all invoked events of type: - EVENT_TYPE_CLICK, EVENT_TYPE_LONG_CLICK, EVENT_TYPE_CHANGED, EVENT_TYPE_KEYDOWN, - EVENT_TYPE_KEYUP. */ - void invalidateStates(); - - /** Call if something changes that might cause any skin to change due to different state - or conditions. This is called automatically from InvalidateStates(), when event - EVENT_TYPE_CHANGED is invoked, and in various other situations. */ - void invalidateSkinStates(); - - /** Delete the widget with the possibility for some extended life during animations. - - If any widget listener responds true to onWidgetDying it will be kept as a child and live - until the animations are done, but the widgets and all its children are marked as dying. - Dying widgets get no input or focus. - - If no widget listener responded, it will be deleted immediately. */ - void die(); - - /** Return true if this widget or any of its parents is dying. */ - bool getIsDying() const { - return m_packed.is_dying || ((m_parent != nullptr) && m_parent->getIsDying()); - } - - /** Set the id reference for this widgets. This id is 0 by default. - You can use this id to receive the widget from GetWidgetByID (or - preferable TBSafeGetByID to avoid dangerous casts). */ - void setID(const TBID &id); - TBID &getID() { - return m_id; - } - - /** Set the group id reference for this widgets. This id is 0 by default. - All widgets with the same group id under the same group root will - be automatically changed when one change its value. */ - void setGroupID(const TBID &id) { - m_group_id = id; - } - TBID &getGroupID() { - return m_group_id; - } - - /** Get this widget or any child widget with a matching id, or nullptr if none is found. */ - TBWidget *getWidgetByID(const TBID &id) { - return getWidgetByIDInternal(id); - } - - /** Get this widget or any child widget with a matching id and type, or nullptr if none is found. */ - template T *getWidgetByIDAndType(const TBID &id) { - return (T *)getWidgetByIDInternal(id, getTypeId()); - } - - /** Enable or disable the given state(s). The state affects which skin state is used when drawing. - Some states are set automatically on interaction. See GetAutoState(). */ - void setState(WIDGET_STATE state, bool on); - - /** Get status of the given state(s). Returns true if the given state combination is set. */ - bool getState(WIDGET_STATE state) const { - return (m_state & state) != 0U; - } - - /** Set the widget state. Like SetState but setting the entire state as given, instead - of toggling individual states. See SetState for more info on states. */ - void setStateRaw(WIDGET_STATE state); - - /** Get the widget state. */ - WIDGET_STATE getStateRaw() const { - return m_state; - } - - /** Return the current combined state for this widget. It will also add some - automatic states, such as hovered (if the widget is currently hovered), or pressed etc. - - Automatic states: WIDGET_STATE_PRESSED, WIDGET_STATE_HOVERED, WIDGET_STATE_FOCUSED. - - Remarks for WIDGET_STATE_FOCUSED: May also be controlled by calling SetAutoFocusState and - the define TB_ALWAYS_SHOW_EDIT_FOCUS. */ - WIDGET_STATE getAutoState() const; - - /** Set if the state WIDGET_STATE_FOCUSED should be set automatically for the focused widget. - This value is set to true when moving focus by keyboard, and set to off when clicking - with the pointer. */ - static void setAutoFocusState(bool on); - - /** Set opacity for this widget and its children from 0.0 - 1.0. - If opacity is 0 (invisible), the widget won't receive any input. */ - void setOpacity(float opacity); - float getOpacity() const { - return m_opacity; - } - - /** Set visibility for this widget and its children. - If visibility is not WIDGET_VISIBILITY_VISIBLE, the widget won't receive any input. */ - void setVisibility(WIDGET_VISIBILITY vis); - WIDGET_VISIBILITY getVisibility() const; - - /** Return true if this widget and all its ancestors are visible - (has a opacity > 0 and visibility WIDGET_VISIBILITY_VISIBLE) */ - bool getVisibilityCombined() const; - - /** Return true if this widget or any of its parents are disabled (has state WIDGET_STATE_DISABLED). */ - bool getDisabled() const; - - /** Add the child to this widget. The child widget will automatically be deleted when - this widget is deleted. (If the child isn't removed again with RemoveChild.) */ - void addChild(TBWidget *child, WIDGET_Z z = WIDGET_Z_TOP, WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL); - - /** Add the child to this widget. See AddChild for adding a child to the top or bottom. - This takes a relative Z and insert the child before or after the given reference widget.*/ - void addChildRelative(TBWidget *child, WIDGET_Z_REL z, TBWidget *reference, - WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL); - - /** Remove child from this widget without deleting it. */ - void removeChild(TBWidget *child, WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL); - - /** Remove this widget from parent if it has one. */ - void removeFromParent() { - if (m_parent != nullptr) { - m_parent->removeChild(this); - } - } - - /** Remove and delete all children in this widget. - Note: This won't invoke Die so there's no chance for widgets to survive or - animate. They will be instantly removed and deleted. */ - void deleteAllChildren(); - - /** Sets the z-order of this widget related to its siblings. When a widget is added with AddChild, it will be - placed at the top in the parent (Above previously added widget). SetZ can be used to change the order. */ - void setZ(WIDGET_Z z); - - /** Set the z order in which children are added during resource loading. */ - void setZInflate(WIDGET_Z z) { - m_packed.inflate_child_z = z; - } - WIDGET_Z getZInflate() const { - return (WIDGET_Z)m_packed.inflate_child_z; - } - - /** Set the widget gravity (any combination of WIDGET_GRAVITY). - For child widgets in a layout, the gravity affects how the layout is done depending on the layout settings. - For child widgets in a non layout widget, it will do some basic resizing/moving: - -left && right: Widget resize horizontally when parent resize. - -!left && right: Widget follows the right edge when parent resize. - -top && bottom: Widget resize vertically when parent resize. - -!top && bottom: Widget follows the bottom edge when parent resize. */ - void setGravity(WIDGET_GRAVITY g); - WIDGET_GRAVITY getGravity() const { - return m_gravity; - } - - /** Set the skin background for this widget and call onSkinChanged if it changed. - - The skin background is used for calculating padding, preferred size - etc. if the widget doesn't have any preferences itself. - - The skin will be painted according to the current widget state (WIDGET_STATE). - If there is no special skin state for WIDGET_STATE_FOCUSED, it will paint the skin - element called "generic_focus" (if it exist) after painting all widget children. - - It's possible to omit the OnSkinChanged callback using WIDGET_INVOKE_INFO_NO_CALLBACKS. */ - void setSkinBg(const TBID &skin_bg, WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL); - - /** Return the current skin background, as set by SetSkinBg. */ - TBID getSkinBg() const { - return m_skin_bg; - } - - /** Return the skin background element, or nullptr. */ - TBSkinElement *getSkinBgElement(); - - /** Set if this widget is a group root. Grouped widgets (such as TBRadioButton) will toggle all other - widgets with the same group_id under the nearest parent group root. TBWindow is a group root by default. */ - void setIsGroupRoot(bool groupRoot) { - m_packed.is_group_root = groupRoot; - } - bool getIsGroupRoot() const { - return m_packed.is_group_root; - } - - /** Set if this widget should be able to receive focus or not. */ - void setIsFocusable(bool focusable) { - m_packed.is_focusable = focusable; - } - bool getIsFocusable() const { - return m_packed.is_focusable; - } - - void setWantCaptureOnHover(bool want_capture_on_hover) { - m_packed.want_capture_on_hover = want_capture_on_hover; - } - bool getWantCaptureOnHover() const { - return m_packed.want_capture_on_hover; - } - - void setWantFocusOnHover(bool want_focus_on_hover) { - m_packed.want_focus_on_hover = want_focus_on_hover; - } - bool getWantFocusOnHover() const { - return m_packed.want_focus_on_hover; - } - - /** Set if this widget should emulate a click when it's focused and pressing enter or space. */ - void setClickByKey(bool clickByKey) { - m_packed.click_by_key = clickByKey; - } - bool getClickByKey() const { - return m_packed.click_by_key; - } - - /** Set if this widget should generate long-click event (or context menu event if nothing - handles the long click event). The default is false. */ - void setWantLongClick(bool wantLongClick) { - m_packed.want_long_click = wantLongClick; - } - bool getWantLongClick() const { - return m_packed.want_long_click; - } - - /** Set if this widget should ignore input, as if it didn't exist. */ - void setIgnoreInput(bool ignoreInput) { - m_packed.ignore_input = ignoreInput; - } - bool getIgnoreInput() const { - return m_packed.ignore_input; - } - - /** Get if this widget wants interaction depending on various states. - Cares about zero opacity, visibility, flag set by SetIgnoreInput, disabled state, - and if the widget is currently dying. */ - bool getIsInteractable() const; - - /** Set this widget to be the focused widget. It will be the one receiving keyboard input. - Widgets can be focused only after enabling it (See SetIsFocusable(true)). - Invisible or disabled widgets can not be focused. - - If SetFocus is called on a widget in a inactive window, it will succeed (return true), - but it won't actually become focused until that window is activated (See TBWindow::setLastFocus). - - Returns true if successfully focused, or if set as last focus in its window. */ - bool setFocus(WIDGET_FOCUS_REASON reason, WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL); - bool getIsFocused() const { - return focused_widget == this; - } - - /** Call SetFocus on all children and their children, until a widget is found that accepts it. - Returns true if some child was successfully focused. */ - bool setFocusRecursive(WIDGET_FOCUS_REASON reason = WIDGET_FOCUS_REASON_UNKNOWN); - - /** Move focus from the currently focused widget to another focusable widget. It will search - for a focusable widget in the same TBWindow (or top root if there is no window) forward or - backwards in the widget order. */ - bool moveFocus(bool forward); - - /** Returns the child widget that contains the coordinate or nullptr if no one does. If include_children - is true, the search will recurse into the childrens children. */ - TBWidget *getWidgetAt(int x, int y, bool include_children) const; - - /** Get the child at the given index, or nullptr if there was no child at that index. - Note: Avoid calling this in loops since it does iteration. Consider iterating - the widgets directly instead! */ - TBWidget *getChildFromIndex(int index) const; - - /** Get the child index of the given widget (that must be a child of this widget). - Note: Avoid calling this in loops since it does iteration. Consider iterating - the widgets directly instead! */ - int getIndexFromChild(TBWidget *child) const; - - /** Get the text of a child widget with the given id, or an empty string if there was - no widget with that id. */ - core::String getTextByID(const TBID &id); - - /** Get the value of a child widget with the given id, or 0 if there was no widget - with that id. */ - int getValueByID(const TBID &id); - - TBWidget *getNextDeep(const TBWidget *bounding_ancestor = nullptr) const; - TBWidget *getPrevDeep() const; - TBWidget *getLastLeaf() const; - inline TBWidget *getFirstChild() const { - return m_children.getFirst(); - } - inline TBWidget *getLastChild() const { - return m_children.getLast(); - } - TBLinkListOf::Iterator getIteratorForward() { - return m_children.iterateForward(); - } - TBLinkListOf::Iterator getIteratorBackward() { - return m_children.iterateBackward(); - } - - /** Return true if this widget is the same or a ancestor of other_widget. */ - bool isAncestorOf(TBWidget *other_widget) const; - - /** Return true if this widget is the same as other_widget or if other_widget - events are going through this widget (See GetEventDestination()) */ - bool isEventDestinationFor(TBWidget *other_widget) const; - - // == Callbacks ============================================== - - /** Add a listener to this widget. It should be removed again with - RemoveListener before the widget is deleted. */ - void addListener(TBWidgetListener *listener); - void removeListener(TBWidgetListener *listener); - bool hasListener(TBWidgetListener *listener) const; - - /** Callback for handling events. - Return true if the event is handled and should not - continue to be handled by any parent widgets. */ - virtual bool onEvent(const TBWidgetEvent &ev) { - return false; - } - - /** Callback for doing anything that might be needed before paint. - F.ex Updating invalid layout, formatting text etc. */ - virtual void onProcess() { - } - - /** Callback for doing anything that might be needed before paint. - This is called after OnProcess has been called on this widgets children. */ - virtual void onProcessAfterChildren() { - } - - /** Callback for doing state updates that depend on your application state. - F.ex setting the disabled state on a widget whose action is currently not - available. This callback is called for all widgets before OnProcess if - something has called InvalidateStates().*/ - virtual void onProcessStates() { - } - - /** PaintProps contains properties needed for painting a widget. - Properties may be inherited from the parent widget if not specified - in the skin. */ - class PaintProps { - public: - PaintProps(); - - /** Text color as specified in the skin element, or inherited from parent. */ - TBColor text_color; - }; - - /** Callback for painting this widget. - The skin is already painted and the opacity set to reflect this widgets. - This is only called for widgets with a opacity > 0 */ - virtual void onPaint(const PaintProps &paintProps) { - } - - /** Callback for painting child widgets. - The default implementation is painting all children. */ - virtual void onPaintChildren(const PaintProps &paint_props); - - /** Callback for when this widget or any of its children have - called Invalidate() */ - virtual void onInvalid() { - } - - /** Called when the background skin changes by calling SetSkinBg(), or when the skin - has changed indirectly after a skin condition changes in a way that may affect layout. - - For indirect skin changes, OnSkinChanged is called before validation of layouts is about - to happen in InvokeProcess(). - */ - virtual void onSkinChanged() { - } - - /** Called when the font has changed. */ - virtual void onFontChanged() { - } - - /** Called when the focus has changed. */ - virtual void onFocusChanged(bool focused) { - } - - /** Called when the visibility has changed. - Note: This is not called when combined visibility change, so it may change visibility - because of ancestors without this being called. */ - virtual void onVisibilityChanged() { - } - - /** Called when the capture has changed. */ - virtual void onCaptureChanged(bool captured) { - } - - /** Called when a child widget has been added to this widget (before calling OnAdded on child). */ - virtual void onChildAdded(TBWidget *child) { - } - - /** Called when a child widget is about to be removed from this widget (before calling OnRemove on child). */ - virtual void onChildRemove(TBWidget *child) { - } - - /** Called when this widget has been added to a parent (after calling OnChildAdded on parent). */ - virtual void onAdded() { - } - - /** Called when a this widget has been removed from its parent (after calling OnChildRemove on parent). */ - virtual void onRemove() { - } - - /** Called when Die() is called on this widget. Note: Not called for children to the widget Die() was - invoked on even though they are also dying. */ - virtual void onDie() { - } - - /** Called when this widget has been resized. - The default implementation move and resize all children according to their gravity. */ - virtual void onResized(int oldW, int oldH); - - /** Called when this widget has been scrolled. */ - virtual void onScroll(int scrollX, int scrollY) { - } - - /** Called just after a child have been inflated into this widget. - The default implementation will resize the child to it's preferred size - and position it according to the gravity. If you implement a layouting - widget, you should override this to prevent doing unnecessary measuring. */ - virtual void onInflateChild(TBWidget *child); - - /** Called when this widget is inflated from resources, before any children - have been inflated. This will read generic widget properties and add the - widget to the hierarchy if it's not already added. If overridden, you - must call the super implementation. */ - virtual void onInflate(const INFLATE_INFO &info); - - /** Get hit status tests if this widget should be hit at the given coordinate. - The default implementation checks the visibility, ignored input flag, rectangle, - and disabled status. */ - virtual WIDGET_HIT_STATUS getHitStatus(int x, int y); - - /** Get if skin condition applies to this widget. This is called when a skin condition has the property - PROPERTY_CUSTOM (not a generic one known by skin and the default widget condition context). - This can be used to extend the skin conditions support with properties specific to different widgets. */ - virtual bool getCustomSkinCondition(const TBSkinCondition::CONDITION_INFO &info) { - return false; - } - - /** Get this widget or a child widget that should be root for other children. This is useful - for widgets having multiple children by default, to specify which one that should get the children. */ - virtual TBWidget *getContentRoot() { - return this; - } - - /** Get this widget or a parent widget that is the absolute root parent. */ - TBWidget *getParentRoot(); - - /** Get the closest parent widget that is a TBWindow or nullptr if there is none. - If this widget is a window itself, this will be returned. */ - TBWindow *getParentWindow(); - - /** Get the parent widget, or nullptr if this widget is not added. */ - inline TBWidget *getParent() const { - return m_parent; - } - - /** Get the widget that should receive the events this widget invoke. By default the parent. */ - virtual TBWidget *getEventDestination() const { - return m_parent; - } - - /** Return translation the children should have. Any scrolling of child widgets - should be done with this method, by returning the wanted translation. - - When reimplementing this, you must also implement ScrollTo and GetScrollInfo - so focus-scroll and panning will work automatically when dragging this or - any child widget. Note: You can apply the translation on one widget and - implement those methods on a parent, by returning this widget from the - parents GetScrollRoot(). */ - virtual void getChildTranslation(int &x, int &y) const { - x = y = 0; - } - - /** If this is a widget that scroll children (see GetChildTranslation), it should - scroll to the coordinates x, y. - This must result in calling OnScroll if scrolling occured. */ - virtual void scrollTo(int x, int y) { - } - - /** Start the TBScroller for this widget and scroll it to the given position. - Will cancel any on going smooth scroll operation. */ - void scrollToSmooth(int x, int y); - - /** If this is a widget that scroll children (see GetChildTranslation), it will - scroll by delta dx, dy relative to its current position. */ - void scrollBy(int dx, int dy); - - /** Start the TBScroller for this widget and scroll it by the given delta. - Consecutive calls will accumulate the scroll speed. */ - void scrollBySmooth(int dx, int dy); - - /** Information about scrolling for a widget at the time of calling GetScrollInfo. */ - class ScrollInfo { - public: - ScrollInfo() : min_x(0), min_y(0), max_x(0), max_y(0), x(0), y(0) { - } - bool canScrollX() const { - return max_x > min_x; - } - bool canScrollY() const { - return max_y > min_y; - } - bool canScrollLeft() const { - return x > min_x; - } - bool canScrollRight() const { - return x < max_x; - } - bool canScrollUp() const { - return y > min_y; - } - bool canScrollDown() const { - return y < max_y; - } - bool canScroll() const { - return canScrollX() || canScrollY(); - } - int min_x, min_y; ///< Minimum x and y scroll position. - int max_x, max_y; ///< Maximum x and y scroll position. - int x, y; ///< Current x and y scroll position. - }; - - /** If this is a widget that scroll children (see GetChildTranslation), - it should return the current scroll information. */ - virtual ScrollInfo getScrollInfo() { - return ScrollInfo(); - } - - /** If this widget is implementing ScrollTo and GetScrollInfo but - the corresponding GetChildTranslation is implemented on a child, - you should return that child from this method. */ - virtual TBWidget *getScrollRoot() { - return this; - } - - /** Scroll this widget and/or any parent widgets by the given delta. - dx and dy will be reduced by the amount that was successfully - scrolled. */ - void scrollByRecursive(int &dx, int &dy); - - /** Make this widget visible by calling ScrollIntoView on all parent widgets */ - void scrollIntoViewRecursive(); - - /** If this is a widget that scroll children (see GetChildTranslation), it will - scroll so that rect is visible. Rect is relative to this widget. */ - void scrollIntoView(const TBRect &rect); - - /** Return the TBScroller set up for this widget, or nullptr if creation failed. */ - TBScroller *getScroller(); - - // == Setter shared for many types of widgets ============ - - /** Set along which axis the content should be layouted. */ - virtual void setAxis(AXIS axis) { - } - virtual AXIS getAxis() const { - return AXIS_X; - } - - /** Set the value of this widget. Implemented by most widgets (that has a value). - Note: Some widgets also provide special setters with other types (such as double). */ - virtual void setValue(int value) { - } - virtual int getValue() const { - return 0; - } - - /** Set the value in double precision. It only makes sense to use this instead - of setValue() on widgets that store the value as double. F.ex TBScrollBar, TBSlider. */ - virtual void setValueDouble(double value) { - setValue((int)value); - } - - /** Return the value in double precision. It only makes sense to use this instead - of getValue() on widgets that store the value as double. F.ex TBScrollBar, TBSlider. */ - virtual double getValueDouble() const { - return (double)getValue(); - } - - /** Set the text of this widget. Implemented by most widgets (that has text). */ - virtual bool setText(const char *text) { - return true; - } - - /** Set the text of this widget. Implemented by most widgets (that has text). */ - bool setText(const core::String& text) { - return setText(text.c_str()); - } - - /** Get the text of this widget. Implemented by most widgets (that has text). - returns false if it failed. */ - virtual bool getText(core::String &text) { - text.clear(); - return true; - } - - /** Get the text of this widget. Implemented by most widgets (that has text). */ - core::String getText() { - core::String str; - getText(str); - return str; - } - - /** Connect this widget to a widget value. - - When this widget invoke EVENT_TYPE_CHANGED, it will automatically update the - connected widget value, and any other widgets that may be connected to it. - - On connection, the value of this widget will be updated to the value of the - given TBWidgetValue. */ - void connect(TBWidgetValue *value) { - m_connection.connect(value, this); - } - - /** Unconnect, if this widget is connected to a TBWidgetValue. */ - void unconnect() { - m_connection.unconnect(); - } - - /** Get the rectangle inside any padding, relative to this widget. This is the - rectangle in which the content should be rendered. - - This may be overridden to f.ex deduct space allocated by visible scrollbars - managed by this widget. Anything that removes space from the content area. */ - virtual TBRect getPaddingRect(); - - /** Calculate the preferred content size for this widget. This is the size of the actual - content. Don't care about padding or other decoration. */ - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints); - - /** Calculate the preferred size for this widget. This is the full size of the widget, - content + padding + eventual other decoration (but not skin expansion). - This is the size that should be used for layouting a widget. - The returned PreferredSize also contains minimal size and maximum size. */ - virtual PreferredSize onCalculatePreferredSize(const SizeConstraints &constraints); - - /** Get the PreferredSize for this widget. - This returns cached data if valid, or calls OnCalculatePreferredSize if needed. */ - PreferredSize getPreferredSize(const SizeConstraints &constraints); - PreferredSize getPreferredSize() { - return getPreferredSize(SizeConstraints()); - } - - /** Type used for InvalidateLayout */ - enum INVALIDATE_LAYOUT { - INVALIDATE_LAYOUT_TARGET_ONLY, ///< InvalidateLayout should not be recursively called on parents. - INVALIDATE_LAYOUT_RECURSIVE ///< InvalidateLayout should recursively be called on parents too. - }; - - /** Invalidate layout for this widget so it will be scheduled for relayout. - Any change to the size preferences for a widget should call this to let parent layout adjust to the change. - - Remarks for layout widgets: - - When a layout widget get this, it should mark its layout as invalid and do the layout later - (in GetPreferredContentSize/GetPreferredSize are called). If a layout knows that no parents will - be affected, it may stop recursion to parents to avoid unnecessary relayout. - - When setting the size of a layout widget (typically from another layout widget or from a OnResize), - it should be called with INVALIDATE_LAYOUT_TARGET_ONLY to avoid recursing back up to parents when - already recursing down, to avoid unnecessary computation. - */ - virtual void invalidateLayout(INVALIDATE_LAYOUT il); - - /** Set layout params. Calls InvalidateLayout. */ - void setLayoutParams(const LayoutParams &lp); - - /** Get layout params, or nullptr if not specified. - Note: The layout params has already been applied to the PreferredSize returned - from GetPreferredSize so you normally don't need to check these params. */ - const LayoutParams *getLayoutParams() const { - return m_layout_params; - } - - // == Misc methods for invoking events. Should normally be called only on the root widget =============== - - /** Invoke OnProcess and OnProcessAfterChildren on this widget and its children. */ - void invokeProcess(); - - /** Invoke OnProcessStates on all child widgets, if state processing - is needed (InvalidateStates() has been called) */ - void invokeProcessStates(bool force_update = false); - - /** Invoke paint on this widget and all its children */ - void invokePaint(const PaintProps &parent_paint_props); - - /** Invoke OnFontChanged on this widget and recursively on any children that inherit the font. */ - void invokeFontChanged(); - - /** Invoke a event on this widget. - - This will first check with all registered TBWidgetListener if the event should be dispatched. - - If the widgets onEvent returns false (event not handled), it will continue traversing to - GetEventDestination (by default the parent) until a widget handles the event. - - Note: When invoking event EVENT_TYPE_CHANGED, this will update the value of other widgets connected - to the same group. - - Note: Some event types will automatically invalidate states (See InvalidateStates(), InvalidateSkinStates()) - - Note: Remember that this widgets may be deleted after this call! So if you really must do something after - this call and are not sure what the event will cause, use TBWidgetSafePointer to detect self deletion. */ - bool invokeEvent(TBWidgetEvent &ev); - - bool invokePointerDown(int x, int y, int click_count, MODIFIER_KEYS modifierkeys, BUTTON_TYPE type); - bool invokePointerUp(int x, int y, MODIFIER_KEYS modifierkeys, BUTTON_TYPE type); - void invokePointerMove(int x, int y, MODIFIER_KEYS modifierkeys, BUTTON_TYPE type); - void invokePointerCancel(); - - /** Invoke touch events with ref_id set as the given id. - Note: For id 0, it will invoke EVENT_TYPE_POINTER_DOWN (with touch flag set to true), and EVENT_TYPE_TOUCH_DOWN - for other id > 0. This results in normal interaction for first finger, and optional handling of other - simultaneous interaction. GetTouchInfo(id) can be used to get additional interaction info. */ - bool invokeTouchDown(int x, int y, uint32_t id, int click_count, MODIFIER_KEYS modifierkeys); - bool invokeTouchUp(int x, int y, uint32_t id, MODIFIER_KEYS modifierkeys); - void invokeTouchMove(int x, int y, uint32_t id, MODIFIER_KEYS modifierkeys); - void invokeTouchCancel(uint32_t id); - - bool invokeWheel(int x, int y, int deltaX, int deltaY, MODIFIER_KEYS modifierkeys); - - /** Invoke the EVENT_TYPE_KEY_DOWN and EVENT_TYPE_KEY_UP events on the currently focused widget. - This will also do some generic key handling, such as cycling focus on tab etc. */ - bool invokeKey(int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys, bool down); - - /** A widget that receive a EVENT_TYPE_POINTER_DOWN event, will stay "captured" until EVENT_TYPE_POINTER_UP - is received. While captured, all EVENT_TYPE_POINTER_MOVE are sent to it. This method can force release the - capture, which may happen f.ex if the TBWidget is removed while captured. */ - void releaseCapture(); - - /** Make x and y (relative to this widget) relative to the upper left corner of the root widget. */ - void convertToRoot(int &x, int &y) const; - - /** Make x and y (relative to the upper left corner of the root widget) relative to this widget. */ - void convertFromRoot(int &x, int &y) const; - - /** Set the font description for this widget and any children that inherit the font. - - Setting a unspecified TBFontDescription (no changes made since construction) means - it will be inherited from parent (the default). - - Returns true and invokes OnFontChanged on all affected widgets, if the - font was successfully set. - - Returns false and keep the font onchanged if it no matching font exists or fails creation. */ - bool setFontDescription(const TBFontDescription &font_desc); - - /** Get the font description as set with SetFontDescription. Use GetCalculatedFontDescription() - to get the calculated font description (Inherit from parent widget etc.) */ - TBFontDescription getFontDescription() const { - return m_font_desc; - } - - /** Calculate the font description for this widget. If this widget have unspecified font - description, it will be inheritted from parent. If no parent specify any font, - the default font description will be returned. */ - TBFontDescription getCalculatedFontDescription() const; - - /** Get the TBFontFace for this widget from the current font description (calculated - by GetCalculatedFontDescription) */ - TBFontFace *getFont() const; - -private: - friend class TBWidgetListener; ///< It does iteration of m_listeners for us. - TBWidget *m_parent; ///< The parent of this widget - TBRect m_rect; ///< The rectangle of this widget, relative to the parent. See SetRect. - TBID m_id; ///< ID for GetWidgetByID and others. - TBID m_group_id; ///< ID for button groups (such as TBRadioButton) - TBID m_skin_bg; ///< ID for the background skin (0 for no skin). - TBID m_skin_bg_expected; ///< ID for the background skin after strong override, - ///< used to indirect skin changes because of condition changes. - TBLinkListOf m_children; ///< List of child widgets - TBWidgetValueConnection m_connection; ///< TBWidget value connection - TBLinkListOf m_listeners; ///< List of listeners - float m_opacity; ///< Opacity 0-1. See SetOpacity. - WIDGET_STATE m_state; ///< The widget state (excluding any auto states) - WIDGET_GRAVITY m_gravity; ///< The layout gravity setting. - TBFontDescription m_font_desc; ///< The font description. - PreferredSize m_cached_ps; ///< Cached preferred size. - SizeConstraints m_cached_sc; ///< Cached size constraints. - LayoutParams *m_layout_params; ///< Layout params, or nullptr. - TBScroller *m_scroller; - TBLongClickTimer *m_long_click_timer; - union { - struct { - uint16_t is_group_root : 1; - uint16_t is_focusable : 1; - uint16_t want_capture_on_hover : 1; - uint16_t want_focus_on_hover : 1; - uint16_t click_by_key : 1; - uint16_t has_key_pressed_state : 1; - uint16_t ignore_input : 1; - uint16_t is_dying : 1; - uint16_t is_cached_ps_valid : 1; - uint16_t no_automatic_hover_state : 1; - uint16_t is_panning : 1; - uint16_t want_long_click : 1; - uint16_t visibility : 2; - uint16_t inflate_child_z : 1; // Should have enough bits to hold WIDGET_Z values. - } m_packed; - uint16_t m_packed_init; - }; - -public: - /** This value is free to use for anything. It's not used by TBWidget itself. Initially TYPE_NULL. */ - TBValue data; - - // Debugging -#ifdef TB_RUNTIME_DEBUG_INFO - double last_measure_time; - double last_layout_time; -#endif // TB_RUNTIME_DEBUG_INFO - - // TBWidget related globals - static TBWidget *hovered_widget; ///< The currently hovered widget, or nullptr. - static TBWidget *captured_widget; ///< The currently captured widget, or nullptr. - static TBWidget *focused_widget; ///< The currently focused widget, or nullptr. - static int pointer_down_widget_x; ///< Pointer x position on down event, relative to the captured widget. - static int pointer_down_widget_y; ///< Pointer y position on down event, relative to the captured widget. - static int pointer_move_widget_x; ///< Pointer x position on last pointer event, relative to the captured widget (if - ///< any) or hovered widget. - static int pointer_move_widget_y; ///< Pointer y position on last pointer event, relative to the captured widget (if - ///< any) or hovered widget. - static bool cancel_click; ///< true if the pointer up event should not generate a click event. - static bool - update_widget_states; ///< true if something has called invalidateStates() and it still hasn't been updated. - static bool - update_skin_states; ///< true if something has called invalidateSkinStates() and skin still hasn't been updated. - static bool show_focus_state; ///< true if the focused state should be painted automatically. - struct TOUCH_INFO { - TBWidget *hovered_widget; ///< The currently hovered widget, or nullptr. - TBWidget *captured_widget; ///< The currently captured widget, or nullptr. - int down_widget_x; ///< Touch x position on down event, relative to the captured widget. - int down_widget_y; ///< Touch y position on down event, relative to the captured widget. - int move_widget_x; ///< Touch x position on last touch event, relative to the captured widget. - int move_widget_y; ///< Touch y position on last touch event, relative to the captured widget. - }; - /** Return TOUCH_INFO for the given id, or nullptr if no touch is active for that id. */ - static TOUCH_INFO *getTouchInfo(uint32_t id); - -private: - /** Return this widget or the nearest parent that is scrollable - in the given axis, or nullptr if there is none. */ - TBWidget *findScrollableWidget(bool scrollX, bool scrollY); - TBScroller *findStartedScroller(); - TBScroller *getReadyScroller(bool scrollX, bool scrollY); - TBWidget *getWidgetByIDInternal(const TBID &id, TB_TYPE_ID type_id = nullptr); - void invokeSkinUpdatesInternal(bool force_update); - void invokeProcessInternal(); - static void setHoveredWidget(TBWidget *widget, bool touch); - static void setCapturedWidget(TBWidget *widget); - void handlePanningOnMove(int x, int y); - void startLongClickTimer(BUTTON_TYPE type); - void stopLongClickTimer(); - friend class TBLongClickTimer; - void maybeInvokeLongClickOrContextMenu(BUTTON_TYPE type); - /** Returns the opacity for this widget multiplied with its skin opacity and state opacity. */ - float calculateOpacityInternal(WIDGET_STATE state, TBSkinElement *skin_element) const; -}; - -inline bool TBWidgetEvent::isAny(const tb::TBID &id) const { - return target->getID() == id || ref_id == id; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets_common.cpp b/src/modules/ui/turbobadger/tb/tb_widgets_common.cpp deleted file mode 100644 index a34efae32..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets_common.cpp +++ /dev/null @@ -1,784 +0,0 @@ -/** - * @file - */ - -#include "tb_widgets_common.h" -#include "core/Assert.h" -#include "core/Var.h" -#include "command/Command.h" -#include "command/CommandHandler.h" -#include "tb_font_renderer.h" -#include "tb_system.h" -#include "tb_widgets_listener.h" - -namespace tb { - -TBWidgetString::TBWidgetString() : m_text_align(TB_TEXT_ALIGN_CENTER), m_width(0), m_height(0) { -} - -void TBWidgetString::validatCachedSize(TBWidget *widget) { - const TBFontDescription fd = widget->getCalculatedFontDescription(); - if ((m_height == 0) || fd != m_fd) { - m_fd = fd; - TBFontFace *font = g_font_manager->getFontFace(fd); - m_width = font->getStringWidth(m_text.c_str(), m_text.size()); - m_height = font->getHeight(); - } -} - -int TBWidgetString::getWidth(TBWidget *widget) { - validatCachedSize(widget); - return m_width; -} - -int TBWidgetString::getHeight(TBWidget *widget) { - validatCachedSize(widget); - return m_height; -} - -bool TBWidgetString::setText(const char *text) { - // Invalidate cache - m_height = 0; - m_text = text; - return true; -} - -void TBWidgetString::paint(TBWidget *widget, const TBRect &rect, const TBColor &color) { - validatCachedSize(widget); - TBFontFace *font = widget->getFont(); - - int x = rect.x; - if (m_text_align == TB_TEXT_ALIGN_RIGHT) { - x += rect.w - m_width; - } else if (m_text_align == TB_TEXT_ALIGN_CENTER) { - x += Max(0, (rect.w - m_width) / 2); - } - int y = rect.y + (rect.h - m_height) / 2; - - if (m_width <= rect.w) { - font->drawString(x, y, color, m_text.c_str(), m_text.size()); - } else { - // There's not enough room for the entire string - // so cut it off and end with ellipsis (...) - - // const char *end = "…"; // 2026 HORIZONTAL ELLIPSIS - // Some fonts seem to render ellipsis a lot uglier than three dots. - const char *end = "..."; - - int endw = font->getStringWidth(end, SDL_strlen(end)); - int startw = 0; - int startlen = 0; - while (m_text.c_str()[startlen] != 0) { - int new_startw = font->getStringWidth(m_text.c_str(), startlen); - if (new_startw + endw > rect.w) { - break; - } - startw = new_startw; - startlen++; - } - startlen = Max(0, startlen - 1); - font->drawString(x, y, color, m_text.c_str(), startlen); - font->drawString(x + startw, y, color, end, SDL_strlen(end)); - } -} - -/** This value on m_cached_text_width means it needs to be updated again. */ -#define UPDATE_TEXT_WIDTH_CACHE (-1) - -TBTextField::TBTextField() : m_cached_text_width(UPDATE_TEXT_WIDTH_CACHE), m_squeezable(false) { - setSkinBg(TBIDC("TBTextField"), WIDGET_INVOKE_INFO_NO_CALLBACKS); -} - -bool TBTextField::setText(const char *text) { - if (m_text.equals(text)) { - return true; - } - m_cached_text_width = UPDATE_TEXT_WIDTH_CACHE; - invalidate(); - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); - return m_text.setText(text); -} - -void TBTextField::setSqueezable(bool squeezable) { - if (squeezable == m_squeezable) { - return; - } - m_squeezable = squeezable; - invalidate(); - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); -} - -PreferredSize TBTextField::onCalculatePreferredContentSize(const SizeConstraints &constraints) { - PreferredSize ps; - if (m_cached_text_width == UPDATE_TEXT_WIDTH_CACHE) { - m_cached_text_width = m_text.getWidth(this); - } - ps.pref_w = m_cached_text_width; - ps.pref_h = ps.min_h = m_text.getHeight(this); - // If gravity pull both up and down, use default max_h (grow as much as possible). - // Otherwise it makes sense to only accept one line height. - if (!(((getGravity() & WIDGET_GRAVITY_TOP) != 0U) && ((getGravity() & WIDGET_GRAVITY_BOTTOM) != 0U))) { - ps.max_h = ps.pref_h; - } - if (!m_squeezable) { - ps.min_w = ps.pref_w; - } - return ps; -} - -void TBTextField::onFontChanged() { - m_cached_text_width = UPDATE_TEXT_WIDTH_CACHE; - invalidateLayout(INVALIDATE_LAYOUT_RECURSIVE); -} - -void TBTextField::onPaint(const PaintProps &paintProps) { - m_text.paint(this, getPaddingRect(), paintProps.text_color); -} - -const int auto_click_first_delay = 500; -const int auto_click_repeat_delay = 100; - -TBButton::TBButton() : m_auto_repeat_click(false), m_toggle_mode(false) { - setIsFocusable(true); - setClickByKey(true); - setSkinBg(TBIDC("TBButton"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - addChild(&m_layout); - // Set the textfield gravity to all, even though it would display the same with default gravity. - // This will make the buttons layout expand if there is space available, without forcing the parent - // layout to grow to make the space available. - m_textfield.setGravity(WIDGET_GRAVITY_ALL); - m_layout.addChild(&m_textfield); - m_layout.setRect(getPaddingRect()); - m_layout.setGravity(WIDGET_GRAVITY_ALL); - m_layout.setPaintOverflowFadeout(false); -} - -TBButton::~TBButton() { - m_layout.removeChild(&m_textfield); - removeChild(&m_layout); -} - -bool TBButton::setText(const char *text) { - bool ret = m_textfield.setText(text); - updateTextFieldVisibility(); - return ret; -} - -void TBButton::setValue(int value) { - if (value == getValue()) { - return; - } - setState(WIDGET_STATE_PRESSED, value != 0); - - if (canToggle()) { - // Invoke a changed event. - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); - } - - if (value != 0 && getGroupID() != 0U) { - TBRadioCheckBox::updateGroupWidgets(this); - } -} - -int TBButton::getValue() const { - return static_cast(getState(WIDGET_STATE_PRESSED)); -} - -void TBButton::onCaptureChanged(bool captured) { - if (captured && m_auto_repeat_click) { - postMessageDelayed(TBIDC("auto_click"), nullptr, auto_click_first_delay); - } else if (!captured) { - if (TBMessage *msg = getMessageByID(TBIDC("auto_click"))) { - deleteMessage(msg); - } - } -} - -void TBButton::onProcess() { - TBWidget::onProcess(); - if (!_var || !_var->isDirty()) { - return; - } - setValue(_var->intVal()); - _var->markClean(); -} - -void TBButton::onSkinChanged() { - m_layout.setRect(getPaddingRect()); -} - -bool TBButton::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_CLICK && ev.target == this) { - if (canToggle()) { - TBWidgetSafePointer this_widget(this); - - // Toggle the value, if it's not a grouped widget with value on. - if (!((getGroupID() != 0U) && (getValue() != 0))) { - setValue(static_cast(static_cast(getValue()) == 0)); - } - - if (this_widget.get() == nullptr) { - return true; // We got removed so we actually handled this event. - } - - // Intentionally don't return true for this event. We want it to continue propagating. - } - if (_var) { - _var->setVal(getValue()); - } - if (!_command.empty()) { - if (canToggle()) { - execute("%s %i", _command.c_str(), getValue()); - } else { - execute("%s", _command.c_str()); - } - } - } - return TBWidget::onEvent(ev); -} - -void TBButton::onMessageReceived(TBMessage *msg) { - if (msg->message == TBIDC("auto_click")) { - core_assert(captured_widget == this); - if (!cancel_click && (getHitStatus(pointer_move_widget_x, pointer_move_widget_y) != 0U)) { - TBWidgetEvent ev(EVENT_TYPE_CLICK, pointer_move_widget_x, pointer_move_widget_y, TB_TOUCH); - captured_widget->invokeEvent(ev); - } - if (auto_click_repeat_delay != 0) { - postMessageDelayed(TBIDC("auto_click"), nullptr, auto_click_repeat_delay); - } - } -} - -WIDGET_HIT_STATUS TBButton::getHitStatus(int x, int y) { - // Never hit any of the children to the button. We always want to the button itself. - return TBWidget::getHitStatus(x, y) != 0U ? WIDGET_HIT_STATUS_HIT_NO_CHILDREN : WIDGET_HIT_STATUS_NO_HIT; -} - -void TBButton::updateTextFieldVisibility() { - // Auto-collapse the textfield if the text is empty and there are other - // widgets added apart from the textfield. This removes the extra spacing - // added between the textfield and the other widget. - bool collapse_textfield = m_textfield.isEmpty() && m_layout.getFirstChild() != m_layout.getLastChild(); - m_textfield.setVisibility(collapse_textfield ? WIDGET_VISIBILITY_GONE : WIDGET_VISIBILITY_VISIBLE); -} - -void TBButton::ButtonLayout::onChildAdded(TBWidget *child) { - static_cast(getParent())->updateTextFieldVisibility(); -} - -void TBButton::ButtonLayout::onChildRemove(TBWidget *child) { - static_cast(getParent())->updateTextFieldVisibility(); -} - -TBClickLabel::TBClickLabel() { - addChild(&m_layout); - m_layout.addChild(&m_textfield); - m_layout.setRect(getPaddingRect()); - m_layout.setGravity(WIDGET_GRAVITY_ALL); - m_layout.setLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); -} - -TBClickLabel::~TBClickLabel() { - m_layout.removeChild(&m_textfield); - removeChild(&m_layout); -} - -bool TBClickLabel::onEvent(const TBWidgetEvent &ev) { - // Get a widget from the layout that isn't the textfield, or just bail out - // if we only have the textfield. - if (m_layout.getFirstChild() == m_layout.getLastChild()) { - return false; - } - TBWidget *click_target = - (m_layout.getFirstChild() == &m_textfield ? m_layout.getLastChild() : m_layout.getFirstChild()); - // Invoke the event on it, as if it was invoked on the target itself. - if ((click_target != nullptr) && ev.target != click_target) { - // Focus the target if we clicked the label. - if (ev.type == EVENT_TYPE_CLICK) { - click_target->setFocus(WIDGET_FOCUS_REASON_POINTER); - } - - // Sync our pressed state with the click target. Special case for when we're just about to - // lose it ourself (pointer is being released). - bool pressed_state = (ev.target->getAutoState() & WIDGET_STATE_PRESSED) != 0U; - if (ev.type == EVENT_TYPE_POINTER_UP || ev.type == EVENT_TYPE_CLICK) { - pressed_state = false; - } - - click_target->setState(WIDGET_STATE_PRESSED, pressed_state); - - TBWidgetEvent target_ev(ev.type, ev.target_x - click_target->getRect().x, - ev.target_y - click_target->getRect().y, ev.button_type, ev.modifierkeys); - return click_target->invokeEvent(target_ev); - } - return false; -} - -PreferredSize TBSkinImage::onCalculatePreferredSize(const SizeConstraints &constraints) { - PreferredSize ps = TBWidget::onCalculatePreferredSize(constraints); - // FIX: Make it stretched proportionally if shrunk. - ps.max_w = ps.pref_w; - ps.max_h = ps.pref_h; - return ps; -} - -TBSeparator::TBSeparator() { - setSkinBg(TBIDC("TBSeparator"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - setState(WIDGET_STATE_DISABLED, true); -} - -// FIX: Add spin_speed to skin! -// FIX: Make it post messages only if visible -const int spin_speed = 1000 / 30; ///< How fast should the spinner animation animate. - -TBProgressSpinner::TBProgressSpinner() : m_value(0), m_frame(0) { - setSkinBg(TBIDC("TBProgressSpinner"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_skin_fg.set(TBIDC("TBProgressSpinner.fg")); -} - -void TBProgressSpinner::setValue(int value) { - if (value == m_value) { - return; - } - invalidateSkinStates(); - core_assert(value >= 0); // If this happens, you probably have unballanced Begin/End calls. - m_value = value; - if (value > 0) { - // Start animation - if (getMessageByID(TBID(1)) == nullptr) { - m_frame = 0; - postMessageDelayed(TBID(1), nullptr, spin_speed); - } - } else { - // Stop animation - if (TBMessage *msg = getMessageByID(TBID(1))) { - deleteMessage(msg); - } - } -} - -void TBProgressSpinner::onPaint(const PaintProps &paintProps) { - if (isRunning()) { - TBSkinElement *e = g_tb_skin->getSkinElement(m_skin_fg); - if ((e != nullptr) && (e->bitmap != nullptr)) { - int size = e->bitmap->height(); - int num_frames = e->bitmap->width() / e->bitmap->height(); - int current_frame = m_frame % num_frames; - g_renderer->drawBitmap(getPaddingRect(), TBRect(current_frame * size, 0, size, size), e->bitmap); - } - } -} - -void TBProgressSpinner::onMessageReceived(TBMessage *msg) { - m_frame++; - invalidate(); - // Keep animation running - postMessageDelayed(TBID(1), nullptr, spin_speed); -} - -TBRadioCheckBox::TBRadioCheckBox() : m_value(0) { - setIsFocusable(true); - setClickByKey(true); -} - -// static -void TBRadioCheckBox::updateGroupWidgets(TBWidget *newLeader) { - core_assert(newLeader->getValue() && newLeader->getGroupID()); - - // Find the group root widget. - TBWidget *group = newLeader; - while ((group != nullptr) && !group->getIsGroupRoot() && (group->getParent() != nullptr)) { - group = group->getParent(); - } - - for (TBWidget *child = group; child != nullptr; child = child->getNextDeep(group)) { - if (child != newLeader && child->getGroupID() == newLeader->getGroupID()) { - child->setValue(0); - } - } -} - -void TBRadioCheckBox::setValue(int value) { - if (m_value == value) { - return; - } - m_value = value; - if (_var) { - _var->setVal(value != 0); - } - if (!_command.empty()) { - execute("%s %i", _command.c_str(), m_value); - } - setState(WIDGET_STATE_SELECTED, value != 0); - - if (value != 0 && getGroupID() != 0U) { - updateGroupWidgets(this); - } - - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); -} - -PreferredSize TBRadioCheckBox::onCalculatePreferredSize(const SizeConstraints &constraints) { - PreferredSize ps = TBWidget::onCalculatePreferredSize(constraints); - ps.min_w = ps.max_w = ps.pref_w; - ps.min_h = ps.max_h = ps.pref_h; - return ps; -} - -void TBRadioCheckBox::onProcess() { - TBWidget::onProcess(); - if (!_var || !_var->isDirty()) { - return; - } - setValue(_var->intVal()); - _var->markClean(); -} - -bool TBRadioCheckBox::onEvent(const TBWidgetEvent &ev) { - if (ev.target == this && ev.type == EVENT_TYPE_CLICK) { - // Toggle the value, if it's not a grouped widget with value on. - if (!((getGroupID() != 0U) && (getValue() != 0))) { - setValue(static_cast(static_cast(getValue()) == 0)); - } - } - return false; -} - -TBScrollBar::TBScrollBar() - : m_axis(AXIS_Y) ///< Make setAxis below always succeed and set the skin - , - m_value(0), m_min(0), m_max(1), m_visible(1), m_to_pixel_factor(0) { - setAxis(AXIS_X); - addChild(&m_handle); -} - -TBScrollBar::~TBScrollBar() { - removeChild(&m_handle); -} - -void TBScrollBar::setAxis(AXIS axis) { - if (axis == m_axis) { - return; - } - m_axis = axis; - if (axis == AXIS_X) { - setSkinBg(TBIDC("TBScrollBarBgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_handle.setSkinBg(TBIDC("TBScrollBarFgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - } else { - setSkinBg(TBIDC("TBScrollBarBgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_handle.setSkinBg(TBIDC("TBScrollBarFgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - } - invalidate(); -} - -void TBScrollBar::setLimits(double min, double max, double visible) { - max = Max(min, max); - visible = Max(visible, 0.0); - if (min == m_min && max == m_max && m_visible == visible) { - return; - } - m_min = min; - m_max = max; - m_visible = visible; - setValueDouble(m_value); - - // If we're currently dragging the scrollbar handle, convert the down point - // to root and then back after the applying the new limit. - // This prevents sudden jumps to unexpected positions when scrolling. - if (captured_widget == &m_handle) { - m_handle.convertToRoot(pointer_down_widget_x, pointer_down_widget_y); - } - - updateHandle(); - - if (captured_widget == &m_handle) { - m_handle.convertFromRoot(pointer_down_widget_x, pointer_down_widget_y); - } -} - -void TBScrollBar::setValueDouble(double value) { - value = Clamp(value, m_min, m_max); - if (value == m_value) { - return; - } - m_value = value; - - updateHandle(); - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); -} - -bool TBScrollBar::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == &m_handle) { - if (m_to_pixel_factor > 0) { - int dx = ev.target_x - pointer_down_widget_x; - int dy = ev.target_y - pointer_down_widget_y; - double delta_val = (m_axis == AXIS_X ? dx : dy) / m_to_pixel_factor; - setValueDouble(m_value + delta_val); - } - return true; - } - if (ev.type == EVENT_TYPE_POINTER_MOVE && ev.target == this) { - return true; - } - if (ev.type == EVENT_TYPE_POINTER_DOWN && ev.target == this) { - bool after_handle = - (m_axis == AXIS_X ? ev.target_x > m_handle.getRect().x : ev.target_y > m_handle.getRect().y); - setValueDouble(m_value + (after_handle ? m_visible : -m_visible)); - return true; - } - if (ev.type == EVENT_TYPE_WHEEL) { - double old_val = m_value; - setValueDouble(m_value + ev.delta_y * TBSystem::getPixelsPerLine()); - return m_value != old_val; - } - return false; -} - -void TBScrollBar::updateHandle() { - // Calculate the mover size and position - bool horizontal = m_axis == AXIS_X; - int available_pixels = horizontal ? getRect().w : getRect().h; - int min_thickness_pixels = Min(getRect().h, getRect().w); - - int visible_pixels = available_pixels; - - if (m_max - m_min > 0 && m_visible > 0) { - double visible_proportion = m_visible / (m_visible + m_max - m_min); - visible_pixels = (int)(visible_proportion * available_pixels); - - // Limit the size of the indicator to the slider thickness so that it doesn't - // become too tiny when the visible proportion is very small. - visible_pixels = Max(visible_pixels, min_thickness_pixels); - - m_to_pixel_factor = (double)(available_pixels - visible_pixels) / (m_max - m_min) /*+ 0.5*/; - } else { - m_to_pixel_factor = 0; - - // If we can't scroll anything, make the handle invisible - visible_pixels = 0; - } - - int pixel_pos = (int)(m_value * m_to_pixel_factor); - - TBRect rect; - if (horizontal) { - rect.set(pixel_pos, 0, visible_pixels, getRect().h); - } else { - rect.set(0, pixel_pos, getRect().w, visible_pixels); - } - - m_handle.setRect(rect); -} - -void TBScrollBar::onResized(int oldW, int oldH) { - updateHandle(); -} - -TBSlider::TBSlider() - : m_axis(AXIS_Y) ///< Make setAxis below always succeed and set the skin - , - m_value(0), m_min(0), m_max(1), m_to_pixel_factor(0) { - setIsFocusable(true); - setAxis(AXIS_X); - addChild(&m_handle); -} - -TBSlider::~TBSlider() { - removeChild(&m_handle); -} - -void TBSlider::setAxis(AXIS axis) { - if (axis == m_axis) { - return; - } - m_axis = axis; - if (axis == AXIS_X) { - setSkinBg(TBIDC("TBSliderBgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_handle.setSkinBg(TBIDC("TBSliderFgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - } else { - setSkinBg(TBIDC("TBSliderBgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - m_handle.setSkinBg(TBIDC("TBSliderFgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - } - invalidate(); -} - -void TBSlider::setLimits(double min, double max) { - min = Min(min, max); - if (min == m_min && max == m_max) { - return; - } - m_min = min; - m_max = max; - setValueDouble(m_value); - updateHandle(); -} - -void TBSlider::onProcess() { - TBWidget::onProcess(); - if (!_var || !_var->isDirty()) { - return; - } - setValue(_var->intVal()); - _var->markClean(); -} - -void TBSlider::setValueDouble(double value) { - value = Clamp(value, m_min, m_max); - if (value == m_value) { - return; - } - m_value = value; - if (_var) { - _var->setVal((float)value); - } - if (!_command.empty()) { - execute("%s %f", _command.c_str(), m_value); - } - - updateHandle(); - TBWidgetEvent ev(EVENT_TYPE_CHANGED); - invokeEvent(ev); -} - -bool TBSlider::onEvent(const TBWidgetEvent &ev) { - if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == &m_handle) { - if (m_to_pixel_factor > 0) { - int dx = ev.target_x - pointer_down_widget_x; - int dy = ev.target_y - pointer_down_widget_y; - double delta_val = (m_axis == AXIS_X ? dx : -dy) / m_to_pixel_factor; - setValueDouble(m_value + delta_val); - } - return true; - } - if (ev.type == EVENT_TYPE_WHEEL) { - double old_val = m_value; - double step = (m_axis == AXIS_X ? getSmallStep() : -getSmallStep()); - setValueDouble(m_value + step * ev.delta_y); - return m_value != old_val; - } - if (ev.type == EVENT_TYPE_KEY_DOWN) { - double step = (m_axis == AXIS_X ? getSmallStep() : -getSmallStep()); - if (ev.special_key == TB_KEY_LEFT || ev.special_key == TB_KEY_UP) { - setValueDouble(getValueDouble() - step); - } else if (ev.special_key == TB_KEY_RIGHT || ev.special_key == TB_KEY_DOWN) { - setValueDouble(getValueDouble() + step); - } else { - return false; - } - return true; - } - if (ev.type == EVENT_TYPE_KEY_UP) { - if (ev.special_key == TB_KEY_LEFT || ev.special_key == TB_KEY_UP || ev.special_key == TB_KEY_RIGHT || - ev.special_key == TB_KEY_DOWN) { - return true; - } - } - return TBWidget::onEvent(ev); -} - -void TBSlider::updateHandle() { - // Calculate the handle position - bool horizontal = m_axis == AXIS_X; - int available_pixels = horizontal ? getRect().w : getRect().h; - - TBRect rect; - if (m_max - m_min > 0) { - PreferredSize ps = m_handle.getPreferredSize(); - int handle_pixels = horizontal ? ps.pref_w : ps.pref_h; - m_to_pixel_factor = (double)(available_pixels - handle_pixels) / (m_max - m_min) /*+ 0.5*/; - - int pixel_pos = (int)((m_value - m_min) * m_to_pixel_factor); - - if (horizontal) { - rect.set(pixel_pos, (getRect().h - ps.pref_h) / 2, ps.pref_w, ps.pref_h); - } else { - rect.set((getRect().w - ps.pref_w) / 2, getRect().h - handle_pixels - pixel_pos, ps.pref_w, ps.pref_h); - } - } else { - m_to_pixel_factor = 0; - } - - m_handle.setRect(rect); -} - -void TBSlider::onResized(int oldW, int oldH) { - updateHandle(); -} - -TBContainer::TBContainer() { - setSkinBg(TBIDC("TBContainer"), WIDGET_INVOKE_INFO_NO_CALLBACKS); -} - -TBMover::TBMover() { - setSkinBg(TBIDC("TBMover"), WIDGET_INVOKE_INFO_NO_CALLBACKS); -} - -bool TBMover::onEvent(const TBWidgetEvent &ev) { - TBWidget *target = getParent(); - if (target == nullptr) { - return false; - } - if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == this) { - int dx = ev.target_x - pointer_down_widget_x; - int dy = ev.target_y - pointer_down_widget_y; - TBRect rect = target->getRect().offset(dx, dy); - if (target->getParent() != nullptr) { - // Apply limit. - rect.x = Clamp(rect.x, -pointer_down_widget_x, target->getParent()->getRect().w - pointer_down_widget_x); - rect.y = Clamp(rect.y, -pointer_down_widget_y, target->getParent()->getRect().h - pointer_down_widget_y); - } - target->setRect(rect); - return true; - } - return false; -} - -TBResizer::TBResizer() { - setSkinBg(TBIDC("TBResizer"), WIDGET_INVOKE_INFO_NO_CALLBACKS); -} - -WIDGET_HIT_STATUS TBResizer::getHitStatus(int x, int y) { - // Shave off some of the upper left diagonal half from the hit area. - const int extra_hit_area = 3; - if (x < getRect().w - y - extra_hit_area) { - return WIDGET_HIT_STATUS_NO_HIT; - } - return TBWidget::getHitStatus(x, y); -} - -bool TBResizer::onEvent(const TBWidgetEvent &ev) { - TBWidget *target = getParent(); - if (target == nullptr) { - return false; - } - if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == this) { - int dx = ev.target_x - pointer_down_widget_x; - int dy = ev.target_y - pointer_down_widget_y; - TBRect rect = target->getRect(); - rect.w += dx; - rect.h += dy; - // Apply limit. We should not use minimum size since we can squeeze - // the layout much more, and provide scroll/pan when smaller. - rect.w = Max(rect.w, 50); - rect.h = Max(rect.h, 50); - target->setRect(rect); - } else { - return false; - } - return true; -} - -TBDimmer::TBDimmer() { - setSkinBg(TBIDC("TBDimmer"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - setGravity(WIDGET_GRAVITY_ALL); -} - -void TBDimmer::onAdded() { - setRect(TBRect(0, 0, getParent()->getRect().w, getParent()->getRect().h)); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets_common.h b/src/modules/ui/turbobadger/tb/tb_widgets_common.h deleted file mode 100644 index e6dd6cf07..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets_common.h +++ /dev/null @@ -1,546 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "core/Var.h" -#include "tb_layout.h" -#include "tb_msg.h" -#include "tb_widgets.h" - -namespace tb { - -/** TB_TEXT_ALIGN specifies horizontal text alignment */ -enum TB_TEXT_ALIGN { - TB_TEXT_ALIGN_LEFT, ///< Aligned left - TB_TEXT_ALIGN_RIGHT, ///< Aligned right - TB_TEXT_ALIGN_CENTER ///< Aligned center -}; - -/** TBWidgetString holds a string that can be painted as one line with the set alignment. */ -class TBWidgetString { -public: - TBWidgetString(); - - void paint(TBWidget *widget, const TBRect &rect, const TBColor &color); - - int getWidth(TBWidget *widget); - int getHeight(TBWidget *widget); - - bool setText(const char *text); - bool getText(core::String &text) const { - text = m_text; - return true; - } - - bool isEmpty() const { - return m_text.empty(); - } - bool equals(const char *str) const { - return m_text.equals(str); - } - - /** Set which alignment the text should have if the space - given when painting is larger than the text. */ - void setTextAlign(TB_TEXT_ALIGN align) { - m_text_align = align; - } - TB_TEXT_ALIGN getTextAlign() const { - return m_text_align; - } - -private: - core::String m_text; - TB_TEXT_ALIGN m_text_align; - // Cached data - int m_width, m_height; - TBFontDescription m_fd; - void validatCachedSize(TBWidget *widget); -}; - -/** TBTextField is a one line text field that is not editable. */ - -class TBTextField : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBTextField, TBWidget); - - TBTextField(); - - /** Set the text of the text field. */ - virtual bool setText(const char *text) override; - bool setText(const core::String& text) { - return setText(text.c_str()); - } - virtual bool getText(core::String &text) override { - return m_text.getText(text); - } - using TBWidget::getText; ///< Make all versions in base class available. - - bool isEmpty() const { - return m_text.isEmpty(); - } - - /** Set which alignment the text should have if the space - given when painting is larger than the text. */ - void setTextAlign(TB_TEXT_ALIGN align) { - m_text.setTextAlign(align); - } - TB_TEXT_ALIGN getTextAlign() { - return m_text.getTextAlign(); - } - - /** Set if this text field should be allowed to squeeze below its - preferred size. If squeezable it may shrink to width 0. */ - void setSqueezable(bool squeezable); - bool getSqueezable() const { - return m_squeezable; - } - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override; - virtual void onFontChanged() override; - virtual void onPaint(const PaintProps &paint_props) override; - -protected: - TBWidgetString m_text; - int m_cached_text_width; ///< Cached width of m_text - bool m_squeezable; -}; - -/** TBButton is a regular button widget with auto repeat, toggle and group capabilities. - Has a text field in its internal layout by default. Other widgets can be added - under GetContentRoot(). */ - -class TBButton : public TBWidget, protected TBMessageHandler { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBButton, TBWidget); - - TBButton(); - ~TBButton(); - - /** Set along which axis the content should layouted (If the button has more content than the text) */ - virtual void setAxis(AXIS axis) override { - m_layout.setAxis(axis); - } - virtual AXIS getAxis() const override { - return m_layout.getAxis(); - } - - /** Set if the text field should be allowed to squeeze below its - preferred size. If squeezable it may shrink to width 0. */ - void setSqueezable(bool squeezable) { - m_textfield.setSqueezable(squeezable); - } - bool getSqueezable() const { - return m_textfield.getSqueezable(); - } - - /** Set to true if the button should fire repeatedly while pressed. */ - void setAutoRepeat(bool autoRepeatClick) { - m_auto_repeat_click = autoRepeatClick; - } - bool getAutoRepeat() const { - return m_auto_repeat_click; - } - - /** Set to true if the button should toggle on and off, instead of just fire - click events. When it's on, it will have value 1 pressed state. */ - void setToggleMode(bool toggleModeOn) { - m_toggle_mode = toggleModeOn; - } - bool getToggleMode() const { - return m_toggle_mode; - } - - /** Set the text of the button. */ - virtual bool setText(const char *text) override; - virtual bool getText(core::String &text) override { - return m_textfield.getText(text); - } - using TBWidget::getText; ///< Make all versions in base class available. - using TBWidget::setText; ///< Make all versions in base class available. - - virtual void setValue(int value) override; - virtual int getValue() const override; - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual void onCaptureChanged(bool captured) override; - virtual void onProcess() override; - virtual void onSkinChanged() override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual WIDGET_HIT_STATUS getHitStatus(int x, int y) override; - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override { - return m_layout.getPreferredSize(); - } - - virtual TBWidget *getContentRoot() override { - return &m_layout; - } - - // == TBMessageHandler ============================================================== - virtual void onMessageReceived(TBMessage *msg) override; - -protected: - void updateTextFieldVisibility(); - bool canToggle() { - return m_toggle_mode || (getGroupID() != 0U); - } - class ButtonLayout : public TBLayout { - virtual void onChildAdded(TBWidget *child) override; - virtual void onChildRemove(TBWidget *child) override; - }; - ButtonLayout m_layout; - TBTextField m_textfield; - bool m_auto_repeat_click; - bool m_toggle_mode; - core::VarPtr _var; - core::String _command; -}; - -/** TBClickLabel has a text field in its internal layout by default. Pointer input on the - text field will be redirected to another child widget (that you add) to it. - Typically useful for creating check boxes, radio buttons with labels. */ - -class TBClickLabel : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBClickLabel, TBWidget); - - TBClickLabel(); - ~TBClickLabel(); - - /** Set along which axis the content should layouted (If the label has more content than the text) */ - virtual void setAxis(AXIS axis) override { - m_layout.setAxis(axis); - } - virtual AXIS getAxis() const override { - return m_layout.getAxis(); - } - - /** Set the text of the label. */ - virtual bool setText(const char *text) override { - return m_textfield.setText(text); - } - virtual bool getText(core::String &text) override { - return m_textfield.getText(text); - } - using TBWidget::getText; ///< Make all versions in base class available. - - virtual PreferredSize onCalculatePreferredContentSize(const SizeConstraints &constraints) override { - return m_layout.getPreferredSize(); - } - - virtual TBWidget *getContentRoot() override { - return &m_layout; - } - - virtual bool onEvent(const TBWidgetEvent &ev) override; - -protected: - TBLayout m_layout; - TBTextField m_textfield; -}; - -/** TBSkinImage is a widget showing a skin element, constrained in size to its skin. - If you need to load and show images dynamically (i.e. not always loaded as the skin), - you can use TBImageWidget. */ - -class TBSkinImage : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSkinImage, TBWidget); - - TBSkinImage() { - } - TBSkinImage(const TBID &skinBg) { - setSkinBg(skinBg); - } - - virtual PreferredSize onCalculatePreferredSize(const SizeConstraints &constraints) override; -}; - -/** TBSeparator is a widget only showing a skin. - It is disabled by default. */ -class TBSeparator : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSeparator, TBWidget); - - TBSeparator(); -}; - -/** TBProgressSpinner is a animation that is running while its value is 1. - Typically used to indicate that the application is working. */ -class TBProgressSpinner : public TBWidget, protected TBMessageHandler { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBProgressSpinner, TBWidget); - - TBProgressSpinner(); - - /** Return true if the animation is running. */ - bool isRunning() { - return m_value > 0; - } - - /** Begin/End are used to start or stop the animation in a incremental way. - If several tasks may activate the same spinner, calling Begin/End instead - of using setValue, so it will keep running as long as any source wants it to. */ - void begin() { - setValue(getValue() + 1); - } - void end() { - setValue(getValue() - 1); - } - - /** Setting the value to 1 will start the spinner. - Setting it to 0 will stop it. */ - virtual void setValue(int value) override; - virtual int getValue() const override { - return m_value; - } - - virtual void onPaint(const PaintProps &paintProps) override; - - // == TBMessageHandler ============================================================== - virtual void onMessageReceived(TBMessage *msg) override; - -protected: - int m_value; - int m_frame; - TBID m_skin_fg; -}; - -/** TBRadioCheckBox has shared functionality for TBCheckBox and TBRadioButton. */ -class TBRadioCheckBox : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBRadioCheckBox, TBWidget); - - TBRadioCheckBox(); - - virtual void setValue(int value) override; - virtual int getValue() const override { - return m_value; - } - - virtual PreferredSize onCalculatePreferredSize(const SizeConstraints &constraints) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onInflate(const INFLATE_INFO &info) override; - virtual void onProcess() override; - - /** Make sure all widgets sharing the same group as new_leader are set to value 0. */ - static void updateGroupWidgets(TBWidget *new_leader); - -protected: - int m_value; - core::VarPtr _var; - core::String _command; -}; - -/** TBCheckBox is a box toggling a check mark on click. - For a labeled checkbox, use a TBClickLabel containing a TBCheckBox. */ -class TBCheckBox : public TBRadioCheckBox { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBCheckBox, TBRadioCheckBox); - - TBCheckBox() { - setSkinBg(TBIDC("TBCheckBox"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - } -}; - -/** TBRadioButton is a button which unselects other radiobuttons of the same - group number when clicked. - For a labeled radio button, use a TBClickLabel containing a TBRadioButton. */ -class TBRadioButton : public TBRadioCheckBox { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBRadioButton, TBRadioCheckBox); - - TBRadioButton() { - setSkinBg(TBIDC("TBRadioButton"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - } -}; - -/** TBScrollBar is a scroll bar in the given axis. */ - -class TBScrollBar : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBScrollBar, TBWidget); - - TBScrollBar(); - ~TBScrollBar(); - - /** Set along which axis the scrollbar should scroll */ - virtual void setAxis(AXIS axis) override; - virtual AXIS getAxis() const override { - return m_axis; - } - - /** Set the min, max limits for the scrollbar. - The visible parameter is how much of the range that is visible. - When this is called, the scrollbar might change value and invoke if the current value is - outside of the new limits. */ - void setLimits(double min, double max, double visible); - - /** Return true if the scrollbar has anywhere to go with the current limits. */ - bool canScroll() const { - return m_visible > 0; - } - - /** Return true if the scrollbar can scroll in the positive direction with its current limits. */ - bool canScrollPositive() const { - return m_value < m_max; - } - - /** Return true if the scrollbar can scroll in the negative direction with its current limits. */ - bool canScrollNegative() const { - return m_value > m_min; - } - - double getMinValue() const { - return m_min; - } - double getMaxValue() const { - return m_max; - } - double getVisible() const { - return m_visible; - } - - /** Same as setValue, but with double precision. */ - virtual void setValueDouble(double value) override; - virtual double getValueDouble() const override { - return m_value; - } - - virtual void setValue(int value) override { - setValueDouble(value); - } - virtual int getValue() const override { - return (int)getValueDouble(); - } - - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onResized(int oldW, int oldH) override; - -protected: - TBWidget m_handle; - AXIS m_axis; - double m_value; - double m_min, m_max, m_visible; - double m_to_pixel_factor; - void updateHandle(); -}; - -/** TBSlider is a horizontal or vertical slider for a number within a range. */ - -// FIX: Add a "track value" showing as a line within the track (to be used for buffering etc). -// FIX: Also add a auto track that keeps it up to date with value (default). -class TBSlider : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBSlider, TBWidget); - - TBSlider(); - ~TBSlider(); - - /** Set along which axis the scrollbar should scroll */ - virtual void setAxis(AXIS axis) override; - virtual AXIS getAxis() const override { - return m_axis; - } - - /** Set the min, max limits for the slider. */ - void setLimits(double min, double max); - - double getMinValue() const { - return m_min; - } - double getMaxValue() const { - return m_max; - } - - /** Get a small value (depending on the min and max limits) for stepping by f.ex. keyboard. */ - double getSmallStep() const { - return (m_max - m_min) / 100.0; - } - - /** Same as setValue, but with double precision. */ - virtual void setValueDouble(double value) override; - virtual double getValueDouble() const override { - return m_value; - } - - virtual void setValue(int value) override { - setValueDouble(value); - } - virtual int getValue() const override { - return (int)getValueDouble(); - } - - virtual void onProcess() override; - virtual void onInflate(const INFLATE_INFO &info) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onResized(int oldW, int oldH) override; - -protected: - TBWidget m_handle; - AXIS m_axis; - double m_value; - double m_min, m_max; - double m_to_pixel_factor; - core::VarPtr _var; - core::String _command; - void updateHandle(); -}; - -/** TBContainer is just a TBWidget with border and padding (using skin "TBContainer") */ -class TBContainer : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBContainer, TBWidget); - - TBContainer(); -}; - -/** TBMover is moving its parent widget when dragged. */ -class TBMover : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBMover, TBWidget); - - TBMover(); - - virtual bool onEvent(const TBWidgetEvent &ev) override; -}; - -/** TBResizer is a lower right corner resize grip. It will resize its parent widget. */ -class TBResizer : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBResizer, TBWidget); - - TBResizer(); - virtual WIDGET_HIT_STATUS getHitStatus(int x, int y) override; - virtual bool onEvent(const TBWidgetEvent &ev) override; -}; - -/** TBDimmer dim widgets in the background and block input. */ -class TBDimmer : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBDimmer, TBWidget); - - TBDimmer(); - - virtual void onAdded() override; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets_listener.cpp b/src/modules/ui/turbobadger/tb/tb_widgets_listener.cpp deleted file mode 100644 index d6bc99acb..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets_listener.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @file - */ - -#include "tb_widgets_listener.h" - -namespace tb { - -TBLinkListOf g_listeners; - -void TBWidgetListener::addGlobalListener(TBWidgetListener *listener) { - g_listeners.addLast(listener); -} - -void TBWidgetListener::removeGlobalListener(TBWidgetListener *listener) { - g_listeners.remove(listener); -} - -void TBWidgetListener::invokeWidgetDelete(TBWidget *widget) { - TBLinkListOf::Iterator global_i = g_listeners.iterateForward(); - TBLinkListOf::Iterator local_i = widget->m_listeners.iterateForward(); - while (TBWidgetListener *listener = local_i.getAndStep()) { - listener->onWidgetDelete(widget); - } - while (TBWidgetListenerGlobalLink *link = global_i.getAndStep()) { - static_cast(link)->onWidgetDelete(widget); - } -} - -bool TBWidgetListener::invokeWidgetDying(TBWidget *widget) { - bool handled = false; - TBLinkListOf::Iterator global_i = g_listeners.iterateForward(); - TBLinkListOf::Iterator local_i = widget->m_listeners.iterateForward(); - while (TBWidgetListener *listener = local_i.getAndStep()) { - handled |= listener->onWidgetDying(widget); - } - while (TBWidgetListenerGlobalLink *link = global_i.getAndStep()) { - handled |= static_cast(link)->onWidgetDying(widget); - } - return handled; -} - -void TBWidgetListener::invokeWidgetAdded(TBWidget *parent, TBWidget *child) { - TBLinkListOf::Iterator global_i = g_listeners.iterateForward(); - TBLinkListOf::Iterator local_i = parent->m_listeners.iterateForward(); - while (TBWidgetListener *listener = local_i.getAndStep()) { - listener->onWidgetAdded(parent, child); - } - while (TBWidgetListenerGlobalLink *link = global_i.getAndStep()) { - static_cast(link)->onWidgetAdded(parent, child); - } -} - -void TBWidgetListener::invokeWidgetRemove(TBWidget *parent, TBWidget *child) { - TBLinkListOf::Iterator global_i = g_listeners.iterateForward(); - TBLinkListOf::Iterator local_i = parent->m_listeners.iterateForward(); - while (TBWidgetListener *listener = local_i.getAndStep()) { - listener->onWidgetRemove(parent, child); - } - while (TBWidgetListenerGlobalLink *link = global_i.getAndStep()) { - static_cast(link)->onWidgetRemove(parent, child); - } -} - -void TBWidgetListener::invokeWidgetFocusChanged(TBWidget *widget, bool focused) { - TBLinkListOf::Iterator global_i = g_listeners.iterateForward(); - TBLinkListOf::Iterator local_i = widget->m_listeners.iterateForward(); - while (TBWidgetListener *listener = local_i.getAndStep()) { - listener->onWidgetFocusChanged(widget, focused); - } - while (TBWidgetListenerGlobalLink *link = global_i.getAndStep()) { - static_cast(link)->onWidgetFocusChanged(widget, focused); - } -} - -bool TBWidgetListener::invokeWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) { - bool handled = false; - TBLinkListOf::Iterator global_i = g_listeners.iterateForward(); - TBLinkListOf::Iterator local_i = widget->m_listeners.iterateForward(); - while (TBWidgetListener *listener = local_i.getAndStep()) { - handled |= listener->onWidgetInvokeEvent(widget, ev); - } - while (TBWidgetListenerGlobalLink *link = global_i.getAndStep()) { - handled |= static_cast(link)->onWidgetInvokeEvent(widget, ev); - } - return handled; -} - -void TBWidgetSafePointer::set(TBWidget *widget) { - if (m_widget == widget) { - return; - } - if (m_widget != nullptr) { - m_widget->removeListener(this); - } - m_widget = widget; - if (m_widget != nullptr) { - m_widget->addListener(this); - } -} - -void TBWidgetSafePointer::onWidgetDelete(TBWidget *widget) { - if (widget == m_widget) { - set(nullptr); - } -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets_listener.h b/src/modules/ui/turbobadger/tb/tb_widgets_listener.h deleted file mode 100644 index 575fbd1d0..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets_listener.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_core.h" -#include "tb_linklist.h" -#include "tb_widgets.h" - -namespace tb { - -class TBWidget; - -/** TBWidgetListenerGlobalLink should never be created or subclassed anywhere except - in TBWidgetListener. It's only purpose is to add a extra typed link for - TBWidgetListener, since it needs to be added in multiple lists. */ -class TBWidgetListenerGlobalLink : public TBLinkOf {}; - -/** TBWidgetListener listens to some callbacks from TBWidget. - It may either listen to all widgets globally, or one specific widget. - - Local listeners (added with TBWidget:AddListener) will be invoked before - global listeners (added with TBWidgetListener::AddGlobalListener). */ - -class TBWidgetListener : public TBLinkOf, public TBWidgetListenerGlobalLink { -public: - virtual ~TBWidgetListener() { - } - /** Add a listener to all widgets. */ - static void addGlobalListener(TBWidgetListener *listener); - static void removeGlobalListener(TBWidgetListener *listener); - - /** Called when widget is being deleted (in its destructor, so virtual functions are already gone). */ - virtual void onWidgetDelete(TBWidget *widget) { - } - - /** This is called when the widget request to be deleted. - Return true if you want the widget to not die immediately, f.ex. to fade it out before it - is deleted. If you return true, it's up to you to finally remove it from its parent delete it. - - Remember that the widget may still be deleted prematurely for many other reasons (f.ex if its parent is - deleted or several listeners respond true and take on the task to delete it at some point). You can - use TBWidgetSafePointer to safely handle that. */ - virtual bool onWidgetDying(TBWidget *widget) { - return false; - } - - /** Called when the child has been added to parent, after its parents OnChildAdded. - Local listeners are invoked on the parent widget. */ - virtual void onWidgetAdded(TBWidget *parent, TBWidget *child) { - } - - /** Called when the child is about to be removed from parent, after its parents OnChildRemove. - Local listeners are invoked on the parent widget. */ - virtual void onWidgetRemove(TBWidget *parent, TBWidget *child) { - } - - /** Called when widget focus has changed on a widget. */ - virtual void onWidgetFocusChanged(TBWidget *widget, bool focused) { - } - - /** Called when a event is about to be invoked on a widget. This make it possible - to intercept events before they are handled, and block it (by returning true). - Note, if returning true, other global listeners will still also be notified. */ - virtual bool onWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) { - return false; - } - -private: - friend class TBWidget; - static void invokeWidgetDelete(TBWidget *widget); - static bool invokeWidgetDying(TBWidget *widget); - static void invokeWidgetAdded(TBWidget *parent, TBWidget *child); - static void invokeWidgetRemove(TBWidget *parent, TBWidget *child); - static void invokeWidgetFocusChanged(TBWidget *widget, bool focused); - static bool invokeWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev); -}; - -/** TBWidgetSafePointer keeps a pointer to a widget that will be set to - nullptr if the widget is removed. */ -class TBWidgetSafePointer : private TBWidgetListener { -public: - TBWidgetSafePointer() : m_widget(nullptr) { - } - TBWidgetSafePointer(TBWidget *widget) : m_widget(nullptr) { - set(widget); - } - virtual ~TBWidgetSafePointer() { - set(nullptr); - } - - /** Set the widget pointer that should be nulled if deleted. */ - void set(TBWidget *widget); - - /** Return the widget, or nullptr if it has been deleted. */ - TBWidget *get() const { - return m_widget; - } - -private: - virtual void onWidgetDelete(TBWidget *widget); - TBWidget *m_widget; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets_reader.cpp b/src/modules/ui/turbobadger/tb/tb_widgets_reader.cpp deleted file mode 100644 index d91f67ad0..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets_reader.cpp +++ /dev/null @@ -1,665 +0,0 @@ -/** - * @file - */ - -#include "core/StringUtil.h" -#include "tb_widgets_reader.h" -#include "image/tb_image_widget.h" -#include "tb_editfield.h" -#include "tb_font_renderer.h" -#include "tb_inline_select.h" -#include "tb_node_tree.h" -#include "tb_scroll_container.h" -#include "tb_select.h" -#include "tb_system.h" -#include "tb_tab_container.h" -#include "tb_toggle_container.h" -#include "tb_widgets_common.h" - -namespace tb { - -TB_WIDGET_FACTORY(TBWidget, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -void TBWidget::onInflate(const INFLATE_INFO &info) { - TBWidgetsReader::setIDFromNode(getID(), info.node->getNode("id")); - - TBWidgetsReader::setIDFromNode(getGroupID(), info.node->getNode("group-id")); - - if (info.sync_type == TBValue::TYPE_FLOAT) { - setValueDouble(info.node->getValueFloat("value", 0)); - } else { - setValue(info.node->getValueInt("value", 0)); - } - - if (TBNode *data_node = info.node->getNode("data")) { - data.copy(data_node->getValue()); - } - - setIsGroupRoot(info.node->getValueInt("is-group-root", static_cast(getIsGroupRoot())) != 0); - - setIsFocusable(info.node->getValueInt("is-focusable", static_cast(getIsFocusable())) != 0); - - setWantLongClick(info.node->getValueInt("want-long-click", static_cast(getWantLongClick())) != 0); - - setWantCaptureOnHover(info.node->getValueInt("want-capture-on-hover", static_cast(getWantCaptureOnHover())) != 0); - - setWantFocusOnHover(info.node->getValueInt("want-focus-on-hover", static_cast(getWantFocusOnHover())) != 0); - - setIgnoreInput(info.node->getValueInt("ignore-input", static_cast(getIgnoreInput())) != 0); - - setOpacity(info.node->getValueFloat("opacity", getOpacity())); - - if (const char *text = info.node->getValueString("text", nullptr)) { - setText(text); - } - - if (const char *connection = info.node->getValueStringRaw("connection", nullptr)) { - // If we already have a widget value with this name, just connect to it and the widget will - // adjust its value to it. Otherwise create a new widget value, and give it the value we - // got from the resource. - if (TBWidgetValue *value = g_value_group.getValue(connection)) { - connect(value); - } else if (TBWidgetValue *value = g_value_group.createValueIfNeeded(connection, info.sync_type)) { - value->setFromWidget(this); - connect(value); - } - } - if (const char *axis = info.node->getValueString("axis", nullptr)) { - setAxis(*axis == 'x' ? AXIS_X : AXIS_Y); - } - if (const char *gravity = info.node->getValueString("gravity", nullptr)) { - WIDGET_GRAVITY g = WIDGET_GRAVITY_NONE; - if (SDL_strstr(gravity, "left") != nullptr) { - g |= WIDGET_GRAVITY_LEFT; - } - if (SDL_strstr(gravity, "top") != nullptr) { - g |= WIDGET_GRAVITY_TOP; - } - if (SDL_strstr(gravity, "right") != nullptr) { - g |= WIDGET_GRAVITY_RIGHT; - } - if (SDL_strstr(gravity, "bottom") != nullptr) { - g |= WIDGET_GRAVITY_BOTTOM; - } - if (SDL_strstr(gravity, "all") != nullptr) { - g |= WIDGET_GRAVITY_ALL; - } - if ((g & WIDGET_GRAVITY_LEFT_RIGHT) == 0U) { - g |= WIDGET_GRAVITY_LEFT; - } - if ((g & WIDGET_GRAVITY_TOP_BOTTOM) == 0U) { - g |= WIDGET_GRAVITY_TOP; - } - setGravity(g); - } - if (const char *visibility = info.node->getValueString("visibility", nullptr)) { - if (SDL_strcmp(visibility, "visible") == 0) { - setVisibility(WIDGET_VISIBILITY_VISIBLE); - } else if (SDL_strcmp(visibility, "invisible") == 0) { - setVisibility(WIDGET_VISIBILITY_INVISIBLE); - } else if (SDL_strcmp(visibility, "gone") == 0) { - setVisibility(WIDGET_VISIBILITY_GONE); - } - } - if (const char *state = info.node->getValueString("state", nullptr)) { - if (SDL_strstr(state, "disabled") != nullptr) { - setState(WIDGET_STATE_DISABLED, true); - } - } - if (const char *skin = info.node->getValueString("skin", nullptr)) { - setSkinBg(skin); - } - if (TBNode *lp = info.node->getNode("lp")) { - LayoutParams layout_params; - if (getLayoutParams() != nullptr) { - layout_params = *getLayoutParams(); - } - const TBDimensionConverter *dc = g_tb_skin->getDimensionConverter(); - if (const char *str = lp->getValueString("width", nullptr)) { - layout_params.setWidth(dc->getPxFromString(str, LayoutParams::UNSPECIFIED)); - } - if (const char *str = lp->getValueString("height", nullptr)) { - layout_params.setHeight(dc->getPxFromString(str, LayoutParams::UNSPECIFIED)); - } - if (const char *str = lp->getValueString("min-width", nullptr)) { - layout_params.min_w = dc->getPxFromString(str, LayoutParams::UNSPECIFIED); - } - if (const char *str = lp->getValueString("max-width", nullptr)) { - layout_params.max_w = dc->getPxFromString(str, LayoutParams::UNSPECIFIED); - } - if (const char *str = lp->getValueString("pref-width", nullptr)) { - layout_params.pref_w = dc->getPxFromString(str, LayoutParams::UNSPECIFIED); - } - if (const char *str = lp->getValueString("min-height", nullptr)) { - layout_params.min_h = dc->getPxFromString(str, LayoutParams::UNSPECIFIED); - } - if (const char *str = lp->getValueString("max-height", nullptr)) { - layout_params.max_h = dc->getPxFromString(str, LayoutParams::UNSPECIFIED); - } - if (const char *str = lp->getValueString("pref-height", nullptr)) { - layout_params.pref_h = dc->getPxFromString(str, LayoutParams::UNSPECIFIED); - } - setLayoutParams(layout_params); - } - - // Add the new widget to the hiearchy if not already added. - if (getParent() == nullptr) { - info.target->addChild(this, info.target->getZInflate()); - } - - // Read the font now when the widget is in the hiearchy so inheritance works. - if (TBNode *font = info.node->getNode("font")) { - TBFontDescription fd = getCalculatedFontDescription(); - if (const char *size = font->getValueString("size", nullptr)) { - int new_size = g_tb_skin->getDimensionConverter()->getPxFromString(size, fd.getSize()); - fd.setSize(new_size); - } - if (const char *name = font->getValueString("name", nullptr)) { - fd.setID(name); - } - setFontDescription(fd); - } - - info.target->onInflateChild(this); - - if (TBNode *rect_node = info.node->getNode("rect")) { - const TBDimensionConverter *dc = g_tb_skin->getDimensionConverter(); - TBValue &val = rect_node->getValue(); - if (val.getArrayLength() == 4) { - setRect(TBRect(dc->getPxFromValue(val.getArray()->getValue(0), 0), - dc->getPxFromValue(val.getArray()->getValue(1), 0), - dc->getPxFromValue(val.getArray()->getValue(2), 0), - dc->getPxFromValue(val.getArray()->getValue(3), 0))); - } - } -} - -TB_WIDGET_FACTORY(TBWindow, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} - -TB_WIDGET_FACTORY(TBButton, TBValue::TYPE_NULL, WIDGET_Z_BOTTOM) { -} -void TBButton::onInflate(const INFLATE_INFO &info) { - setSqueezable(info.node->getValueInt("squeezable", static_cast(getSqueezable())) != 0); - setAutoRepeat(info.node->getValueInt("auto-repeat", static_cast(getAutoRepeat())) != 0); - setToggleMode(info.node->getValueInt("toggle-mode", static_cast(getToggleMode())) != 0); - TBWidget::onInflate(info); - if (const char *command = info.node->getValueString("command", nullptr)) { - _command = command; - } - if (const char *varname = info.node->getValueString("varref", nullptr)) { - _var = core::Var::get(varname, 0); - setValue(_var->intVal()); - } -} - -void TBInlineSelectBase::onInflate(const INFLATE_INFO &info) { - Super::onInflate(info); - if (const char *command = info.node->getValueString("command", nullptr)) { - _command = command; - } -} - -TB_WIDGET_FACTORY(TBInlineSelect, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -void TBInlineSelect::onInflate(const INFLATE_INFO &info) { - Super::onInflate(info); - int min = info.node->getValueInt("min", getMinValue()); - int max = info.node->getValueInt("max", getMaxValue()); - setLimits(min, max); - if (const char *varname = info.node->getValueString("varref", nullptr)) { - _var = core::Var::get(varname, m_value); - setValue(_var->intVal()); - } -} - -TB_WIDGET_FACTORY(TBInlineSelectDouble, TBValue::TYPE_FLOAT, WIDGET_Z_TOP) { -} -void TBInlineSelectDouble::onInflate(const INFLATE_INFO &info) { - Super::onInflate(info); - double min = info.node->getValueFloat("min", getMinValue()); - double max = info.node->getValueFloat("max", getMaxValue()); - setLimits(min, max); - if (const char *varname = info.node->getValueString("varref", nullptr)) { - char buf[32]; - core::string::formatBuf(buf, sizeof(buf), "%f", (float)m_value); - _var = core::Var::get(varname, buf); - setValue(_var->intVal()); - } -} - -TB_WIDGET_FACTORY(TBClickLabel, TBValue::TYPE_STRING, WIDGET_Z_BOTTOM) { -} - -TB_WIDGET_FACTORY(TBMover, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} - -TB_WIDGET_FACTORY(TBEditField, TBValue::TYPE_STRING, WIDGET_Z_TOP) { -} -void TBEditField::onInflate(const INFLATE_INFO &info) { - setMultiline(info.node->getValueInt("multiline", 0) != 0); - setStyling(info.node->getValueInt("styling", 0) != 0); - setReadOnly(info.node->getValueInt("readonly", 0) != 0); - setWrapping(info.node->getValueInt("wrap", static_cast(getWrapping())) != 0); - setAdaptToContentSize(info.node->getValueInt("adapt-to-content", static_cast(getAdaptToContentSize())) != 0); - if (const char *virtual_width = info.node->getValueString("virtual-width", nullptr)) { - setVirtualWidth(g_tb_skin->getDimensionConverter()->getPxFromString(virtual_width, getVirtualWidth())); - } - if (const char *text = info.node->getValueString("placeholder", nullptr)) { - setPlaceholderText(text); - } - if (const char *text_align = info.node->getValueString("text-align", nullptr)) { - if (SDL_strcmp(text_align, "left") == 0) { - setTextAlign(TB_TEXT_ALIGN_LEFT); - } else if (SDL_strcmp(text_align, "center") == 0) { - setTextAlign(TB_TEXT_ALIGN_CENTER); - } else if (SDL_strcmp(text_align, "right") == 0) { - setTextAlign(TB_TEXT_ALIGN_RIGHT); - } - } - if (const char *type = info.node->getValueString("type", nullptr)) { - if (stristr(type, "text") != nullptr) { - setEditType(EDIT_TYPE_TEXT); - } else if (stristr(type, "search") != nullptr) { - setEditType(EDIT_TYPE_SEARCH); - } else if (stristr(type, "password") != nullptr) { - setEditType(EDIT_TYPE_PASSWORD); - } else if (stristr(type, "email") != nullptr) { - setEditType(EDIT_TYPE_EMAIL); - } else if (stristr(type, "phone") != nullptr) { - setEditType(EDIT_TYPE_PHONE); - } else if (stristr(type, "url") != nullptr) { - setEditType(EDIT_TYPE_URL); - } else if (stristr(type, "number") != nullptr) { - setEditType(EDIT_TYPE_NUMBER); - } - } - TBWidget::onInflate(info); - if (const char *varname = info.node->getValueString("varref", nullptr)) { - _var = core::Var::get(varname, getText().c_str()); - setText(_var->strVal().c_str()); - } -} - -TB_WIDGET_FACTORY(TBLayout, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -void TBLayout::onInflate(const INFLATE_INFO &info) { - if (const char *spacing = info.node->getValueString("spacing", nullptr)) { - setSpacing(g_tb_skin->getDimensionConverter()->getPxFromString(spacing, SPACING_FROM_SKIN)); - } - setGravity(WIDGET_GRAVITY_ALL); - if (const char *size = info.node->getValueString("size", nullptr)) { - LAYOUT_SIZE ls = LAYOUT_SIZE_PREFERRED; - if (SDL_strstr(size, "available") != nullptr) { - ls = LAYOUT_SIZE_AVAILABLE; - } else if (SDL_strstr(size, "gravity") != nullptr) { - ls = LAYOUT_SIZE_GRAVITY; - } else if (SDL_strstr(size, "preferred") != nullptr) { - } else { - Log::debug("TBLayout: Unknown size '%s'", size); - } - setLayoutSize(ls); - } - if (const char *pos = info.node->getValueString("position", nullptr)) { - LAYOUT_POSITION lp = LAYOUT_POSITION_CENTER; - if ((SDL_strstr(pos, "left") != nullptr) || (SDL_strstr(pos, "top") != nullptr)) { - lp = LAYOUT_POSITION_LEFT_TOP; - } else if ((SDL_strstr(pos, "right") != nullptr) || (SDL_strstr(pos, "bottom") != nullptr)) { - lp = LAYOUT_POSITION_RIGHT_BOTTOM; - } else if (SDL_strstr(pos, "gravity") != nullptr) { - lp = LAYOUT_POSITION_GRAVITY; - } else if (SDL_strcmp(pos, "center") == 0) { - } else { - Log::debug("TBLayout: Unknown position '%s'", pos); - } - setLayoutPosition(lp); - } - if (const char *pos = info.node->getValueString("overflow", nullptr)) { - LAYOUT_OVERFLOW lo = LAYOUT_OVERFLOW_CLIP; - if (SDL_strstr(pos, "scroll") != nullptr) { - lo = LAYOUT_OVERFLOW_SCROLL; - } else if (SDL_strstr(pos, "clip") != nullptr) { - } else { - Log::debug("TBLayout: Unknown overflow '%s'", pos); - } - setLayoutOverflow(lo); - } - if (const char *dist = info.node->getValueString("distribution", nullptr)) { - LAYOUT_DISTRIBUTION ld = LAYOUT_DISTRIBUTION_PREFERRED; - if (SDL_strstr(dist, "available") != nullptr) { - ld = LAYOUT_DISTRIBUTION_AVAILABLE; - } else if (SDL_strstr(dist, "gravity") != nullptr) { - ld = LAYOUT_DISTRIBUTION_GRAVITY; - } else if (SDL_strstr(dist, "preferred") != nullptr) { - } else { - Log::debug("TBLayout: Unknown distribution '%s'", dist); - } - setLayoutDistribution(ld); - } - if (const char *dist = info.node->getValueString("distribution-position", nullptr)) { - LAYOUT_DISTRIBUTION_POSITION ld = LAYOUT_DISTRIBUTION_POSITION_CENTER; - if ((SDL_strstr(dist, "left") != nullptr) || (SDL_strstr(dist, "top") != nullptr)) { - ld = LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP; - } else if ((SDL_strstr(dist, "right") != nullptr) || (SDL_strstr(dist, "bottom") != nullptr)) { - ld = LAYOUT_DISTRIBUTION_POSITION_RIGHT_BOTTOM; - } - setLayoutDistributionPosition(ld); - } - TBWidget::onInflate(info); -} - -TB_WIDGET_FACTORY(TBScrollContainer, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -void TBScrollContainer::onInflate(const INFLATE_INFO &info) { - setGravity(WIDGET_GRAVITY_ALL); - setAdaptContentSize(info.node->getValueInt("adapt-content", static_cast(getAdaptContentSize())) != 0); - setAdaptToContentSize(info.node->getValueInt("adapt-to-content", static_cast(getAdaptToContentSize())) != 0); - if (const char *mode = info.node->getValueString("scroll-mode", nullptr)) { - if (SDL_strcmp(mode, "xy") == 0) { - setScrollMode(SCROLL_MODE_X_Y); - } else if (SDL_strcmp(mode, "y") == 0) { - setScrollMode(SCROLL_MODE_Y); - } else if (SDL_strcmp(mode, "y-auto") == 0) { - setScrollMode(SCROLL_MODE_Y_AUTO); - } else if (SDL_strcmp(mode, "auto") == 0) { - setScrollMode(SCROLL_MODE_X_AUTO_Y_AUTO); - } else if (SDL_strcmp(mode, "off") == 0) { - setScrollMode(SCROLL_MODE_OFF); - } else { - Log::debug("TBScrollContainer: Unknown scroll-mode '%s'", mode); - } - } - TBWidget::onInflate(info); -} - -TB_WIDGET_FACTORY(TBTabContainer, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -void TBTabContainer::onInflate(const INFLATE_INFO &info) { - TBWidget::onInflate(info); - - if (const char *align = info.node->getValueString("align", nullptr)) { - if (SDL_strcmp(align, "left") == 0) { - setAlignment(TB_ALIGN_LEFT); - } else if (SDL_strcmp(align, "top") == 0) { - setAlignment(TB_ALIGN_TOP); - } else if (SDL_strcmp(align, "right") == 0) { - setAlignment(TB_ALIGN_RIGHT); - } else if (SDL_strcmp(align, "bottom") == 0) { - setAlignment(TB_ALIGN_BOTTOM); - } else { - Log::debug("TBTabContainer: Unknown align '%s'", align); - } - } - // Allow additional attributes to be specified for the "tabs", "content" and "root" layouts by - // calling onInflate. - if (TBNode *tabs = info.node->getNode("tabs")) { - // Inflate the tabs widgets into the tab layout. - TBLayout *tab_layout = getTabLayout(); - info.reader->loadNodeTree(tab_layout, tabs); - - INFLATE_INFO inflate_info(info.reader, tab_layout->getContentRoot(), tabs, TBValue::TYPE_NULL); - tab_layout->onInflate(inflate_info); - } - if (TBNode *tabs = info.node->getNode("content")) { - INFLATE_INFO inflate_info(info.reader, getContentRoot(), tabs, TBValue::TYPE_NULL); - getContentRoot()->onInflate(inflate_info); - } - if (TBNode *tabs = info.node->getNode("root")) { - INFLATE_INFO inflate_info(info.reader, &m_root_layout, tabs, TBValue::TYPE_NULL); - m_root_layout.onInflate(inflate_info); - } -} - -TB_WIDGET_FACTORY(TBScrollBar, TBValue::TYPE_FLOAT, WIDGET_Z_TOP) { -} -void TBScrollBar::onInflate(const INFLATE_INFO &info) { - const char *axis = info.node->getValueString("axis", "x"); - setAxis(*axis == 'x' ? AXIS_X : AXIS_Y); - setGravity(*axis == 'x' ? WIDGET_GRAVITY_LEFT_RIGHT : WIDGET_GRAVITY_TOP_BOTTOM); - TBWidget::onInflate(info); -} - -TB_WIDGET_FACTORY(TBSlider, TBValue::TYPE_FLOAT, WIDGET_Z_TOP) { -} -void TBSlider::onInflate(const INFLATE_INFO &info) { - const char *axis = info.node->getValueString("axis", "x"); - setAxis(*axis == 'x' ? AXIS_X : AXIS_Y); - setGravity(*axis == 'x' ? WIDGET_GRAVITY_LEFT_RIGHT : WIDGET_GRAVITY_TOP_BOTTOM); - double min = (double)info.node->getValueFloat("min", (float)getMinValue()); - double max = (double)info.node->getValueFloat("max", (float)getMaxValue()); - setLimits(min, max); - TBWidget::onInflate(info); - if (const char *command = info.node->getValueString("command", nullptr)) { - _command = command; - } - if (const char *varname = info.node->getValueString("varref", nullptr)) { - _var = core::Var::get(varname, m_value); - setValueDouble(_var->floatVal()); - } -} - -void readItems(TBNode *node, TBGenericStringItemSource *targetSource) { - // If there is a items node, loop through all its children and add - // items to the target item source. - if (TBNode *items = node->getNode("items")) { - for (TBNode *n = items->getFirstChild(); n != nullptr; n = n->getNext()) { - if (SDL_strcmp(n->getName(), "item") != 0) { - continue; - } - const char *item_str = n->getValueString("text", ""); - TBID item_id; - if (TBNode *n_id = n->getNode("id")) { - TBWidgetsReader::setIDFromNode(item_id, n_id); - } - - TBGenericStringItem *item = new TBGenericStringItem(item_str, item_id); - if ((item == nullptr) || !targetSource->addItem(item)) { - // Out of memory - delete item; - break; - } - } - } -} - -TB_WIDGET_FACTORY(TBSelectList, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -void TBSelectList::onInflate(const INFLATE_INFO &info) { - // Read items (if there is any) into the default source - readItems(info.node, getDefaultSource()); - TBWidget::onInflate(info); -} - -TB_WIDGET_FACTORY(TBSelectDropdown, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -void TBSelectDropdown::onInflate(const INFLATE_INFO &info) { - // Read items (if there is any) into the default source - readItems(info.node, getDefaultSource()); - TBWidget::onInflate(info); -} - -void TBRadioCheckBox::onInflate(const INFLATE_INFO &info) { - TBWidget::onInflate(info); - if (const char *command = info.node->getValueString("command", nullptr)) { - _command = command; - } - if (const char *varname = info.node->getValueString("varref", nullptr)) { - _var = core::Var::get(varname, m_value); - setValue(_var->intVal()); - } -} - -TB_WIDGET_FACTORY(TBCheckBox, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -TB_WIDGET_FACTORY(TBRadioButton, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} - -TB_WIDGET_FACTORY(TBTextField, TBValue::TYPE_STRING, WIDGET_Z_TOP) { -} -void TBTextField::onInflate(const INFLATE_INFO &info) { - if (const char *text_align = info.node->getValueString("text-align", nullptr)) { - if (SDL_strcmp(text_align, "left") == 0) { - setTextAlign(TB_TEXT_ALIGN_LEFT); - } else if (SDL_strcmp(text_align, "center") == 0) { - setTextAlign(TB_TEXT_ALIGN_CENTER); - } else if (SDL_strcmp(text_align, "right") == 0) { - setTextAlign(TB_TEXT_ALIGN_RIGHT); - } - } - TBWidget::onInflate(info); -} - -TB_WIDGET_FACTORY(TBSkinImage, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -TB_WIDGET_FACTORY(TBSeparator, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -TB_WIDGET_FACTORY(TBProgressSpinner, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -TB_WIDGET_FACTORY(TBContainer, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -TB_WIDGET_FACTORY(TBSectionHeader, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -TB_WIDGET_FACTORY(TBSection, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} - -TB_WIDGET_FACTORY(TBToggleContainer, TBValue::TYPE_INT, WIDGET_Z_TOP) { -} -void TBToggleContainer::onInflate(const INFLATE_INFO &info) { - if (const char *toggle = info.node->getValueString("toggle", nullptr)) { - if (stristr(toggle, "enabled") != nullptr) { - setToggle(TBToggleContainer::TOGGLE_ENABLED); - } else if (stristr(toggle, "opacity") != nullptr) { - setToggle(TBToggleContainer::TOGGLE_OPACITY); - } else if (stristr(toggle, "expanded") != nullptr) { - setToggle(TBToggleContainer::TOGGLE_EXPANDED); - } - } - setInvert(info.node->getValueInt("invert", static_cast(getInvert())) != 0); - TBWidget::onInflate(info); -} - -TB_WIDGET_FACTORY(TBImageWidget, TBValue::TYPE_NULL, WIDGET_Z_TOP) { -} -void TBImageWidget::onInflate(const INFLATE_INFO &info) { - if (const char *filename = info.node->getValueString("filename", nullptr)) { - setImage(filename); - } - TBWidget::onInflate(info); -} - -// We can't use a linked list object since we don't know if its constructor -// would run before or after any widget factory constructor that add itself -// to it. Using a manual one way link list is very simple. -TBWidgetFactory *g_registered_factories = nullptr; - -TBWidgetFactory::TBWidgetFactory(const char *name, TBValue::TYPE syncType) - : name(name), sync_type(syncType), next_registered_wf(nullptr) { -} - -void TBWidgetFactory::doRegister() { - next_registered_wf = g_registered_factories; - g_registered_factories = this; -} - -// == TBWidgetsReader =================================== - -TBWidgetsReader *TBWidgetsReader::create() { - TBWidgetsReader *w_reader = new TBWidgetsReader; - if ((w_reader == nullptr) || !w_reader->init()) { - delete w_reader; - return nullptr; - } - return w_reader; -} - -bool TBWidgetsReader::init() { - for (TBWidgetFactory *wf = g_registered_factories; wf != nullptr; wf = wf->next_registered_wf) { - if (!addFactory(wf)) { - return false; - } - } - return true; -} - -TBWidgetsReader::~TBWidgetsReader() { -} - -bool TBWidgetsReader::loadFile(TBWidget *target, const char *filename) { - TBNode node; - if (!node.readFile(filename)) { - return false; - } - loadNodeTree(target, &node); - return true; -} - -bool TBWidgetsReader::loadData(TBWidget *target, const char *data) { - TBNode node; - node.readData(data); - loadNodeTree(target, &node); - return true; -} - -bool TBWidgetsReader::loadData(TBWidget *target, const char *data, int dataLen) { - TBNode node; - node.readData(data, dataLen); - loadNodeTree(target, &node); - return true; -} - -void TBWidgetsReader::loadNodeTree(TBWidget *target, TBNode *node) { - // Iterate through all nodes and create widgets - for (TBNode *child = node->getFirstChild(); child != nullptr; child = child->getNext()) { - createWidget(target, child); - } -} - -void TBWidgetsReader::setIDFromNode(TBID &id, TBNode *node) { - if (node == nullptr) { - return; - } - if (node->getValue().isString()) { - id.set(node->getValue().getString()); - } else { - id.set(node->getValue().getInt()); - } -} - -bool TBWidgetsReader::createWidget(TBWidget *target, TBNode *node) { - // Find a widget creator from the node name - TBWidgetFactory *wc = nullptr; - for (wc = factories.getFirst(); wc != nullptr; wc = wc->getNext()) { - if (SDL_strcmp(node->getName(), wc->name) == 0) { - break; - } - } - if (wc == nullptr) { - return false; - } - - // Create the widget - INFLATE_INFO info(this, target->getContentRoot(), node, wc->sync_type); - TBWidget *new_widget = wc->create(&info); - if (new_widget == nullptr) { - return false; - } - - // Read properties and add i to the hierarchy. - new_widget->onInflate(info); - - // If this core_assert is trigged, you probably forgot to call TBWidget::onInflate from an overridden version. - core_assert(new_widget->getParent()); - - // Iterate through all nodes and create widgets - for (TBNode *n = node->getFirstChild(); n != nullptr; n = n->getNext()) { - createWidget(new_widget, n); - } - - if (node->getValueInt("autofocus", 0) != 0) { - new_widget->setFocus(WIDGET_FOCUS_REASON_UNKNOWN); - } - - return true; -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_widgets_reader.h b/src/modules/ui/turbobadger/tb/tb_widgets_reader.h deleted file mode 100644 index 5c8753854..000000000 --- a/src/modules/ui/turbobadger/tb/tb_widgets_reader.h +++ /dev/null @@ -1,173 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_linklist.h" -#include "tb_widgets.h" - -namespace tb { - -class TBWidgetsReader; -class TBWidgetFactory; -class TBWidget; -class TBNode; - -/** INFLATE_INFO contains info passed to TBWidget::onInflate during resource loading. */ -struct INFLATE_INFO { - INFLATE_INFO(TBWidgetsReader *reader, TBWidget *target, TBNode *node, TBValue::TYPE syncType) - : reader(reader), target(target), node(node), sync_type(syncType) { - } - TBWidgetsReader *reader; - - /** The widget that that will be parent to the inflated widget. */ - TBWidget *target; - /** The node containing properties. */ - TBNode *node; - /** The data type that should be synchronized through TBWidgetValue. */ - TBValue::TYPE sync_type; -}; - -/** TBWidgetFactory creates a widget from a TBNode. */ -class TBWidgetFactory : public TBLinkOf { -public: - TBWidgetFactory(const char *name, TBValue::TYPE sync_type); - virtual ~TBWidgetFactory() { - } - - /** Create and return the new widget or nullptr on out of memory. */ - virtual TBWidget *create(INFLATE_INFO *info) = 0; - - void doRegister(); - -public: - const char *name; - TBValue::TYPE sync_type; - TBWidgetFactory *next_registered_wf; -}; - -/** This macro creates a new TBWidgetFactory for the given class name so it can - be created from resources (using TBWidgetsReader). - - classname - The name of the class. - sync_type - The data type that should be synchronized through TBWidgetValue. - add_child_z - The order in which children should be added to it by default. - - It should be followed by an empty block (may eventually be removed). - Reading custom properties from resources can be done by overriding - TBWidget::onInflate. - - Example: - - TB_WIDGET_FACTORY(MyWidget, TBValue::TYPE_INT, WIDGET_Z_TOP) {} - */ -#define TB_WIDGET_FACTORY(classname, sync_type, add_child_z) \ - class classname##WidgetFactory : public tb::TBWidgetFactory { \ - public: \ - classname##WidgetFactory() : tb::TBWidgetFactory(#classname, sync_type) { \ - doRegister(); \ - } \ - virtual ~classname##WidgetFactory() { \ - } \ - virtual tb::TBWidget *create(tb::INFLATE_INFO *info) { \ - classname *widget = new classname(); \ - if (widget) { \ - widget->getContentRoot()->setZInflate(add_child_z); \ - readCustomProps(widget, info); \ - } \ - return widget; \ - } \ - void readCustomProps(classname *widget, tb::INFLATE_INFO *info); \ - }; \ - static classname##WidgetFactory classname##_wf; \ - void classname##WidgetFactory::readCustomProps(classname *widget, tb::INFLATE_INFO *info) - -/** - TBWidgetsReader parse a resource file (or buffer) into a TBNode tree, - and turns it into a hierarchy of widgets. It can create all types of widgets - that have a registered factory (TBWidgetFactory). All core widgets have - a factory by default, and you can also add your own. - - Values may be looked up from any existing TBNodeRefTree using the syntax - "@treename>noderequest". If treename is left out, the request will be looked - up in the same node tree. In addition to this, strings will be looked up - from the global TBLanguage by using the syntax "@stringid" - - Branches may be included or not depending on the value of a TBNodeRefTree - node, using "@if @treename>noderequest" and optionally a following "@else". - - Branches may be included from TBNodeRefTree using - "@include @treename>noderequest", or included from nodes specified previosly - in the same tree using "@include noderequest". - - Files can be included by using the syntax "@file filename". - - Each factory may have its own set of properties, but a set of generic - properties is always supported on all widgets. Those are: - - Resource name: TBWidget property: Values: - - id TBWidget::m_id TBID (string or int) - group-id TBWidget::m_group_id TBID (string or int) - value TBWidget::setValue integer - data TBWidget::m_data integer - is-group-root TBWidget::setIsGroupRoot boolean - is-focusable TBWidget::setIsFocusable boolean - want-long-click TBWidget::setWantLongClick boolean - ignore-input TBWidget::setIgnoreInput boolean - opacity TBWidget::setOpacity float (0 - 1) - text TBWidget::setText string - connection TBWidget::Connect string - axis TBWidget::setAxis x or y - gravity TBWidget::setGravity string (combination of left, top, right, bottom, or all) - visibility TBWidget::setVisibility string (visible, invisible, gone) - state TBWidget::setState string (disabled) - skin TBWidget::setSkinBg TBID (string or int) - rect TBWidget::setRect 4 integers (x, y, width, height) - lp>width TBWidget::setLayoutParams dimension - lp>min-width TBWidget::setLayoutParams dimension - lp>max-width TBWidget::setLayoutParams dimension - lp>pref-width TBWidget::setLayoutParams dimension - lp>height TBWidget::setLayoutParams dimension - lp>min-height TBWidget::setLayoutParams dimension - lp>max-height TBWidget::setLayoutParams dimension - lp>pref-height TBWidget::setLayoutParams dimension - autofocus The TBWidget will be focused automatically the first time its TBWindow is activated. - font>name Font name - font>size Font size -*/ -class TBWidgetsReader { -public: - static TBWidgetsReader *create(); - ~TBWidgetsReader(); - - /** Add a widget factory. Does not take ownership of the factory. - The easiest way to add factories for custom widget types, is using the - TB_WIDGET_FACTORY macro that automatically register it during startup. */ - bool addFactory(TBWidgetFactory *wf) { - factories.addLast(wf); - return true; - } - void removeFactory(TBWidgetFactory *wf) { - factories.remove(wf); - } - - /** Set the id from the given node. */ - static void setIDFromNode(TBID &id, TBNode *node); - - bool loadFile(TBWidget *target, const char *filename); - inline bool loadData(TBWidget *target, const core::String& data) { - return loadData(target, data.c_str()); - } - bool loadData(TBWidget *target, const char *data); - bool loadData(TBWidget *target, const char *data, int data_len); - void loadNodeTree(TBWidget *target, TBNode *node); - -private: - bool init(); - bool createWidget(TBWidget *target, TBNode *node); - TBLinkListOf factories; -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_window.cpp b/src/modules/ui/turbobadger/tb/tb_window.cpp deleted file mode 100644 index a57d6767e..000000000 --- a/src/modules/ui/turbobadger/tb/tb_window.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @file - */ - -#include "tb_window.h" -#include "core/Assert.h" - -namespace tb { - -TBWindow::TBWindow() : m_settings(WINDOW_SETTINGS_DEFAULT) { - setSkinBg(TBIDC("TBWindow"), WIDGET_INVOKE_INFO_NO_CALLBACKS); - addChild(&m_mover); - addChild(&m_resizer); - m_mover.setSkinBg(TBIDC("TBWindow.mover")); - m_mover.addChild(&m_textfield); - m_textfield.setIgnoreInput(true); - m_mover.addChild(&m_close_button); - m_close_button.setSkinBg(TBIDC("TBWindow.close")); - m_close_button.setIsFocusable(false); - m_close_button.setID(TBIDC("TBWindow.close")); - setIsGroupRoot(true); -} - -TBWindow::~TBWindow() { - m_resizer.removeFromParent(); - m_mover.removeFromParent(); - m_close_button.removeFromParent(); - m_textfield.removeFromParent(); -} - -TBRect TBWindow::getResizeToFitContentRect(RESIZE_FIT fit) { - PreferredSize ps = getPreferredSize(); - int new_w = ps.pref_w; - int new_h = ps.pref_h; - if (fit == RESIZE_FIT_MinIMAL) { - new_w = ps.min_w; - new_h = ps.min_h; - } else if (fit == RESIZE_FIT_CURRENT_OR_NEEDED) { - new_w = Clamp(getRect().w, ps.min_w, ps.max_w); - new_h = Clamp(getRect().h, ps.min_h, ps.max_h); - } - if (getParent() != nullptr) { - new_w = Min(new_w, getParent()->getRect().w); - new_h = Min(new_h, getParent()->getRect().h); - } - return TBRect(getRect().x, getRect().y, new_w, new_h); -} - -void TBWindow::resizeToFitContent(RESIZE_FIT fit) { - setRect(getResizeToFitContentRect(fit)); -} - -void TBWindow::close() { - die(); -} - -bool TBWindow::isActive() const { - return getState(WIDGET_STATE_SELECTED); -} - -TBWindow *TBWindow::getTopMostOtherWindow(bool onlyActivableWindows) { - TBWindow *other_window = nullptr; - TBWidget *sibling = getParent()->getLastChild(); - while ((sibling != nullptr) && (other_window == nullptr)) { - if (sibling != this) { - other_window = TBSafeCast(sibling); - } - - if (onlyActivableWindows && (other_window != nullptr) && - ((other_window->m_settings & WINDOW_SETTINGS_CAN_ACTIVATE) == 0U)) { - other_window = nullptr; - } - - sibling = sibling->getPrev(); - } - return other_window; -} - -void TBWindow::activate() { - if ((getParent() == nullptr) || ((m_settings & WINDOW_SETTINGS_CAN_ACTIVATE) == 0U)) { - return; - } - if (isActive()) { - // Already active, but we may still have lost focus, - // so ensure it comes back to us. - ensureFocus(); - return; - } - - // Deactivate currently active window - TBWindow *active_window = getTopMostOtherWindow(true); - if (active_window != nullptr) { - active_window->deActivate(); - } - - // Activate this window - - setZ(WIDGET_Z_TOP); - setWindowActiveState(true); - ensureFocus(); -} - -bool TBWindow::ensureFocus() { - // If we already have focus, we're done. - if ((focused_widget != nullptr) && isAncestorOf(focused_widget)) { - return true; - } - - // Focus last focused widget (if we have one) - bool success = false; - if (m_last_focus.get() != nullptr) { - success = m_last_focus.get()->setFocus(WIDGET_FOCUS_REASON_UNKNOWN); - } - // We didn't have one or failed, so try focus any child. - if (!success) { - success = setFocusRecursive(WIDGET_FOCUS_REASON_UNKNOWN); - } - return success; -} - -void TBWindow::deActivate() { - if (!isActive()) { - return; - } - setWindowActiveState(false); -} - -void TBWindow::setWindowActiveState(bool active) { - setState(WIDGET_STATE_SELECTED, active); - m_mover.setState(WIDGET_STATE_SELECTED, active); -} - -void TBWindow::setSettings(WINDOW_SETTINGS settings) { - if (settings == m_settings) { - return; - } - m_settings = settings; - - if ((settings & WINDOW_SETTINGS_TITLEBAR) != 0U) { - if (m_mover.getParent() == nullptr) { - addChild(&m_mover); - } - } else if ((settings & WINDOW_SETTINGS_TITLEBAR) == 0U) { - m_mover.removeFromParent(); - } - if ((settings & WINDOW_SETTINGS_RESIZABLE) != 0U) { - if (m_resizer.getParent() == nullptr) { - addChild(&m_resizer); - } - } else if ((settings & WINDOW_SETTINGS_RESIZABLE) == 0U) { - m_resizer.removeFromParent(); - } - if ((settings & WINDOW_SETTINGS_CLOSE_BUTTON) != 0U) { - if (m_close_button.getParent() == nullptr) { - m_mover.addChild(&m_close_button); - } - } else if ((settings & WINDOW_SETTINGS_CLOSE_BUTTON) == 0U) { - m_close_button.removeFromParent(); - } - - // FIX: invalidate layout / resize window! - invalidate(); -} - -int TBWindow::getTitleHeight() { - if ((m_settings & WINDOW_SETTINGS_TITLEBAR) != 0U) { - return m_mover.getPreferredSize().pref_h; - } - return 0; -} - -TBRect TBWindow::getPaddingRect() { - TBRect padding_rect = TBWidget::getPaddingRect(); - int title_height = getTitleHeight(); - padding_rect.y += title_height; - padding_rect.h -= title_height; - return padding_rect; -} - -PreferredSize TBWindow::onCalculatePreferredSize(const SizeConstraints &constraints) { - PreferredSize ps = onCalculatePreferredContentSize(constraints); - - // Add window skin padding - if (TBSkinElement *e = getSkinBgElement()) { - ps.min_w += e->padding_left + e->padding_right; - ps.pref_w += e->padding_left + e->padding_right; - ps.min_h += e->padding_top + e->padding_bottom; - ps.pref_h += e->padding_top + e->padding_bottom; - } - // Add window title bar height - int title_height = getTitleHeight(); - ps.min_h += title_height; - ps.pref_h += title_height; - return ps; -} - -bool TBWindow::onEvent(const TBWidgetEvent &ev) { - if (ev.target == &m_close_button) { - if (ev.type == EVENT_TYPE_CLICK) { - close(); - } - return true; - } - return false; -} - -void TBWindow::onAdded() { - // If we was added last, call Activate to update status etc. - if (getParent()->getLastChild() == this) { - activate(); - } -} - -void TBWindow::onRemove() { - deActivate(); - - // Active the top most other window - if (TBWindow *active_window = getTopMostOtherWindow(true)) { - active_window->activate(); - } -} - -void TBWindow::onChildAdded(TBWidget *child) { - m_resizer.setZ(WIDGET_Z_TOP); -} - -void TBWindow::onResized(int oldW, int oldH) { - // Apply gravity on children - TBWidget::onResized(oldW, oldH); - // Manually move our own decoration children - // FIX: Put a layout in the TBMover so we can add things there nicely. - const int title_height = getTitleHeight(); - m_mover.setRect(TBRect(0, 0, getRect().w, title_height)); - - PreferredSize ps = m_resizer.getPreferredSize(); - m_resizer.setRect(TBRect(getRect().w - ps.pref_w, getRect().h - ps.pref_h, ps.pref_w, ps.pref_h)); - - const TBRect mover_rect = m_mover.getRect(); - const TBRect mover_padding_rect = m_mover.getPaddingRect(); - const int mover_padding_right = mover_rect.x + mover_rect.w - (mover_padding_rect.x + mover_padding_rect.w); - const int button_w = m_close_button.getPreferredSize().pref_w; - const int button_h = Max(m_close_button.getPreferredSize().pref_h, mover_padding_rect.h); - m_close_button.setRect( - TBRect(mover_padding_rect.x + mover_padding_rect.w - button_w, mover_padding_rect.y, button_w, button_h)); - - TBRect title_rect = mover_padding_rect; - if ((m_settings & WINDOW_SETTINGS_CLOSE_BUTTON) != 0U) { - title_rect.w -= mover_padding_right + button_w; - } - m_textfield.setRect(title_rect); -} - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/tb_window.h b/src/modules/ui/turbobadger/tb/tb_window.h deleted file mode 100644 index bde536578..000000000 --- a/src/modules/ui/turbobadger/tb/tb_window.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "tb_widgets_common.h" -#include "tb_widgets_listener.h" - -namespace tb { - -enum WINDOW_SETTINGS { - WINDOW_SETTINGS_NONE = 0, ///< Chrome less window without any other settings. - WINDOW_SETTINGS_TITLEBAR = 1, ///< Show a title bar that can also move the window. - WINDOW_SETTINGS_RESIZABLE = 2, ///< Show a widget for resizing the window. - WINDOW_SETTINGS_CLOSE_BUTTON = 4, ///< Show a widget for closing the window. - WINDOW_SETTINGS_CAN_ACTIVATE = 8, ///< Can be activated and deactivate other windows. - - WINDOW_SETTINGS_DEFAULT = WINDOW_SETTINGS_TITLEBAR | WINDOW_SETTINGS_RESIZABLE | WINDOW_SETTINGS_CLOSE_BUTTON | - WINDOW_SETTINGS_CAN_ACTIVATE -}; -CORE_ENUM_BIT_OPERATIONS(WINDOW_SETTINGS); - -/** TBWindow is a TBWidget that provides some window-like features. - - It can have a titlebar, be movable, resizable etc. - - It will activate and deactivate other windows on click (which will restore - focus to the last focused child widget). */ - -class TBWindow : public TBWidget { -public: - // For safe typecasting - TBOBJECT_SUBCLASS(TBWindow, TBWidget); - - TBWindow(); - virtual ~TBWindow(); - - /** Close this window. - Warning: This window will be deleted after this call! */ - void close(); - - /** Return true if this window is active. */ - bool isActive() const; - - /** Activate this window if it's not already activated. - This will deactivate any currently activated window. - This will automatically call EnsureFocus to restore/set focus to this window. */ - void activate(); - - /** Ensure that this window has focus by attempting to find a focusable child widget. - It will first try to restore focus to the last focused widget in this window, - or a widget that has received SetFocus while the window was inactive. - If that doesn't succeed, it will go through all children and try to set focus. - Returns false if no focusable child was found. */ - bool ensureFocus(); - - /** Set the widget that should be focused when this window is activated next time. - This should not be used to change focus. Call TBWidget::setFocus to focus, which - will call this method if the window is inactive! */ - void setLastFocus(TBWidget *lastFocus) { - m_last_focus.set(lastFocus); - } - - /** Set settings for how this window should look and behave. */ - void setSettings(WINDOW_SETTINGS settings); - WINDOW_SETTINGS getSettings() { - return m_settings; - } - - /** RESIZE_FIT specifies how ResizeToFitContent should resize the window. */ - enum RESIZE_FIT { - RESIZE_FIT_PREFERRED, ///< Fit the preferred size of all content - RESIZE_FIT_MinIMAL, ///< Fit the minimal size of all content - RESIZE_FIT_CURRENT_OR_NEEDED ///< Fit the minimal or maximum size only if needed. Will keep - ///< the new size as close as possible to the current size. - }; - - /** Get a suitable rect for the window based on the contents and the given fit. */ - TBRect getResizeToFitContentRect(RESIZE_FIT fit = RESIZE_FIT_PREFERRED); - - /** Resize the window to fit the its content. This is the same as doing - SetRect(GetResizeToFitContentRect(fit)). */ - void resizeToFitContent(RESIZE_FIT fit = RESIZE_FIT_PREFERRED); - - /** Set the window title. */ - virtual bool setText(const char *text) override { - return m_textfield.setText(text); - } - virtual bool getText(core::String &text) override { - return m_textfield.getText(text); - } - using TBWidget::getText; ///< Make all versions in base class available. - - /** Get the height of the title bar (or 0 if the WINDOW_SETTINGS say this window - shouldn't have any title bar) */ - int getTitleHeight(); - - virtual TBRect getPaddingRect() override; - virtual PreferredSize onCalculatePreferredSize(const SizeConstraints &constraints) override; - - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onAdded() override; - virtual void onRemove() override; - virtual void onChildAdded(TBWidget *child) override; - virtual void onResized(int oldW, int oldH) override; - -protected: - TBMover m_mover; - TBResizer m_resizer; - TBTextField m_textfield; - TBWidget m_close_button; - WINDOW_SETTINGS m_settings; - TBWidgetSafePointer m_last_focus; - TBWindow *getTopMostOtherWindow(bool only_activable_windows); - void setWindowActiveState(bool active); - void deActivate(); -}; - -} // namespace tb diff --git a/src/modules/ui/turbobadger/tb/thirdparty/stb_truetype.h b/src/modules/ui/turbobadger/tb/thirdparty/stb_truetype.h deleted file mode 100644 index bbf2284b1..000000000 --- a/src/modules/ui/turbobadger/tb/thirdparty/stb_truetype.h +++ /dev/null @@ -1,5077 +0,0 @@ -// stb_truetype.h - v1.26 - public domain -// authored from 2009-2021 by Sean Barrett / RAD Game Tools -// -// ======================================================================= -// -// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES -// -// This library does no range checking of the offsets found in the file, -// meaning an attacker can use it to read arbitrary memory. -// -// ======================================================================= -// -// This library processes TrueType files: -// parse files -// extract glyph metrics -// extract glyph shapes -// render glyphs to one-channel bitmaps with antialiasing (box filter) -// render glyphs to one-channel SDF bitmaps (signed-distance field/function) -// -// Todo: -// non-MS cmaps -// crashproof on bad data -// hinting? (no longer patented) -// cleartype-style AA? -// optimize: use simple memory allocator for intermediates -// optimize: build edge-list directly from curves -// optimize: rasterize directly from curves? -// -// ADDITIONAL CONTRIBUTORS -// -// Mikko Mononen: compound shape support, more cmap formats -// Tor Andersson: kerning, subpixel rendering -// Dougall Johnson: OpenType / Type 2 font handling -// Daniel Ribeiro Maciel: basic GPOS-based kerning -// -// Misc other: -// Ryan Gordon -// Simon Glass -// github:IntellectualKitty -// Imanol Celaya -// Daniel Ribeiro Maciel -// -// Bug/warning reports/fixes: -// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe -// Cass Everitt Martins Mozeiko github:aloucks -// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam -// Brian Hook Omar Cornut github:vassvik -// Walter van Niftrik Ryan Griege -// David Gow Peter LaValle -// David Given Sergey Popov -// Ivan-Assen Ivanov Giumo X. Clanjor -// Anthony Pesch Higor Euripedes -// Johan Duparc Thomas Fields -// Hou Qiming Derek Vinyard -// Rob Loach Cort Stratton -// Kenney Phillis Jr. Brian Costabile -// Ken Voskuil (kaesve) -// -// VERSION HISTORY -// -// 1.26 (2021-08-28) fix broken rasterizer -// 1.25 (2021-07-11) many fixes -// 1.24 (2020-02-05) fix warning -// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) -// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined -// 1.21 (2019-02-25) fix warning -// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() -// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod -// 1.18 (2018-01-29) add missing function -// 1.17 (2017-07-23) make more arguments const; doc fix -// 1.16 (2017-07-12) SDF support -// 1.15 (2017-03-03) make more arguments const -// 1.14 (2017-01-16) num-fonts-in-TTC function -// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts -// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// variant PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// -// Full history can be found at the end of this file. -// -// LICENSE -// -// See end of file for license information. -// -// USAGE -// -// Include this file in whatever places need to refer to it. In ONE C/C++ -// file, write: -// #define STB_TRUETYPE_IMPLEMENTATION -// before the #include of this file. This expands out the actual -// implementation into that C/C++ file. -// -// To make the implementation private to the file that generates the implementation, -// #define STBTT_STATIC -// -// Simple 3D API (don't ship this, but it's fine for tools and quick start) -// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture -// stbtt_GetBakedQuad() -- compute quad to draw for a given char -// -// Improved 3D API (more shippable): -// #include "stb_rect_pack.h" -- optional, but you really want it -// stbtt_PackBegin() -// stbtt_PackSetOversampling() -- for improved quality on small fonts -// stbtt_PackFontRanges() -- pack and renders -// stbtt_PackEnd() -// stbtt_GetPackedQuad() -// -// "Load" a font file from a memory buffer (you have to keep the buffer loaded) -// stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections -// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections -// -// Render a unicode codepoint to a bitmap -// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap -// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide -// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be -// -// Character advance/positioning -// stbtt_GetCodepointHMetrics() -// stbtt_GetFontVMetrics() -// stbtt_GetFontVMetricsOS2() -// stbtt_GetCodepointKernAdvance() -// -// Starting with version 1.06, the rasterizer was replaced with a new, -// faster and generally-more-precise rasterizer. The new rasterizer more -// accurately measures pixel coverage for anti-aliasing, except in the case -// where multiple shapes overlap, in which case it overestimates the AA pixel -// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If -// this turns out to be a problem, you can re-enable the old rasterizer with -// #define STBTT_RASTERIZER_VERSION 1 -// which will incur about a 15% speed hit. -// -// ADDITIONAL DOCUMENTATION -// -// Immediately after this block comment are a series of sample programs. -// -// After the sample programs is the "header file" section. This section -// includes documentation for each API function. -// -// Some important concepts to understand to use this library: -// -// Codepoint -// Characters are defined by unicode codepoints, e.g. 65 is -// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is -// the hiragana for "ma". -// -// Glyph -// A visual character shape (every codepoint is rendered as -// some glyph) -// -// Glyph index -// A font-specific integer ID representing a glyph -// -// Baseline -// Glyph shapes are defined relative to a baseline, which is the -// bottom of uppercase characters. Characters extend both above -// and below the baseline. -// -// Current Point -// As you draw text to the screen, you keep track of a "current point" -// which is the origin of each character. The current point's vertical -// position is the baseline. Even "baked fonts" use this model. -// -// Vertical Font Metrics -// The vertical qualities of the font, used to vertically position -// and space the characters. See docs for stbtt_GetFontVMetrics. -// -// Font Size in Pixels or Points -// The preferred interface for specifying font sizes in stb_truetype -// is to specify how tall the font's vertical extent should be in pixels. -// If that sounds good enough, skip the next paragraph. -// -// Most font APIs instead use "points", which are a common typographic -// measurement for describing font size, defined as 72 points per inch. -// stb_truetype provides a point API for compatibility. However, true -// "per inch" conventions don't make much sense on computer displays -// since different monitors have different number of pixels per -// inch. For example, Windows traditionally uses a convention that -// there are 96 pixels per inch, thus making 'inch' measurements have -// nothing to do with inches, and thus effectively defining a point to -// be 1.333 pixels. Additionally, the TrueType font data provides -// an explicit scale factor to scale a given font's glyphs to points, -// but the author has observed that this scale factor is often wrong -// for non-commercial fonts, thus making fonts scaled in points -// according to the TrueType spec incoherently sized in practice. -// -// DETAILED USAGE: -// -// Scale: -// Select how high you want the font to be, in points or pixels. -// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute -// a scale factor SF that will be used by all other functions. -// -// Baseline: -// You need to select a y-coordinate that is the baseline of where -// your text will appear. Call GetFontBoundingBox to get the baseline-relative -// bounding box for all characters. SF*-y0 will be the distance in pixels -// that the worst-case character could extend above the baseline, so if -// you want the top edge of characters to appear at the top of the -// screen where y=0, then you would set the baseline to SF*-y0. -// -// Current point: -// Set the current point where the first character will appear. The -// first character could extend left of the current point; this is font -// dependent. You can either choose a current point that is the leftmost -// point and hope, or add some padding, or check the bounding box or -// left-side-bearing of the first character to be displayed and set -// the current point based on that. -// -// Displaying a character: -// Compute the bounding box of the character. It will contain signed values -// relative to . I.e. if it returns x0,y0,x1,y1, -// then the character should be displayed in the rectangle from -// to = 32 && *text < 128) { - stbtt_aligned_quad q; - stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 - glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); - glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); - glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); - glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); - } - ++text; - } - glEnd(); -} -#endif -// -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program (this compiles): get a single bitmap, print as ASCII art -// -#if 0 -#include -#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -#include "stb_truetype.h" - -char ttf_buffer[1<<25]; - -int main(int argc, char **argv) -{ - stbtt_fontinfo font; - unsigned char *bitmap; - int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); - - fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); - - stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); - bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); - - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) - putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); - putchar('\n'); - } - return 0; -} -#endif -// -// Output: -// -// .ii. -// @@@@@@. -// V@Mio@@o -// :i. V@V -// :oM@@M -// :@@@MM@M -// @@o o@M -// :@@. M@M -// @@@o@@@@ -// :M@@V:@@. -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program: print "Hello World!" banner, with bugs -// -#if 0 -char buffer[24<<20]; -unsigned char screen[20][79]; - -int main(int arg, char **argv) -{ - stbtt_fontinfo font; - int i,j,ascent,baseline,ch=0; - float scale, xpos=2; // leave a little padding in case the character extends left - char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness - - fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); - stbtt_InitFont(&font, buffer, 0); - - scale = stbtt_ScaleForPixelHeight(&font, 15); - stbtt_GetFontVMetrics(&font, &ascent,0,0); - baseline = (int) (ascent*scale); - - while (text[ch]) { - int advance,lsb,x0,y0,x1,y1; - float x_shift = xpos - (float) floor(xpos); - stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); - stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); - stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); - // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong - // because this API is really for baking character bitmaps into textures. if you want to render - // a sequence of characters, you really need to render each bitmap to a temp buffer, then - // "alpha blend" that into the working buffer - xpos += (advance * scale); - if (text[ch+1]) - xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); - ++ch; - } - - for (j=0; j < 20; ++j) { - for (i=0; i < 78; ++i) - putchar(" .:ioVM@"[screen[j][i]>>5]); - putchar('\n'); - } - - return 0; -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//// -//// INTEGRATION WITH YOUR CODEBASE -//// -//// The following sections allow you to supply alternate definitions -//// of C library functions used by stb_truetype, e.g. if you don't -//// link with the C runtime library. - -#ifdef STB_TRUETYPE_IMPLEMENTATION - // #define your own (u)stbtt_int8/16/32 before including to override this - #ifndef stbtt_uint8 - typedef unsigned char stbtt_uint8; - typedef signed char stbtt_int8; - typedef unsigned short stbtt_uint16; - typedef signed short stbtt_int16; - typedef unsigned int stbtt_uint32; - typedef signed int stbtt_int32; - #endif - - typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; - typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; - - // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h - #ifndef STBTT_ifloor - #include - #define STBTT_ifloor(x) ((int) floor(x)) - #define STBTT_iceil(x) ((int) ceil(x)) - #endif - - #ifndef STBTT_sqrt - #include - #define STBTT_sqrt(x) sqrt(x) - #define STBTT_pow(x,y) pow(x,y) - #endif - - #ifndef STBTT_fmod - #include - #define STBTT_fmod(x,y) fmod(x,y) - #endif - - #ifndef STBTT_cos - #include - #define STBTT_cos(x) cos(x) - #define STBTT_acos(x) acos(x) - #endif - - #ifndef STBTT_fabs - #include - #define STBTT_fabs(x) fabs(x) - #endif - - // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h - #ifndef STBTT_malloc - #include - #define STBTT_malloc(x,u) ((void)(u),malloc(x)) - #define STBTT_free(x,u) ((void)(u),free(x)) - #endif - - #ifndef STBTT_assert - #include - #define STBTT_assert(x) assert(x) - #endif - - #ifndef STBTT_strlen - #include - #define STBTT_strlen(x) strlen(x) - #endif - - #ifndef STBTT_memcpy - #include - #define STBTT_memcpy memcpy - #define STBTT_memset memset - #endif -#endif - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// INTERFACE -//// -//// - -#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ -#define __STB_INCLUDE_STB_TRUETYPE_H__ - -#ifdef STBTT_STATIC -#define STBTT_DEF static -#else -#define STBTT_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// private structure -typedef struct -{ - unsigned char *data; - int cursor; - int size; -} stbtt__buf; - -////////////////////////////////////////////////////////////////////////////// -// -// TEXTURE BAKING API -// -// If you use this API, you only have to call two functions ever. -// - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; -} stbtt_bakedchar; - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata); // you allocate this, it's num_chars long -// if return is positive, the first unused row of the bitmap -// if return is negative, returns the negative of the number of characters that fit -// if return is 0, no characters fit and no rows were used -// This uses a very crappy packing. - -typedef struct -{ - float x0,y0,s0,t0; // top-left - float x1,y1,s1,t1; // bottom-right -} stbtt_aligned_quad; - -STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier -// Call GetBakedQuad with char_index = 'character - first_char', and it -// creates the quad you need to draw and advances the current position. -// -// The coordinate system used assumes y increases downwards. -// -// Characters will extend both above and below the current position; -// see discussion of "BASELINE" above. -// -// It's inefficient; you might want to c&p it and optimize it. - -STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); -// Query the font vertical metrics without having to create a font first. - - -////////////////////////////////////////////////////////////////////////////// -// -// NEW TEXTURE BAKING API -// -// This provides options for packing multiple fonts into one atlas, not -// perfectly but better than nothing. - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; - float xoff2,yoff2; -} stbtt_packedchar; - -typedef struct stbtt_pack_context stbtt_pack_context; -typedef struct stbtt_fontinfo stbtt_fontinfo; -#ifndef STB_RECT_PACK_VERSION -typedef struct stbrp_rect stbrp_rect; -#endif - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); -// Initializes a packing context stored in the passed-in stbtt_pack_context. -// Future calls using this context will pack characters into the bitmap passed -// in here: a 1-channel bitmap that is width * height. stride_in_bytes is -// the distance from one row to the next (or 0 to mean they are packed tightly -// together). "padding" is the amount of padding to leave between each -// character (normally you want '1' for bitmaps you'll use as textures with -// bilinear filtering). -// -// Returns 0 on failure, 1 on success. - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); -// Cleans up the packing context and frees all memory. - -#define STBTT_POINT_SIZE(x) (-(x)) - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, - int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); -// Creates character bitmaps from the font_index'th font found in fontdata (use -// font_index=0 if you don't know what that is). It creates num_chars_in_range -// bitmaps for characters with unicode values starting at first_unicode_char_in_range -// and increasing. Data for how to render them is stored in chardata_for_range; -// pass these to stbtt_GetPackedQuad to get back renderable quads. -// -// font_size is the full height of the character from ascender to descender, -// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed -// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() -// and pass that result as 'font_size': -// ..., 20 , ... // font max minus min y is 20 pixels tall -// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall - -typedef struct -{ - float font_size; - int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint - int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints - int num_chars; - stbtt_packedchar *chardata_for_range; // output - unsigned char h_oversample, v_oversample; // don't set these, they're used internally -} stbtt_pack_range; - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); -// Creates character bitmaps from multiple ranges of characters stored in -// ranges. This will usually create a better-packed bitmap than multiple -// calls to stbtt_PackFontRange. Note that you can call this multiple -// times within a single PackBegin/PackEnd. - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); -// Oversampling a font increases the quality by allowing higher-quality subpixel -// positioning, and is especially valuable at smaller text sizes. -// -// This function sets the amount of oversampling for all following calls to -// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given -// pack context. The default (no oversampling) is achieved by h_oversample=1 -// and v_oversample=1. The total number of pixels required is -// h_oversample*v_oversample larger than the default; for example, 2x2 -// oversampling requires 4x the storage of 1x1. For best results, render -// oversampled textures with bilinear filtering. Look at the readme in -// stb/tests/oversample for information about oversampled fonts -// -// To use with PackFontRangesGather etc., you must set it before calls -// call to PackFontRangesGatherRects. - -STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); -// If skip != 0, this tells stb_truetype to skip any codepoints for which -// there is no corresponding glyph. If skip=0, which is the default, then -// codepoints without a glyph recived the font's "missing character" glyph, -// typically an empty box by convention. - -STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int align_to_integer); - -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -// Calling these functions in sequence is roughly equivalent to calling -// stbtt_PackFontRanges(). If you more control over the packing of multiple -// fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version -// using these functions, e.g. call GatherRects multiple times, -// building up a single array of rects, then call PackRects once, -// then call RenderIntoRects repeatedly. This may result in a -// better packing than calling PackFontRanges multiple times -// (or it may not). - -// this is an opaque structure that you shouldn't mess with which holds -// all the context needed from PackBegin to PackEnd. -struct stbtt_pack_context { - void *user_allocator_context; - void *pack_info; - int width; - int height; - int stride_in_bytes; - int padding; - int skip_missing; - unsigned int h_oversample, v_oversample; - unsigned char *pixels; - void *nodes; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// FONT LOADING -// -// - -STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); -// This function will determine the number of fonts in a font file. TrueType -// collection (.ttc) files may contain multiple fonts, while TrueType font -// (.ttf) files only contain one font. The number of fonts can be used for -// indexing with the previous function where the index is between zero and one -// less than the total fonts. If an error occurs, -1 is returned. - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); -// Each .ttf/.ttc file may have more than one font. Each font has a sequential -// index number starting from 0. Call this function to get the font offset for -// a given index; it returns -1 if the index is out of range. A regular .ttf -// file will only define one font and it always be at offset 0, so it will -// return '0' for index 0, and -1 for all other indices. - -// The following structure is defined publicly so you can declare one on -// the stack or as a global or etc, but you should treat it as opaque. -struct stbtt_fontinfo -{ - void * userdata; - unsigned char * data; // pointer to .ttf file - int fontstart; // offset of start of font - - int numGlyphs; // number of glyphs, needed for range checking - - int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf - int index_map; // a cmap mapping for our chosen character encoding - int indexToLocFormat; // format needed to map from glyph index to glyph - - stbtt__buf cff; // cff font data - stbtt__buf charstrings; // the charstring index - stbtt__buf gsubrs; // global charstring subroutines index - stbtt__buf subrs; // private charstring subroutines index - stbtt__buf fontdicts; // array of font dicts - stbtt__buf fdselect; // map from glyph to fontdict -}; - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); -// Given an offset into the file that defines a font, this function builds -// the necessary cached info for the rest of the system. You must allocate -// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't -// need to do anything special to free it, because the contents are pure -// value data with no additional data structures. Returns 0 on failure. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER TO GLYPH-INDEX CONVERSIOn - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); -// If you're going to perform multiple operations on the same character -// and you want a speed-up, call this function with the character you're -// going to process, then use glyph-based functions instead of the -// codepoint-based functions. -// Returns 0 if the character codepoint is not defined in the font. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER PROPERTIES -// - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose "height" is 'pixels' tall. -// Height is measured as the distance from the highest ascender to the lowest -// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics -// and computing: -// scale = pixels / (ascent - descent) -// so if you prefer to measure height by the ascent only, use a similar calculation. - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose EM size is mapped to -// 'pixels' tall. This is probably what traditional APIs compute, but -// I'm not positive. - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); -// ascent is the coordinate above the baseline the font extends; descent -// is the coordinate below the baseline the font extends (i.e. it is typically negative) -// lineGap is the spacing between one row's descent and the next row's ascent... -// so you should advance the vertical position by "*ascent - *descent + *lineGap" -// these are expressed in unscaled coordinates, so you must multiply by -// the scale factor for a given size - -STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); -// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 -// table (specific to MS/Windows TTF files). -// -// Returns 1 on success (table present), 0 on failure. - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); -// the bounding box around all possible characters - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); -// leftSideBearing is the offset from the current horizontal position to the left edge of the character -// advanceWidth is the offset from the current horizontal position to the next horizontal position -// these are expressed in unscaled coordinates - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); -// an additional amount to add to the 'advance' value between ch1 and ch2 - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); -// Gets the bounding box of the visible part of the glyph, in unscaled coordinates - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); -// as above, but takes one or more glyph indices for greater efficiency - -typedef struct stbtt_kerningentry -{ - int glyph1; // use stbtt_FindGlyphIndex - int glyph2; - int advance; -} stbtt_kerningentry; - -STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); -STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); -// Retrieves a complete list of all of the kerning pairs provided by the font -// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. -// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) - -////////////////////////////////////////////////////////////////////////////// -// -// GLYPH SHAPES (you probably don't need these, but they have to go before -// the bitmaps for C declaration-order reasons) -// - -#ifndef STBTT_vmove // you can predefine these to use different values (but why?) - enum { - STBTT_vmove=1, - STBTT_vline, - STBTT_vcurve, - STBTT_vcubic - }; -#endif - -#ifndef stbtt_vertex // you can predefine this to use different values - // (we share this with other code at RAD) - #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file - typedef struct - { - stbtt_vertex_type x,y,cx,cy,cx1,cy1; - unsigned char type,padding; - } stbtt_vertex; -#endif - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); -// returns non-zero if nothing is drawn for this glyph - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); -// returns # of vertices and fills *vertices with the pointer to them -// these are expressed in "unscaled" coordinates -// -// The shape is a series of contours. Each one starts with -// a STBTT_moveto, then consists of a series of mixed -// STBTT_lineto and STBTT_curveto segments. A lineto -// draws a line from previous endpoint to its x,y; a curveto -// draws a quadratic bezier from previous endpoint to -// its x,y, using cx,cy as the bezier control point. - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); -// frees the data allocated above - -STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); -STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); -STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); -// fills svg with the character's SVG data. -// returns data size or 0 if SVG not found. - -////////////////////////////////////////////////////////////////////////////// -// -// BITMAP RENDERING -// - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); -// frees the bitmap allocated below - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// allocates a large-enough single-channel 8bpp bitmap and renders the -// specified character/glyph at the specified scale into it, with -// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). -// *width & *height are filled out with the width & height of the bitmap, -// which is stored left-to-right, top-to-bottom. -// -// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); -// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap -// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap -// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the -// width and height and positioning info for it first. - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); -// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); -// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering -// is performed (see stbtt_PackSetOversampling) - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -// get the bbox of the bitmap centered around the glyph origin; so the -// bitmap width is ix1-ix0, height is iy1-iy0, and location to place -// the bitmap top left is (leftSideBearing*scale,iy0). -// (Note that the bitmap uses y-increases-down, but the shape uses -// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); -// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel -// shift for the character - -// the following functions are equivalent to the above functions, but operate -// on glyph indices instead of Unicode codepoints (for efficiency) -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); - - -// @TODO: don't expose this structure -typedef struct -{ - int w,h,stride; - unsigned char *pixels; -} stbtt__bitmap; - -// rasterize a shape with quadratic beziers into a bitmap -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into - float flatness_in_pixels, // allowable error of curve in pixels - stbtt_vertex *vertices, // array of vertices defining shape - int num_verts, // number of vertices in above array - float scale_x, float scale_y, // scale applied to input vertices - float shift_x, float shift_y, // translation applied to input vertices - int x_off, int y_off, // another translation applied to input - int invert, // if non-zero, vertically flip shape - void *userdata); // context for to STBTT_MALLOC - -////////////////////////////////////////////////////////////////////////////// -// -// Signed Distance Function (or Field) rendering - -STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); -// frees the SDF bitmap allocated below - -STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); -// These functions compute a discretized SDF field for a single character, suitable for storing -// in a single-channel texture, sampling with bilinear filtering, and testing against -// larger than some threshold to produce scalable fonts. -// info -- the font -// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap -// glyph/codepoint -- the character to generate the SDF for -// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), -// which allows effects like bit outlines -// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) -// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) -// if positive, > onedge_value is inside; if negative, < onedge_value is inside -// width,height -- output height & width of the SDF bitmap (including padding) -// xoff,yoff -- output origin of the character -// return value -- a 2D array of bytes 0..255, width*height in size -// -// pixel_dist_scale & onedge_value are a scale & bias that allows you to make -// optimal use of the limited 0..255 for your application, trading off precision -// and special effects. SDF values outside the range 0..255 are clamped to 0..255. -// -// Example: -// scale = stbtt_ScaleForPixelHeight(22) -// padding = 5 -// onedge_value = 180 -// pixel_dist_scale = 180/5.0 = 36.0 -// -// This will create an SDF bitmap in which the character is about 22 pixels -// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled -// shape, sample the SDF at each pixel and fill the pixel if the SDF value -// is greater than or equal to 180/255. (You'll actually want to antialias, -// which is beyond the scope of this example.) Additionally, you can compute -// offset outlines (e.g. to stroke the character border inside & outside, -// or only outside). For example, to fill outside the character up to 3 SDF -// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above -// choice of variables maps a range from 5 pixels outside the shape to -// 2 pixels inside the shape to 0..255; this is intended primarily for apply -// outside effects only (the interior range is needed to allow proper -// antialiasing of the font at *smaller* sizes) -// -// The function computes the SDF analytically at each SDF pixel, not by e.g. -// building a higher-res bitmap and approximating it. In theory the quality -// should be as high as possible for an SDF of this size & representation, but -// unclear if this is true in practice (perhaps building a higher-res bitmap -// and computing from that can allow drop-out prevention). -// -// The algorithm has not been optimized at all, so expect it to be slow -// if computing lots of characters or very large sizes. - - - -////////////////////////////////////////////////////////////////////////////// -// -// Finding the right font... -// -// You should really just solve this offline, keep your own tables -// of what font is what, and don't try to get it out of the .ttf file. -// That's because getting it out of the .ttf file is really hard, because -// the names in the file can appear in many possible encodings, in many -// possible languages, and e.g. if you need a case-insensitive comparison, -// the details of that depend on the encoding & language in a complex way -// (actually underspecified in truetype, but also gigantic). -// -// But you can use the provided functions in two possible ways: -// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on -// unicode-encoded names to try to find the font you want; -// you can run this before calling stbtt_InitFont() -// -// stbtt_GetFontNameString() lets you get any of the various strings -// from the file yourself and do your own comparisons on them. -// You have to have called stbtt_InitFont() first. - - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); -// returns the offset (not index) of the font that matches, or -1 if none -// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". -// if you use any other flag, use a font name like "Arial"; this checks -// the 'macStyle' header field; i don't know if fonts set this consistently -#define STBTT_MACSTYLE_DONTCARE 0 -#define STBTT_MACSTYLE_BOLD 1 -#define STBTT_MACSTYLE_ITALIC 2 -#define STBTT_MACSTYLE_UNDERSCORE 4 -#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); -// returns 1/0 whether the first string interpreted as utf8 is identical to -// the second string interpreted as big-endian utf16... useful for strings from next func - -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); -// returns the string (which may be big-endian double byte, e.g. for unicode) -// and puts the length in bytes in *length. -// -// some of the values for the IDs are below; for more see the truetype spec: -// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html -// http://www.microsoft.com/typography/otspec/name.htm - -enum { // platformID - STBTT_PLATFORM_ID_UNICODE =0, - STBTT_PLATFORM_ID_MAC =1, - STBTT_PLATFORM_ID_ISO =2, - STBTT_PLATFORM_ID_MICROSOFT =3 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_UNICODE - STBTT_UNICODE_EID_UNICODE_1_0 =0, - STBTT_UNICODE_EID_UNICODE_1_1 =1, - STBTT_UNICODE_EID_ISO_10646 =2, - STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, - STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT - STBTT_MS_EID_SYMBOL =0, - STBTT_MS_EID_UNICODE_BMP =1, - STBTT_MS_EID_SHIFTJIS =2, - STBTT_MS_EID_UNICODE_FULL =10 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes - STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, - STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, - STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, - STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 -}; - -enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... - // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs - STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, - STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, - STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, - STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, - STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, - STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D -}; - -enum { // languageID for STBTT_PLATFORM_ID_MAC - STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, - STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, - STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, - STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , - STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , - STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, - STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 -}; - -#ifdef __cplusplus -} -#endif - -#endif // __STB_INCLUDE_STB_TRUETYPE_H__ - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// IMPLEMENTATION -//// -//// - -#ifdef STB_TRUETYPE_IMPLEMENTATION - -#ifndef STBTT_MAX_OVERSAMPLE -#define STBTT_MAX_OVERSAMPLE 8 -#endif - -#if STBTT_MAX_OVERSAMPLE > 255 -#error "STBTT_MAX_OVERSAMPLE cannot be > 255" -#endif - -typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; - -#ifndef STBTT_RASTERIZER_VERSION -#define STBTT_RASTERIZER_VERSION 2 -#endif - -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif - -////////////////////////////////////////////////////////////////////////// -// -// stbtt__buf helpers to parse data from file -// - -static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) -{ - if (b->cursor >= b->size) - return 0; - return b->data[b->cursor++]; -} - -static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) -{ - if (b->cursor >= b->size) - return 0; - return b->data[b->cursor]; -} - -static void stbtt__buf_seek(stbtt__buf *b, int o) -{ - STBTT_assert(!(o > b->size || o < 0)); - b->cursor = (o > b->size || o < 0) ? b->size : o; -} - -static void stbtt__buf_skip(stbtt__buf *b, int o) -{ - stbtt__buf_seek(b, b->cursor + o); -} - -static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) -{ - stbtt_uint32 v = 0; - int i; - STBTT_assert(n >= 1 && n <= 4); - for (i = 0; i < n; i++) - v = (v << 8) | stbtt__buf_get8(b); - return v; -} - -static stbtt__buf stbtt__new_buf(const void *p, size_t size) -{ - stbtt__buf r; - STBTT_assert(size < 0x40000000); - r.data = (stbtt_uint8*) p; - r.size = (int) size; - r.cursor = 0; - return r; -} - -#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) -#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) - -static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) -{ - stbtt__buf r = stbtt__new_buf(NULL, 0); - if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; - r.data = b->data + o; - r.size = s; - return r; -} - -static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) -{ - int count, start, offsize; - start = b->cursor; - count = stbtt__buf_get16(b); - if (count) { - offsize = stbtt__buf_get8(b); - STBTT_assert(offsize >= 1 && offsize <= 4); - stbtt__buf_skip(b, offsize * count); - stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); - } - return stbtt__buf_range(b, start, b->cursor - start); -} - -static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) -{ - int b0 = stbtt__buf_get8(b); - if (b0 >= 32 && b0 <= 246) return b0 - 139; - else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; - else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; - else if (b0 == 28) return stbtt__buf_get16(b); - else if (b0 == 29) return stbtt__buf_get32(b); - STBTT_assert(0); - return 0; -} - -static void stbtt__cff_skip_operand(stbtt__buf *b) { - int v, b0 = stbtt__buf_peek8(b); - STBTT_assert(b0 >= 28); - if (b0 == 30) { - stbtt__buf_skip(b, 1); - while (b->cursor < b->size) { - v = stbtt__buf_get8(b); - if ((v & 0xF) == 0xF || (v >> 4) == 0xF) - break; - } - } else { - stbtt__cff_int(b); - } -} - -static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) -{ - stbtt__buf_seek(b, 0); - while (b->cursor < b->size) { - int start = b->cursor, end, op; - while (stbtt__buf_peek8(b) >= 28) - stbtt__cff_skip_operand(b); - end = b->cursor; - op = stbtt__buf_get8(b); - if (op == 12) op = stbtt__buf_get8(b) | 0x100; - if (op == key) return stbtt__buf_range(b, start, end-start); - } - return stbtt__buf_range(b, 0, 0); -} - -static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) -{ - int i; - stbtt__buf operands = stbtt__dict_get(b, key); - for (i = 0; i < outcount && operands.cursor < operands.size; i++) - out[i] = stbtt__cff_int(&operands); -} - -static int stbtt__cff_index_count(stbtt__buf *b) -{ - stbtt__buf_seek(b, 0); - return stbtt__buf_get16(b); -} - -static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) -{ - int count, offsize, start, end; - stbtt__buf_seek(&b, 0); - count = stbtt__buf_get16(&b); - offsize = stbtt__buf_get8(&b); - STBTT_assert(i >= 0 && i < count); - STBTT_assert(offsize >= 1 && offsize <= 4); - stbtt__buf_skip(&b, i*offsize); - start = stbtt__buf_get(&b, offsize); - end = stbtt__buf_get(&b, offsize); - return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); -} - -////////////////////////////////////////////////////////////////////////// -// -// accessors to parse data from file -// - -// on platforms that don't allow misaligned reads, if we want to allow -// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE - -#define ttBYTE(p) (* (stbtt_uint8 *) (p)) -#define ttCHAR(p) (* (stbtt_int8 *) (p)) -#define ttFixed(p) ttLONG(p) - -static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } -static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } -static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) -#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) - -static int stbtt__isfont(stbtt_uint8 *font) -{ - // check the version number - if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 - if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! - if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF - if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 - if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts - return 0; -} - -// @OPTIMIZE: binary search -static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) -{ - stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); - stbtt_uint32 tabledir = fontstart + 12; - stbtt_int32 i; - for (i=0; i < num_tables; ++i) { - stbtt_uint32 loc = tabledir + 16*i; - if (stbtt_tag(data+loc+0, tag)) - return ttULONG(data+loc+8); - } - return 0; -} - -static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) -{ - // if it's just a font, there's only one valid index - if (stbtt__isfont(font_collection)) - return index == 0 ? 0 : -1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - stbtt_int32 n = ttLONG(font_collection+8); - if (index >= n) - return -1; - return ttULONG(font_collection+12+index*4); - } - } - return -1; -} - -static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) -{ - // if it's just a font, there's only one valid font - if (stbtt__isfont(font_collection)) - return 1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - return ttLONG(font_collection+8); - } - } - return 0; -} - -static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) -{ - stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; - stbtt__buf pdict; - stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); - if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); - pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); - stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); - if (!subrsoff) return stbtt__new_buf(NULL, 0); - stbtt__buf_seek(&cff, private_loc[1]+subrsoff); - return stbtt__cff_get_index(&cff); -} - -// since most people won't use this, find this table the first time it's needed -static int stbtt__get_svg(stbtt_fontinfo *info) -{ - stbtt_uint32 t; - if (info->svg < 0) { - t = stbtt__find_table(info->data, info->fontstart, "SVG "); - if (t) { - stbtt_uint32 offset = ttULONG(info->data + t + 2); - info->svg = t + offset; - } else { - info->svg = 0; - } - } - return info->svg; -} - -static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) -{ - stbtt_uint32 cmap, t; - stbtt_int32 i,numTables; - - info->data = data; - info->fontstart = fontstart; - info->cff = stbtt__new_buf(NULL, 0); - - cmap = stbtt__find_table(data, fontstart, "cmap"); // required - info->loca = stbtt__find_table(data, fontstart, "loca"); // required - info->head = stbtt__find_table(data, fontstart, "head"); // required - info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required - info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required - info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required - info->kern = stbtt__find_table(data, fontstart, "kern"); // not required - info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required - - if (!cmap || !info->head || !info->hhea || !info->hmtx) - return 0; - if (info->glyf) { - // required for truetype - if (!info->loca) return 0; - } else { - // initialization for CFF / Type2 fonts (OTF) - stbtt__buf b, topdict, topdictidx; - stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; - stbtt_uint32 cff; - - cff = stbtt__find_table(data, fontstart, "CFF "); - if (!cff) return 0; - - info->fontdicts = stbtt__new_buf(NULL, 0); - info->fdselect = stbtt__new_buf(NULL, 0); - - // @TODO this should use size from table (not 512MB) - info->cff = stbtt__new_buf(data+cff, 512*1024*1024); - b = info->cff; - - // read the header - stbtt__buf_skip(&b, 2); - stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize - - // @TODO the name INDEX could list multiple fonts, - // but we just use the first one. - stbtt__cff_get_index(&b); // name INDEX - topdictidx = stbtt__cff_get_index(&b); - topdict = stbtt__cff_index_get(topdictidx, 0); - stbtt__cff_get_index(&b); // string INDEX - info->gsubrs = stbtt__cff_get_index(&b); - - stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); - stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); - stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); - stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); - info->subrs = stbtt__get_subrs(b, topdict); - - // we only support Type 2 charstrings - if (cstype != 2) return 0; - if (charstrings == 0) return 0; - - if (fdarrayoff) { - // looks like a CID font - if (!fdselectoff) return 0; - stbtt__buf_seek(&b, fdarrayoff); - info->fontdicts = stbtt__cff_get_index(&b); - info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); - } - - stbtt__buf_seek(&b, charstrings); - info->charstrings = stbtt__cff_get_index(&b); - } - - t = stbtt__find_table(data, fontstart, "maxp"); - if (t) - info->numGlyphs = ttUSHORT(data+t+4); - else - info->numGlyphs = 0xffff; - - info->svg = -1; - - // find a cmap encoding table we understand *now* to avoid searching - // later. (todo: could make this installable) - // the same regardless of glyph. - numTables = ttUSHORT(data + cmap + 2); - info->index_map = 0; - for (i=0; i < numTables; ++i) { - stbtt_uint32 encoding_record = cmap + 4 + 8 * i; - // find an encoding we understand: - switch(ttUSHORT(data+encoding_record)) { - case STBTT_PLATFORM_ID_MICROSOFT: - switch (ttUSHORT(data+encoding_record+2)) { - case STBTT_MS_EID_UNICODE_BMP: - case STBTT_MS_EID_UNICODE_FULL: - // MS/Unicode - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - break; - case STBTT_PLATFORM_ID_UNICODE: - // Mac/iOS has these - // all the encodingIDs are unicode, so we don't bother to check it - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - } - if (info->index_map == 0) - return 0; - - info->indexToLocFormat = ttUSHORT(data+info->head + 50); - return 1; -} - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) -{ - stbtt_uint8 *data = info->data; - stbtt_uint32 index_map = info->index_map; - - stbtt_uint16 format = ttUSHORT(data + index_map + 0); - if (format == 0) { // apple byte encoding - stbtt_int32 bytes = ttUSHORT(data + index_map + 2); - if (unicode_codepoint < bytes-6) - return ttBYTE(data + index_map + 6 + unicode_codepoint); - return 0; - } else if (format == 6) { - stbtt_uint32 first = ttUSHORT(data + index_map + 6); - stbtt_uint32 count = ttUSHORT(data + index_map + 8); - if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) - return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); - return 0; - } else if (format == 2) { - STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean - return 0; - } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges - stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; - stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; - stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); - stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; - - // do a binary search of the segments - stbtt_uint32 endCount = index_map + 14; - stbtt_uint32 search = endCount; - - if (unicode_codepoint > 0xffff) - return 0; - - // they lie from endCount .. endCount + segCount - // but searchRange is the nearest power of two, so... - if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) - search += rangeShift*2; - - // now decrement to bias correctly to find smallest - search -= 2; - while (entrySelector) { - stbtt_uint16 end; - searchRange >>= 1; - end = ttUSHORT(data + search + searchRange*2); - if (unicode_codepoint > end) - search += searchRange*2; - --entrySelector; - } - search += 2; - - { - stbtt_uint16 offset, start, last; - stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); - - start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); - last = ttUSHORT(data + endCount + 2*item); - if (unicode_codepoint < start || unicode_codepoint > last) - return 0; - - offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); - if (offset == 0) - return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); - - return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); - } - } else if (format == 12 || format == 13) { - stbtt_uint32 ngroups = ttULONG(data+index_map+12); - stbtt_int32 low,high; - low = 0; high = (stbtt_int32)ngroups; - // Binary search the right group. - while (low < high) { - stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high - stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); - stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); - if ((stbtt_uint32) unicode_codepoint < start_char) - high = mid; - else if ((stbtt_uint32) unicode_codepoint > end_char) - low = mid+1; - else { - stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); - if (format == 12) - return start_glyph + unicode_codepoint-start_char; - else // format == 13 - return start_glyph; - } - } - return 0; // not found - } - // @TODO - STBTT_assert(0); - return 0; -} - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) -{ - return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); -} - -static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) -{ - v->type = type; - v->x = (stbtt_int16) x; - v->y = (stbtt_int16) y; - v->cx = (stbtt_int16) cx; - v->cy = (stbtt_int16) cy; -} - -static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) -{ - int g1,g2; - - STBTT_assert(!info->cff.size); - - if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range - if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format - - if (info->indexToLocFormat == 0) { - g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; - g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; - } else { - g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); - g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); - } - - return g1==g2 ? -1 : g1; // if length is 0, return -1 -} - -static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); - -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - if (info->cff.size) { - stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); - } else { - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; - - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); - } - return 1; -} - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) -{ - return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); -} - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt_int16 numberOfContours; - int g; - if (info->cff.size) - return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; - g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 1; - numberOfContours = ttSHORT(info->data + g); - return numberOfContours == 0; -} - -static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, - stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) -{ - if (start_off) { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); - } - return num_vertices; -} - -static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - stbtt_int16 numberOfContours; - stbtt_uint8 *endPtsOfContours; - stbtt_uint8 *data = info->data; - stbtt_vertex *vertices=0; - int num_vertices=0; - int g = stbtt__GetGlyfOffset(info, glyph_index); - - *pvertices = NULL; - - if (g < 0) return 0; - - numberOfContours = ttSHORT(data + g); - - if (numberOfContours > 0) { - stbtt_uint8 flags=0,flagcount; - stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; - stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; - stbtt_uint8 *points; - endPtsOfContours = (data + g + 10); - ins = ttUSHORT(data + g + 10 + numberOfContours * 2); - points = data + g + 10 + numberOfContours * 2 + 2 + ins; - - n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); - - m = n + 2*numberOfContours; // a loose bound on how many vertices we might need - vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); - if (vertices == 0) - return 0; - - next_move = 0; - flagcount=0; - - // in first pass, we load uninterpreted data into the allocated array - // above, shifted to the end of the array so we won't overwrite it when - // we create our final data starting from the front - - off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated - - // first load flags - - for (i=0; i < n; ++i) { - if (flagcount == 0) { - flags = *points++; - if (flags & 8) - flagcount = *points++; - } else - --flagcount; - vertices[off+i].type = flags; - } - - // now load x coordinates - x=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 2) { - stbtt_int16 dx = *points++; - x += (flags & 16) ? dx : -dx; // ??? - } else { - if (!(flags & 16)) { - x = x + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].x = (stbtt_int16) x; - } - - // now load y coordinates - y=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 4) { - stbtt_int16 dy = *points++; - y += (flags & 32) ? dy : -dy; // ??? - } else { - if (!(flags & 32)) { - y = y + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].y = (stbtt_int16) y; - } - - // now convert them to our format - num_vertices=0; - sx = sy = cx = cy = scx = scy = 0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - x = (stbtt_int16) vertices[off+i].x; - y = (stbtt_int16) vertices[off+i].y; - - if (next_move == i) { - if (i != 0) - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - - // now start the new one - start_off = !(flags & 1); - if (start_off) { - // if we start off with an off-curve point, then when we need to find a point on the curve - // where we can start, and we need to save some state for when we wraparound. - scx = x; - scy = y; - if (!(vertices[off+i+1].type & 1)) { - // next point is also a curve point, so interpolate an on-point curve - sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; - sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; - } else { - // otherwise just use the next point as our start point - sx = (stbtt_int32) vertices[off+i+1].x; - sy = (stbtt_int32) vertices[off+i+1].y; - ++i; // we're using point i+1 as the starting point, so skip it - } - } else { - sx = x; - sy = y; - } - stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); - was_off = 0; - next_move = 1 + ttUSHORT(endPtsOfContours+j*2); - ++j; - } else { - if (!(flags & 1)) { // if it's a curve - if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); - cx = x; - cy = y; - was_off = 1; - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); - was_off = 0; - } - } - } - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours < 0) { - // Compound shapes. - int more = 1; - stbtt_uint8 *comp = data + g + 10; - num_vertices = 0; - vertices = 0; - while (more) { - stbtt_uint16 flags, gidx; - int comp_num_verts = 0, i; - stbtt_vertex *comp_verts = 0, *tmp = 0; - float mtx[6] = {1,0,0,1,0,0}, m, n; - - flags = ttSHORT(comp); comp+=2; - gidx = ttSHORT(comp); comp+=2; - - if (flags & 2) { // XY values - if (flags & 1) { // shorts - mtx[4] = ttSHORT(comp); comp+=2; - mtx[5] = ttSHORT(comp); comp+=2; - } else { - mtx[4] = ttCHAR(comp); comp+=1; - mtx[5] = ttCHAR(comp); comp+=1; - } - } - else { - // @TODO handle matching point - STBTT_assert(0); - } - if (flags & (1<<3)) { // WE_HAVE_A_SCALE - mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } - - // Find transformation scales. - m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); - n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); - - // Get indexed glyph. - comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); - if (comp_num_verts > 0) { - // Transform vertices. - for (i = 0; i < comp_num_verts; ++i) { - stbtt_vertex* v = &comp_verts[i]; - stbtt_vertex_type x,y; - x=v->x; y=v->y; - v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - x=v->cx; y=v->cy; - v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - } - // Append vertices. - tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); - if (!tmp) { - if (vertices) STBTT_free(vertices, info->userdata); - if (comp_verts) STBTT_free(comp_verts, info->userdata); - return 0; - } - if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); - STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); - if (vertices) STBTT_free(vertices, info->userdata); - vertices = tmp; - STBTT_free(comp_verts, info->userdata); - num_vertices += comp_num_verts; - } - // More components ? - more = flags & (1<<5); - } - } else { - // numberOfCounters == 0, do nothing - } - - *pvertices = vertices; - return num_vertices; -} - -typedef struct -{ - int bounds; - int started; - float first_x, first_y; - float x, y; - stbtt_int32 min_x, max_x, min_y, max_y; - - stbtt_vertex *pvertices; - int num_vertices; -} stbtt__csctx; - -#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} - -static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) -{ - if (x > c->max_x || !c->started) c->max_x = x; - if (y > c->max_y || !c->started) c->max_y = y; - if (x < c->min_x || !c->started) c->min_x = x; - if (y < c->min_y || !c->started) c->min_y = y; - c->started = 1; -} - -static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) -{ - if (c->bounds) { - stbtt__track_vertex(c, x, y); - if (type == STBTT_vcubic) { - stbtt__track_vertex(c, cx, cy); - stbtt__track_vertex(c, cx1, cy1); - } - } else { - stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); - c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; - c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; - } - c->num_vertices++; -} - -static void stbtt__csctx_close_shape(stbtt__csctx *ctx) -{ - if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) - stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) -{ - stbtt__csctx_close_shape(ctx); - ctx->first_x = ctx->x = ctx->x + dx; - ctx->first_y = ctx->y = ctx->y + dy; - stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) -{ - ctx->x += dx; - ctx->y += dy; - stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) -{ - float cx1 = ctx->x + dx1; - float cy1 = ctx->y + dy1; - float cx2 = cx1 + dx2; - float cy2 = cy1 + dy2; - ctx->x = cx2 + dx3; - ctx->y = cy2 + dy3; - stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); -} - -static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) -{ - int count = stbtt__cff_index_count(&idx); - int bias = 107; - if (count >= 33900) - bias = 32768; - else if (count >= 1240) - bias = 1131; - n += bias; - if (n < 0 || n >= count) - return stbtt__new_buf(NULL, 0); - return stbtt__cff_index_get(idx, n); -} - -static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt__buf fdselect = info->fdselect; - int nranges, start, end, v, fmt, fdselector = -1, i; - - stbtt__buf_seek(&fdselect, 0); - fmt = stbtt__buf_get8(&fdselect); - if (fmt == 0) { - // untested - stbtt__buf_skip(&fdselect, glyph_index); - fdselector = stbtt__buf_get8(&fdselect); - } else if (fmt == 3) { - nranges = stbtt__buf_get16(&fdselect); - start = stbtt__buf_get16(&fdselect); - for (i = 0; i < nranges; i++) { - v = stbtt__buf_get8(&fdselect); - end = stbtt__buf_get16(&fdselect); - if (glyph_index >= start && glyph_index < end) { - fdselector = v; - break; - } - start = end; - } - } - if (fdselector == -1) stbtt__new_buf(NULL, 0); - return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); -} - -static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) -{ - int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; - int has_subrs = 0, clear_stack; - float s[48]; - stbtt__buf subr_stack[10], subrs = info->subrs, b; - float f; - -#define STBTT__CSERR(s) (0) - - // this currently ignores the initial width value, which isn't needed if we have hmtx - b = stbtt__cff_index_get(info->charstrings, glyph_index); - while (b.cursor < b.size) { - i = 0; - clear_stack = 1; - b0 = stbtt__buf_get8(&b); - switch (b0) { - // @TODO implement hinting - case 0x13: // hintmask - case 0x14: // cntrmask - if (in_header) - maskbits += (sp / 2); // implicit "vstem" - in_header = 0; - stbtt__buf_skip(&b, (maskbits + 7) / 8); - break; - - case 0x01: // hstem - case 0x03: // vstem - case 0x12: // hstemhm - case 0x17: // vstemhm - maskbits += (sp / 2); - break; - - case 0x15: // rmoveto - in_header = 0; - if (sp < 2) return STBTT__CSERR("rmoveto stack"); - stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); - break; - case 0x04: // vmoveto - in_header = 0; - if (sp < 1) return STBTT__CSERR("vmoveto stack"); - stbtt__csctx_rmove_to(c, 0, s[sp-1]); - break; - case 0x16: // hmoveto - in_header = 0; - if (sp < 1) return STBTT__CSERR("hmoveto stack"); - stbtt__csctx_rmove_to(c, s[sp-1], 0); - break; - - case 0x05: // rlineto - if (sp < 2) return STBTT__CSERR("rlineto stack"); - for (; i + 1 < sp; i += 2) - stbtt__csctx_rline_to(c, s[i], s[i+1]); - break; - - // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical - // starting from a different place. - - case 0x07: // vlineto - if (sp < 1) return STBTT__CSERR("vlineto stack"); - goto vlineto; - case 0x06: // hlineto - if (sp < 1) return STBTT__CSERR("hlineto stack"); - for (;;) { - if (i >= sp) break; - stbtt__csctx_rline_to(c, s[i], 0); - i++; - vlineto: - if (i >= sp) break; - stbtt__csctx_rline_to(c, 0, s[i]); - i++; - } - break; - - case 0x1F: // hvcurveto - if (sp < 4) return STBTT__CSERR("hvcurveto stack"); - goto hvcurveto; - case 0x1E: // vhcurveto - if (sp < 4) return STBTT__CSERR("vhcurveto stack"); - for (;;) { - if (i + 3 >= sp) break; - stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); - i += 4; - hvcurveto: - if (i + 3 >= sp) break; - stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); - i += 4; - } - break; - - case 0x08: // rrcurveto - if (sp < 6) return STBTT__CSERR("rcurveline stack"); - for (; i + 5 < sp; i += 6) - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - break; - - case 0x18: // rcurveline - if (sp < 8) return STBTT__CSERR("rcurveline stack"); - for (; i + 5 < sp - 2; i += 6) - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); - stbtt__csctx_rline_to(c, s[i], s[i+1]); - break; - - case 0x19: // rlinecurve - if (sp < 8) return STBTT__CSERR("rlinecurve stack"); - for (; i + 1 < sp - 6; i += 2) - stbtt__csctx_rline_to(c, s[i], s[i+1]); - if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - break; - - case 0x1A: // vvcurveto - case 0x1B: // hhcurveto - if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); - f = 0.0; - if (sp & 1) { f = s[i]; i++; } - for (; i + 3 < sp; i += 4) { - if (b0 == 0x1B) - stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); - else - stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); - f = 0.0; - } - break; - - case 0x0A: // callsubr - if (!has_subrs) { - if (info->fdselect.size) - subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); - has_subrs = 1; - } - // FALLTHROUGH - case 0x1D: // callgsubr - if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); - v = (int) s[--sp]; - if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); - subr_stack[subr_stack_height++] = b; - b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); - if (b.size == 0) return STBTT__CSERR("subr not found"); - b.cursor = 0; - clear_stack = 0; - break; - - case 0x0B: // return - if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); - b = subr_stack[--subr_stack_height]; - clear_stack = 0; - break; - - case 0x0E: // endchar - stbtt__csctx_close_shape(c); - return 1; - - case 0x0C: { // two-byte escape - float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; - float dx, dy; - int b1 = stbtt__buf_get8(&b); - switch (b1) { - // @TODO These "flex" implementations ignore the flex-depth and resolution, - // and always draw beziers. - case 0x22: // hflex - if (sp < 7) return STBTT__CSERR("hflex stack"); - dx1 = s[0]; - dx2 = s[1]; - dy2 = s[2]; - dx3 = s[3]; - dx4 = s[4]; - dx5 = s[5]; - dx6 = s[6]; - stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); - stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); - break; - - case 0x23: // flex - if (sp < 13) return STBTT__CSERR("flex stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dy3 = s[5]; - dx4 = s[6]; - dy4 = s[7]; - dx5 = s[8]; - dy5 = s[9]; - dx6 = s[10]; - dy6 = s[11]; - //fd is s[12] - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); - stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); - break; - - case 0x24: // hflex1 - if (sp < 9) return STBTT__CSERR("hflex1 stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dx4 = s[5]; - dx5 = s[6]; - dy5 = s[7]; - dx6 = s[8]; - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); - stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); - break; - - case 0x25: // flex1 - if (sp < 11) return STBTT__CSERR("flex1 stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dy3 = s[5]; - dx4 = s[6]; - dy4 = s[7]; - dx5 = s[8]; - dy5 = s[9]; - dx6 = dy6 = s[10]; - dx = dx1+dx2+dx3+dx4+dx5; - dy = dy1+dy2+dy3+dy4+dy5; - if (STBTT_fabs(dx) > STBTT_fabs(dy)) - dy6 = -dy; - else - dx6 = -dx; - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); - stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); - break; - - default: - return STBTT__CSERR("unimplemented"); - } - } break; - - default: - if (b0 != 255 && b0 != 28 && b0 < 32) - return STBTT__CSERR("reserved operator"); - - // push immediate - if (b0 == 255) { - f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; - } else { - stbtt__buf_skip(&b, -1); - f = (float)(stbtt_int16)stbtt__cff_int(&b); - } - if (sp >= 48) return STBTT__CSERR("push stack overflow"); - s[sp++] = f; - clear_stack = 0; - break; - } - if (clear_stack) sp = 0; - } - return STBTT__CSERR("no endchar"); - -#undef STBTT__CSERR -} - -static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - // runs the charstring twice, once to count and once to output (to avoid realloc) - stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); - stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); - if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { - *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); - output_ctx.pvertices = *pvertices; - if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { - STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); - return output_ctx.num_vertices; - } - } - *pvertices = NULL; - return 0; -} - -static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - stbtt__csctx c = STBTT__CSCTX_INIT(1); - int r = stbtt__run_charstring(info, glyph_index, &c); - if (x0) *x0 = r ? c.min_x : 0; - if (y0) *y0 = r ? c.min_y : 0; - if (x1) *x1 = r ? c.max_x : 0; - if (y1) *y1 = r ? c.max_y : 0; - return r ? c.num_vertices : 0; -} - -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - if (!info->cff.size) - return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); - else - return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); -} - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) -{ - stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); - if (glyph_index < numOfLongHorMetrics) { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); - } else { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); - } -} - -STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) -{ - stbtt_uint8 *data = info->data + info->kern; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - return ttUSHORT(data+10); -} - -STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) -{ - stbtt_uint8 *data = info->data + info->kern; - int k, length; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - length = ttUSHORT(data+10); - if (table_length < length) - length = table_length; - - for (k = 0; k < length; k++) - { - table[k].glyph1 = ttUSHORT(data+18+(k*6)); - table[k].glyph2 = ttUSHORT(data+20+(k*6)); - table[k].advance = ttSHORT(data+22+(k*6)); - } - - return length; -} - -static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint8 *data = info->data + info->kern; - stbtt_uint32 needle, straw; - int l, r, m; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - l = 0; - r = ttUSHORT(data+10) - 1; - needle = glyph1 << 16 | glyph2; - while (l <= r) { - m = (l + r) >> 1; - straw = ttULONG(data+18+(m*6)); // note: unaligned read - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else - return ttSHORT(data+22+(m*6)); - } - return 0; -} - -static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) -{ - stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); - switch (coverageFormat) { - case 1: { - stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); - - // Binary search. - stbtt_int32 l=0, r=glyphCount-1, m; - int straw, needle=glyph; - while (l <= r) { - stbtt_uint8 *glyphArray = coverageTable + 4; - stbtt_uint16 glyphID; - m = (l + r) >> 1; - glyphID = ttUSHORT(glyphArray + 2 * m); - straw = glyphID; - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else { - return m; - } - } - break; - } - - case 2: { - stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); - stbtt_uint8 *rangeArray = coverageTable + 4; - - // Binary search. - stbtt_int32 l=0, r=rangeCount-1, m; - int strawStart, strawEnd, needle=glyph; - while (l <= r) { - stbtt_uint8 *rangeRecord; - m = (l + r) >> 1; - rangeRecord = rangeArray + 6 * m; - strawStart = ttUSHORT(rangeRecord); - strawEnd = ttUSHORT(rangeRecord + 2); - if (needle < strawStart) - r = m - 1; - else if (needle > strawEnd) - l = m + 1; - else { - stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); - return startCoverageIndex + glyph - strawStart; - } - } - break; - } - - default: return -1; // unsupported - } - - return -1; -} - -static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) -{ - stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); - switch (classDefFormat) - { - case 1: { - stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); - stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); - stbtt_uint8 *classDef1ValueArray = classDefTable + 6; - - if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) - return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); - break; - } - - case 2: { - stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); - stbtt_uint8 *classRangeRecords = classDefTable + 4; - - // Binary search. - stbtt_int32 l=0, r=classRangeCount-1, m; - int strawStart, strawEnd, needle=glyph; - while (l <= r) { - stbtt_uint8 *classRangeRecord; - m = (l + r) >> 1; - classRangeRecord = classRangeRecords + 6 * m; - strawStart = ttUSHORT(classRangeRecord); - strawEnd = ttUSHORT(classRangeRecord + 2); - if (needle < strawStart) - r = m - 1; - else if (needle > strawEnd) - l = m + 1; - else - return (stbtt_int32)ttUSHORT(classRangeRecord + 4); - } - break; - } - - default: - return -1; // Unsupported definition type, return an error. - } - - // "All glyphs not assigned to a class fall into class 0". (OpenType spec) - return 0; -} - -// Define to STBTT_assert(x) if you want to break on unimplemented formats. -#define STBTT_GPOS_TODO_assert(x) - -static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint16 lookupListOffset; - stbtt_uint8 *lookupList; - stbtt_uint16 lookupCount; - stbtt_uint8 *data; - stbtt_int32 i, sti; - - if (!info->gpos) return 0; - - data = info->data + info->gpos; - - if (ttUSHORT(data+0) != 1) return 0; // Major version 1 - if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 - - lookupListOffset = ttUSHORT(data+8); - lookupList = data + lookupListOffset; - lookupCount = ttUSHORT(lookupList); - - for (i=0; i= pairSetCount) return 0; - - needle=glyph2; - r=pairValueCount-1; - l=0; - - // Binary search. - while (l <= r) { - stbtt_uint16 secondGlyph; - stbtt_uint8 *pairValue; - m = (l + r) >> 1; - pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; - secondGlyph = ttUSHORT(pairValue); - straw = secondGlyph; - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else { - stbtt_int16 xAdvance = ttSHORT(pairValue + 2); - return xAdvance; - } - } - } else - return 0; - break; - } - - case 2: { - stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); - stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); - if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? - stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); - stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); - int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); - int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); - - stbtt_uint16 class1Count = ttUSHORT(table + 12); - stbtt_uint16 class2Count = ttUSHORT(table + 14); - stbtt_uint8 *class1Records, *class2Records; - stbtt_int16 xAdvance; - - if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed - if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed - - class1Records = table + 16; - class2Records = class1Records + 2 * (glyph1class * class2Count); - xAdvance = ttSHORT(class2Records + 2 * glyph2class); - return xAdvance; - } else - return 0; - break; - } - - default: - return 0; // Unsupported position format - } - } - } - - return 0; -} - -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) -{ - int xAdvance = 0; - - if (info->gpos) - xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); - else if (info->kern) - xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); - - return xAdvance; -} - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) -{ - if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs - return 0; - return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); -} - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) -{ - stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); -} - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) -{ - if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); - if (descent) *descent = ttSHORT(info->data+info->hhea + 6); - if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); -} - -STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) -{ - int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); - if (!tab) - return 0; - if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); - if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); - if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); - return 1; -} - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) -{ - *x0 = ttSHORT(info->data + info->head + 36); - *y0 = ttSHORT(info->data + info->head + 38); - *x1 = ttSHORT(info->data + info->head + 40); - *y1 = ttSHORT(info->data + info->head + 42); -} - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) -{ - int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); - return (float) height / fheight; -} - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) -{ - int unitsPerEm = ttUSHORT(info->data + info->head + 18); - return pixels / unitsPerEm; -} - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) -{ - STBTT_free(v, info->userdata); -} - -STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) -{ - int i; - stbtt_uint8 *data = info->data; - stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); - - int numEntries = ttUSHORT(svg_doc_list); - stbtt_uint8 *svg_docs = svg_doc_list + 2; - - for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) - return svg_doc; - } - return 0; -} - -STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) -{ - stbtt_uint8 *data = info->data; - stbtt_uint8 *svg_doc; - - if (info->svg == 0) - return 0; - - svg_doc = stbtt_FindSVGDoc(info, gl); - if (svg_doc != NULL) { - *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); - return ttULONG(svg_doc + 8); - } else { - return 0; - } -} - -STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) -{ - return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); -} - -////////////////////////////////////////////////////////////////////////////// -// -// antialiasing software rasterizer -// - -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning - if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { - // e.g. space character - if (ix0) *ix0 = 0; - if (iy0) *iy0 = 0; - if (ix1) *ix1 = 0; - if (iy1) *iy1 = 0; - } else { - // move to integral bboxes (treating pixels as little squares, what pixels get touched)? - if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); - if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); - if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); - if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); - } -} - -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); -} - -////////////////////////////////////////////////////////////////////////////// -// -// Rasterizer - -typedef struct stbtt__hheap_chunk -{ - struct stbtt__hheap_chunk *next; -} stbtt__hheap_chunk; - -typedef struct stbtt__hheap -{ - struct stbtt__hheap_chunk *head; - void *first_free; - int num_remaining_in_head_chunk; -} stbtt__hheap; - -static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) -{ - if (hh->first_free) { - void *p = hh->first_free; - hh->first_free = * (void **) p; - return p; - } else { - if (hh->num_remaining_in_head_chunk == 0) { - int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); - stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); - if (c == NULL) - return NULL; - c->next = hh->head; - hh->head = c; - hh->num_remaining_in_head_chunk = count; - } - --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; - } -} - -static void stbtt__hheap_free(stbtt__hheap *hh, void *p) -{ - *(void **) p = hh->first_free; - hh->first_free = p; -} - -static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) -{ - stbtt__hheap_chunk *c = hh->head; - while (c) { - stbtt__hheap_chunk *n = c->next; - STBTT_free(c, userdata); - c = n; - } -} - -typedef struct stbtt__edge { - float x0,y0, x1,y1; - int invert; -} stbtt__edge; - - -typedef struct stbtt__active_edge -{ - struct stbtt__active_edge *next; - #if STBTT_RASTERIZER_VERSION==1 - int x,dx; - float ey; - int direction; - #elif STBTT_RASTERIZER_VERSION==2 - float fx,fdx,fdy; - float direction; - float sy; - float ey; - #else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" - #endif -} stbtt__active_edge; - -#if STBTT_RASTERIZER_VERSION == 1 -#define STBTT_FIXSHIFT 10 -#define STBTT_FIX (1 << STBTT_FIXSHIFT) -#define STBTT_FIXMASK (STBTT_FIX-1) - -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - if (!z) return z; - - // round dx down to avoid overshooting - if (dxdy < 0) - z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); - else - z->dx = STBTT_ifloor(STBTT_FIX * dxdy); - - z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount - z->x -= off_x * STBTT_FIX; - - z->ey = e->y1; - z->next = 0; - z->direction = e->invert ? 1 : -1; - return z; -} -#elif STBTT_RASTERIZER_VERSION == 2 -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - //STBTT_assert(e->y0 <= start_point); - if (!z) return z; - z->fdx = dxdy; - z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; - z->fx = e->x0 + dxdy * (start_point - e->y0); - z->fx -= off_x; - z->direction = e->invert ? 1.0f : -1.0f; - z->sy = e->y0; - z->ey = e->y1; - z->next = 0; - return z; -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#if STBTT_RASTERIZER_VERSION == 1 -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) -{ - // non-zero winding fill - int x0=0, w=0; - - while (e) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->direction; - } else { - int x1 = e->x; w += e->direction; - // if we went to zero, we need to draw - if (w == 0) { - int i = x0 >> STBTT_FIXSHIFT; - int j = x1 >> STBTT_FIXSHIFT; - - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = scanline[i] + (stbtt_uint8) max_weight; - } - } - } - } - - e = e->next; - } -} - -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0; - int max_weight = (255 / vsubsample); // weight per vertical scanline - int s; // vertical subsample index - unsigned char scanline_data[512], *scanline; - - if (result->w > 512) - scanline = (unsigned char *) STBTT_malloc(result->w, userdata); - else - scanline = scanline_data; - - y = off_y * vsubsample; - e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; - - while (j < result->h) { - STBTT_memset(scanline, 0, result->w); - for (s=0; s < vsubsample; ++s) { - // find center of pixel for this scanline - float scan_y = y + 0.5f; - stbtt__active_edge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for(;;) { - int changed=0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - stbtt__active_edge *t = *step; - stbtt__active_edge *q = t->next; - - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e->y0 <= scan_y) { - if (e->y1 > scan_y) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); - if (z != NULL) { - // find insertion point - if (active == NULL) - active = z; - else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - stbtt__active_edge *p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - } - ++e; - } - - // now process all active edges in XOR fashion - if (active) - stbtt__fill_active_edges(scanline, result->w, active, max_weight); - - ++y; - } - STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} - -#elif STBTT_RASTERIZER_VERSION == 2 - -// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 -// (i.e. it has already been clipped to those) -static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) -{ - if (y0 == y1) return; - STBTT_assert(y0 < y1); - STBTT_assert(e->sy <= e->ey); - if (y0 > e->ey) return; - if (y1 < e->sy) return; - if (y0 < e->sy) { - x0 += (x1-x0) * (e->sy - y0) / (y1-y0); - y0 = e->sy; - } - if (y1 > e->ey) { - x1 += (x1-x0) * (e->ey - y1) / (y1-y0); - y1 = e->ey; - } - - if (x0 == x) - STBTT_assert(x1 <= x+1); - else if (x0 == x+1) - STBTT_assert(x1 >= x); - else if (x0 <= x) - STBTT_assert(x1 <= x); - else if (x0 >= x+1) - STBTT_assert(x1 >= x+1); - else - STBTT_assert(x1 >= x && x1 <= x+1); - - if (x0 <= x && x1 <= x) - scanline[x] += e->direction * (y1-y0); - else if (x0 >= x+1 && x1 >= x+1) - ; - else { - STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); - scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position - } -} - -static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) -{ - STBTT_assert(top_width >= 0); - STBTT_assert(bottom_width >= 0); - return (top_width + bottom_width) / 2.0f * height; -} - -static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) -{ - return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); -} - -static float stbtt__sized_triangle_area(float height, float width) -{ - return height * width / 2; -} - -static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) -{ - float y_bottom = y_top+1; - - while (e) { - // brute force every pixel - - // compute intersection points with top & bottom - STBTT_assert(e->ey >= y_top); - - if (e->fdx == 0) { - float x0 = e->fx; - if (x0 < len) { - if (x0 >= 0) { - stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); - stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); - } else { - stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); - } - } - } else { - float x0 = e->fx; - float dx = e->fdx; - float xb = x0 + dx; - float x_top, x_bottom; - float sy0,sy1; - float dy = e->fdy; - STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); - - // compute endpoints of line segment clipped to this scanline (if the - // line segment starts on this scanline. x0 is the intersection of the - // line with y_top, but that may be off the line segment. - if (e->sy > y_top) { - x_top = x0 + dx * (e->sy - y_top); - sy0 = e->sy; - } else { - x_top = x0; - sy0 = y_top; - } - if (e->ey < y_bottom) { - x_bottom = x0 + dx * (e->ey - y_top); - sy1 = e->ey; - } else { - x_bottom = xb; - sy1 = y_bottom; - } - - if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { - // from here on, we don't have to range check x values - - if ((int) x_top == (int) x_bottom) { - float height; - // simple case, only spans one pixel - int x = (int) x_top; - height = (sy1 - sy0) * e->direction; - STBTT_assert(x >= 0 && x < len); - scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); - scanline_fill[x] += height; // everything right of this pixel is filled - } else { - int x,x1,x2; - float y_crossing, y_final, step, sign, area; - // covers 2+ pixels - if (x_top > x_bottom) { - // flip scanline vertically; signed area is the same - float t; - sy0 = y_bottom - (sy0 - y_top); - sy1 = y_bottom - (sy1 - y_top); - t = sy0, sy0 = sy1, sy1 = t; - t = x_bottom, x_bottom = x_top, x_top = t; - dx = -dx; - dy = -dy; - t = x0, x0 = xb, xb = t; - } - STBTT_assert(dy >= 0); - STBTT_assert(dx >= 0); - - x1 = (int) x_top; - x2 = (int) x_bottom; - // compute intersection with y axis at x1+1 - y_crossing = y_top + dy * (x1+1 - x0); - - // compute intersection with y axis at x2 - y_final = y_top + dy * (x2 - x0); - - // x1 x_top x2 x_bottom - // y_top +------|-----+------------+------------+--------|---+------------+ - // | | | | | | - // | | | | | | - // sy0 | Txxxxx|............|............|............|............| - // y_crossing | *xxxxx.......|............|............|............| - // | | xxxxx..|............|............|............| - // | | /- xx*xxxx........|............|............| - // | | dy < | xxxxxx..|............|............| - // y_final | | \- | xx*xxx.........|............| - // sy1 | | | | xxxxxB...|............| - // | | | | | | - // | | | | | | - // y_bottom +------------+------------+------------+------------+------------+ - // - // goal is to measure the area covered by '.' in each pixel - - // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 - // @TODO: maybe test against sy1 rather than y_bottom? - if (y_crossing > y_bottom) - y_crossing = y_bottom; - - sign = e->direction; - - // area of the rectangle covered from sy0..y_crossing - area = sign * (y_crossing-sy0); - - // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) - scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); - - // check if final y_crossing is blown up; no test case for this - if (y_final > y_bottom) { - y_final = y_bottom; - dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom - } - - // in second pixel, area covered by line segment found in first pixel - // is always a rectangle 1 wide * the height of that line segment; this - // is exactly what the variable 'area' stores. it also gets a contribution - // from the line segment within it. the THIRD pixel will get the first - // pixel's rectangle contribution, the second pixel's rectangle contribution, - // and its own contribution. the 'own contribution' is the same in every pixel except - // the leftmost and rightmost, a trapezoid that slides down in each pixel. - // the second pixel's contribution to the third pixel will be the - // rectangle 1 wide times the height change in the second pixel, which is dy. - - step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, - // which multiplied by 1-pixel-width is how much pixel area changes for each step in x - // so the area advances by 'step' every time - - for (x = x1+1; x < x2; ++x) { - scanline[x] += area + step/2; // area of trapezoid is 1*step/2 - area += step; - } - STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down - STBTT_assert(sy1 > y_final-0.01f); - - // area covered in the last pixel is the rectangle from all the pixels to the left, - // plus the trapezoid filled by the line segment in this pixel all the way to the right edge - scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); - - // the rest of the line is filled based on the total height of the line segment in this pixel - scanline_fill[x2] += sign * (sy1-sy0); - } - } else { - // if edge goes outside of box we're drawing, we require - // clipping logic. since this does not match the intended use - // of this library, we use a different, very slow brute - // force implementation - // note though that this does happen some of the time because - // x_top and x_bottom can be extrapolated at the top & bottom of - // the shape and actually lie outside the bounding box - int x; - for (x=0; x < len; ++x) { - // cases: - // - // there can be up to two intersections with the pixel. any intersection - // with left or right edges can be handled by splitting into two (or three) - // regions. intersections with top & bottom do not necessitate case-wise logic. - // - // the old way of doing this found the intersections with the left & right edges, - // then used some simple logic to produce up to three segments in sorted order - // from top-to-bottom. however, this had a problem: if an x edge was epsilon - // across the x border, then the corresponding y position might not be distinct - // from the other y segment, and it might ignored as an empty segment. to avoid - // that, we need to explicitly produce segments based on x positions. - - // rename variables to clearly-defined pairs - float y0 = y_top; - float x1 = (float) (x); - float x2 = (float) (x+1); - float x3 = xb; - float y3 = y_bottom; - - // x = e->x + e->dx * (y-y_top) - // (y-y_top) = (x - e->x) / e->dx - // y = (x - e->x) / e->dx + y_top - float y1 = (x - x0) / dx + y_top; - float y2 = (x+1 - x0) / dx + y_top; - - if (x0 < x1 && x3 > x2) { // three segments descending down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x1 && x0 > x2) { // three segments descending down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else { // one segment - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); - } - } - } - } - e = e->next; - } -} - -// directly AA rasterize edges w/o supersampling -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0, i; - float scanline_data[129], *scanline, *scanline2; - - STBTT__NOTUSED(vsubsample); - - if (result->w > 64) - scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); - else - scanline = scanline_data; - - scanline2 = scanline + result->w; - - y = off_y; - e[n].y0 = (float) (off_y + result->h) + 1; - - while (j < result->h) { - // find center of pixel for this scanline - float scan_y_top = y + 0.0f; - float scan_y_bottom = y + 1.0f; - stbtt__active_edge **step = &active; - - STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); - STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); - - // update all active edges; - // remove all active edges that terminate before the top of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y_top) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - step = &((*step)->next); // advance through list - } - } - - // insert all edges that start before the bottom of this scanline - while (e->y0 <= scan_y_bottom) { - if (e->y0 != e->y1) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); - if (z != NULL) { - if (j == 0 && off_y != 0) { - if (z->ey < scan_y_top) { - // this can happen due to subpixel positioning and some kind of fp rounding error i think - z->ey = scan_y_top; - } - } - STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds - // insert at front - z->next = active; - active = z; - } - } - ++e; - } - - // now process all active edges - if (active) - stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); - - { - float sum = 0; - for (i=0; i < result->w; ++i) { - float k; - int m; - sum += scanline2[i]; - k = scanline[i] + sum; - k = (float) STBTT_fabs(k)*255 + 0.5f; - m = (int) k; - if (m > 255) m = 255; - result->pixels[j*result->stride + i] = (unsigned char) m; - } - } - // advance all the edges - step = &active; - while (*step) { - stbtt__active_edge *z = *step; - z->fx += z->fdx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - - ++y; - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) - -static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) -{ - int i,j; - for (i=1; i < n; ++i) { - stbtt__edge t = p[i], *a = &t; - j = i; - while (j > 0) { - stbtt__edge *b = &p[j-1]; - int c = STBTT__COMPARE(a,b); - if (!c) break; - p[j] = p[j-1]; - --j; - } - if (i != j) - p[j] = t; - } -} - -static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) -{ - /* threshold for transitioning to insertion sort */ - while (n > 12) { - stbtt__edge t; - int c01,c12,c,m,i,j; - - /* compute median of three */ - m = n >> 1; - c01 = STBTT__COMPARE(&p[0],&p[m]); - c12 = STBTT__COMPARE(&p[m],&p[n-1]); - /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ - if (c01 != c12) { - /* otherwise, we'll need to swap something else to middle */ - int z; - c = STBTT__COMPARE(&p[0],&p[n-1]); - /* 0>mid && midn => n; 0 0 */ - /* 0n: 0>n => 0; 0 n */ - z = (c == c12) ? 0 : n-1; - t = p[z]; - p[z] = p[m]; - p[m] = t; - } - /* now p[m] is the median-of-three */ - /* swap it to the beginning so it won't move around */ - t = p[0]; - p[0] = p[m]; - p[m] = t; - - /* partition loop */ - i=1; - j=n-1; - for(;;) { - /* handling of equality is crucial here */ - /* for sentinels & efficiency with duplicates */ - for (;;++i) { - if (!STBTT__COMPARE(&p[i], &p[0])) break; - } - for (;;--j) { - if (!STBTT__COMPARE(&p[0], &p[j])) break; - } - /* make sure we haven't crossed */ - if (i >= j) break; - t = p[i]; - p[i] = p[j]; - p[j] = t; - - ++i; - --j; - } - /* recurse on smaller side, iterate on larger */ - if (j < (n-i)) { - stbtt__sort_edges_quicksort(p,j); - p = p+i; - n = n-i; - } else { - stbtt__sort_edges_quicksort(p+i, n-i); - n = j; - } - } -} - -static void stbtt__sort_edges(stbtt__edge *p, int n) -{ - stbtt__sort_edges_quicksort(p, n); - stbtt__sort_edges_ins_sort(p, n); -} - -typedef struct -{ - float x,y; -} stbtt__point; - -static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) -{ - float y_scale_inv = invert ? -scale_y : scale_y; - stbtt__edge *e; - int n,i,j,k,m; -#if STBTT_RASTERIZER_VERSION == 1 - int vsubsample = result->h < 8 ? 15 : 5; -#elif STBTT_RASTERIZER_VERSION == 2 - int vsubsample = 1; -#else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - // vsubsample should divide 255 evenly; otherwise we won't reach full opacity - - // now we have to blow out the windings into explicit edge lists - n = 0; - for (i=0; i < windings; ++i) - n += wcount[i]; - - e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel - if (e == 0) return; - n = 0; - - m=0; - for (i=0; i < windings; ++i) { - stbtt__point *p = pts + m; - m += wcount[i]; - j = wcount[i]-1; - for (k=0; k < wcount[i]; j=k++) { - int a=k,b=j; - // skip the edge if horizontal - if (p[j].y == p[k].y) - continue; - // add edge from j to k to the list - e[n].invert = 0; - if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { - e[n].invert = 1; - a=j,b=k; - } - e[n].x0 = p[a].x * scale_x + shift_x; - e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; - e[n].x1 = p[b].x * scale_x + shift_x; - e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; - ++n; - } - } - - // now sort the edges by their highest point (should snap to integer, and then by x) - //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); - stbtt__sort_edges(e, n); - - // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule - stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); - - STBTT_free(e, userdata); -} - -static void stbtt__add_point(stbtt__point *points, int n, float x, float y) -{ - if (!points) return; // during first pass, it's unallocated - points[n].x = x; - points[n].y = y; -} - -// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching -static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) -{ - // midpoint - float mx = (x0 + 2*x1 + x2)/4; - float my = (y0 + 2*y1 + y2)/4; - // versus directly drawn line - float dx = (x0+x2)/2 - mx; - float dy = (y0+y2)/2 - my; - if (n > 16) // 65536 segments on one curve better be enough! - return 1; - if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA - stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x2,y2); - *num_points = *num_points+1; - } - return 1; -} - -static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) -{ - // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough - float dx0 = x1-x0; - float dy0 = y1-y0; - float dx1 = x2-x1; - float dy1 = y2-y1; - float dx2 = x3-x2; - float dy2 = y3-y2; - float dx = x3-x0; - float dy = y3-y0; - float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); - float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); - float flatness_squared = longlen*longlen-shortlen*shortlen; - - if (n > 16) // 65536 segments on one curve better be enough! - return; - - if (flatness_squared > objspace_flatness_squared) { - float x01 = (x0+x1)/2; - float y01 = (y0+y1)/2; - float x12 = (x1+x2)/2; - float y12 = (y1+y2)/2; - float x23 = (x2+x3)/2; - float y23 = (y2+y3)/2; - - float xa = (x01+x12)/2; - float ya = (y01+y12)/2; - float xb = (x12+x23)/2; - float yb = (y12+y23)/2; - - float mx = (xa+xb)/2; - float my = (ya+yb)/2; - - stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x3,y3); - *num_points = *num_points+1; - } -} - -// returns number of contours -static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) -{ - stbtt__point *points=0; - int num_points=0; - - float objspace_flatness_squared = objspace_flatness * objspace_flatness; - int i,n=0,start=0, pass; - - // count how many "moves" there are to get the contour count - for (i=0; i < num_verts; ++i) - if (vertices[i].type == STBTT_vmove) - ++n; - - *num_contours = n; - if (n == 0) return 0; - - *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); - - if (*contour_lengths == 0) { - *num_contours = 0; - return 0; - } - - // make two passes through the points so we don't need to realloc - for (pass=0; pass < 2; ++pass) { - float x=0,y=0; - if (pass == 1) { - points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); - if (points == NULL) goto error; - } - num_points = 0; - n= -1; - for (i=0; i < num_verts; ++i) { - switch (vertices[i].type) { - case STBTT_vmove: - // start the next contour - if (n >= 0) - (*contour_lengths)[n] = num_points - start; - ++n; - start = num_points; - - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x,y); - break; - case STBTT_vline: - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x, y); - break; - case STBTT_vcurve: - stbtt__tesselate_curve(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - case STBTT_vcubic: - stbtt__tesselate_cubic(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].cx1, vertices[i].cy1, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - } - } - (*contour_lengths)[n] = num_points - start; - } - - return points; -error: - STBTT_free(points, userdata); - STBTT_free(*contour_lengths, userdata); - *contour_lengths = 0; - *num_contours = 0; - return NULL; -} - -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) -{ - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count = 0; - int *winding_lengths = NULL; - stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); - if (windings) { - stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); - STBTT_free(winding_lengths, userdata); - STBTT_free(windings, userdata); - } -} - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - int ix0,iy0,ix1,iy1; - stbtt__bitmap gbm; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) { - STBTT_free(vertices, info->userdata); - return NULL; - } - scale_y = scale_x; - } - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); - - // now we get the size - gbm.w = (ix1 - ix0); - gbm.h = (iy1 - iy0); - gbm.pixels = NULL; // in case we error - - if (width ) *width = gbm.w; - if (height) *height = gbm.h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - if (gbm.w && gbm.h) { - gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); - if (gbm.pixels) { - gbm.stride = gbm.w; - - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); - } - } - STBTT_free(vertices, info->userdata); - return gbm.pixels; -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) -{ - int ix0,iy0; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); - gbm.pixels = output; - gbm.w = out_w; - gbm.h = out_h; - gbm.stride = out_stride; - - if (gbm.w && gbm.h) - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); - - STBTT_free(vertices, info->userdata); -} - -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) -{ - stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); -} - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-CRAPPY packing to keep source code small - -static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata) -{ - float scale; - int x,y,bottom_y, i; - stbtt_fontinfo f; - f.userdata = NULL; - if (!stbtt_InitFont(&f, data, offset)) - return -1; - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - x=y=1; - bottom_y = 1; - - scale = stbtt_ScaleForPixelHeight(&f, pixel_height); - - for (i=0; i < num_chars; ++i) { - int advance, lsb, x0,y0,x1,y1,gw,gh; - int g = stbtt_FindGlyphIndex(&f, first_char + i); - stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); - stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); - gw = x1-x0; - gh = y1-y0; - if (x + gw + 1 >= pw) - y = bottom_y, x = 1; // advance to next row - if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row - return -i; - STBTT_assert(x+gw < pw); - STBTT_assert(y+gh < ph); - stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); - chardata[i].x0 = (stbtt_int16) x; - chardata[i].y0 = (stbtt_int16) y; - chardata[i].x1 = (stbtt_int16) (x + gw); - chardata[i].y1 = (stbtt_int16) (y + gh); - chardata[i].xadvance = scale * advance; - chardata[i].xoff = (float) x0; - chardata[i].yoff = (float) y0; - x = x + gw + 1; - if (y+gh+1 > bottom_y) - bottom_y = y+gh+1; - } - return bottom_y; -} - -STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) -{ - float d3d_bias = opengl_fillrule ? 0 : -0.5f; - float ipw = 1.0f / pw, iph = 1.0f / ph; - const stbtt_bakedchar *b = chardata + char_index; - int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); - int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); - - q->x0 = round_x + d3d_bias; - q->y0 = round_y + d3d_bias; - q->x1 = round_x + b->x1 - b->x0 + d3d_bias; - q->y1 = round_y + b->y1 - b->y0 + d3d_bias; - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// rectangle packing replacement routines if you don't have stb_rect_pack.h -// - -#ifndef STB_RECT_PACK_VERSION - -typedef int stbrp_coord; - -//////////////////////////////////////////////////////////////////////////////////// -// // -// // -// COMPILER WARNING ?!?!? // -// // -// // -// if you get a compile warning due to these symbols being defined more than // -// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // -// // -//////////////////////////////////////////////////////////////////////////////////// - -typedef struct -{ - int width,height; - int x,y,bottom_y; -} stbrp_context; - -typedef struct -{ - unsigned char x; -} stbrp_node; - -struct stbrp_rect -{ - stbrp_coord x,y; - int id,w,h,was_packed; -}; - -static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) -{ - con->width = pw; - con->height = ph; - con->x = 0; - con->y = 0; - con->bottom_y = 0; - STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); -} - -static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) -{ - int i; - for (i=0; i < num_rects; ++i) { - if (con->x + rects[i].w > con->width) { - con->x = 0; - con->y = con->bottom_y; - } - if (con->y + rects[i].h > con->height) - break; - rects[i].x = con->x; - rects[i].y = con->y; - rects[i].was_packed = 1; - con->x += rects[i].w; - if (con->y + rects[i].h > con->bottom_y) - con->bottom_y = con->y + rects[i].h; - } - for ( ; i < num_rects; ++i) - rects[i].was_packed = 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If -// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) -{ - stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); - int num_nodes = pw - padding; - stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); - - if (context == NULL || nodes == NULL) { - if (context != NULL) STBTT_free(context, alloc_context); - if (nodes != NULL) STBTT_free(nodes , alloc_context); - return 0; - } - - spc->user_allocator_context = alloc_context; - spc->width = pw; - spc->height = ph; - spc->pixels = pixels; - spc->pack_info = context; - spc->nodes = nodes; - spc->padding = padding; - spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; - spc->h_oversample = 1; - spc->v_oversample = 1; - spc->skip_missing = 0; - - stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); - - if (pixels) - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - - return 1; -} - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) -{ - STBTT_free(spc->nodes , spc->user_allocator_context); - STBTT_free(spc->pack_info, spc->user_allocator_context); -} - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) -{ - STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); - STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); - if (h_oversample <= STBTT_MAX_OVERSAMPLE) - spc->h_oversample = h_oversample; - if (v_oversample <= STBTT_MAX_OVERSAMPLE) - spc->v_oversample = v_oversample; -} - -STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) -{ - spc->skip_missing = skip; -} - -#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) - -static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_w = w - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < h; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < w; ++i) { - STBTT_assert(pixels[i] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i] = (unsigned char) (total / kernel_width); - } - - pixels += stride_in_bytes; - } -} - -static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_h = h - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < w; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < h; ++i) { - STBTT_assert(pixels[i*stride_in_bytes] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - - pixels += 1; - } -} - -static float stbtt__oversample_shift(int oversample) -{ - if (!oversample) - return 0.0f; - - // The prefilter is a box filter of width "oversample", - // which shifts phase by (oversample - 1)/2 pixels in - // oversampled space. We want to shift in the opposite - // direction to counter this. - return (float)-(oversample - 1) / (2.0f * (float)oversample); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k; - int missing_glyph_added = 0; - - k=0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - ranges[i].h_oversample = (unsigned char) spc->h_oversample; - ranges[i].v_oversample = (unsigned char) spc->v_oversample; - for (j=0; j < ranges[i].num_chars; ++j) { - int x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { - rects[k].w = rects[k].h = 0; - } else { - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); - if (glyph == 0) - missing_glyph_added = 1; - } - ++k; - } - } - - return k; -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, - output, - out_w - (prefilter_x - 1), - out_h - (prefilter_y - 1), - out_stride, - scale_x, - scale_y, - shift_x, - shift_y, - glyph); - - if (prefilter_x > 1) - stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); - - if (prefilter_y > 1) - stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); - - *sub_x = stbtt__oversample_shift(prefilter_x); - *sub_y = stbtt__oversample_shift(prefilter_y); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k, missing_glyph = -1, return_value = 1; - - // save current values - int old_h_over = spc->h_oversample; - int old_v_over = spc->v_oversample; - - k = 0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - float recip_h,recip_v,sub_x,sub_y; - spc->h_oversample = ranges[i].h_oversample; - spc->v_oversample = ranges[i].v_oversample; - recip_h = 1.0f / spc->h_oversample; - recip_v = 1.0f / spc->v_oversample; - sub_x = stbtt__oversample_shift(spc->h_oversample); - sub_y = stbtt__oversample_shift(spc->v_oversample); - for (j=0; j < ranges[i].num_chars; ++j) { - stbrp_rect *r = &rects[k]; - if (r->was_packed && r->w != 0 && r->h != 0) { - stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; - int advance, lsb, x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbrp_coord pad = (stbrp_coord) spc->padding; - - // pad on left and top - r->x += pad; - r->y += pad; - r->w -= pad; - r->h -= pad; - stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); - stbtt_GetGlyphBitmapBox(info, glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - &x0,&y0,&x1,&y1); - stbtt_MakeGlyphBitmapSubpixel(info, - spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w - spc->h_oversample+1, - r->h - spc->v_oversample+1, - spc->stride_in_bytes, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - glyph); - - if (spc->h_oversample > 1) - stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->h_oversample); - - if (spc->v_oversample > 1) - stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->v_oversample); - - bc->x0 = (stbtt_int16) r->x; - bc->y0 = (stbtt_int16) r->y; - bc->x1 = (stbtt_int16) (r->x + r->w); - bc->y1 = (stbtt_int16) (r->y + r->h); - bc->xadvance = scale * advance; - bc->xoff = (float) x0 * recip_h + sub_x; - bc->yoff = (float) y0 * recip_v + sub_y; - bc->xoff2 = (x0 + r->w) * recip_h + sub_x; - bc->yoff2 = (y0 + r->h) * recip_v + sub_y; - - if (glyph == 0) - missing_glyph = j; - } else if (spc->skip_missing) { - return_value = 0; - } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { - ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; - } else { - return_value = 0; // if any fail, report failure - } - - ++k; - } - } - - // restore original values - spc->h_oversample = old_h_over; - spc->v_oversample = old_v_over; - - return return_value; -} - -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) -{ - stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); -} - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) -{ - stbtt_fontinfo info; - int i,j,n, return_value = 1; - //stbrp_context *context = (stbrp_context *) spc->pack_info; - stbrp_rect *rects; - - // flag all characters as NOT packed - for (i=0; i < num_ranges; ++i) - for (j=0; j < ranges[i].num_chars; ++j) - ranges[i].chardata_for_range[j].x0 = - ranges[i].chardata_for_range[j].y0 = - ranges[i].chardata_for_range[j].x1 = - ranges[i].chardata_for_range[j].y1 = 0; - - n = 0; - for (i=0; i < num_ranges; ++i) - n += ranges[i].num_chars; - - rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); - if (rects == NULL) - return 0; - - info.userdata = spc->user_allocator_context; - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); - - n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); - - stbtt_PackFontRangesPackRects(spc, rects, n); - - return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); - - STBTT_free(rects, spc->user_allocator_context); - return return_value; -} - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, - int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) -{ - stbtt_pack_range range; - range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; - range.array_of_unicode_codepoints = NULL; - range.num_chars = num_chars_in_range; - range.chardata_for_range = chardata_for_range; - range.font_size = font_size; - return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); -} - -STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) -{ - int i_ascent, i_descent, i_lineGap; - float scale; - stbtt_fontinfo info; - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); - scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); - stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); - *ascent = (float) i_ascent * scale; - *descent = (float) i_descent * scale; - *lineGap = (float) i_lineGap * scale; -} - -STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) -{ - float ipw = 1.0f / pw, iph = 1.0f / ph; - const stbtt_packedchar *b = chardata + char_index; - - if (align_to_integer) { - float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); - float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); - q->x0 = x; - q->y0 = y; - q->x1 = x + b->xoff2 - b->xoff; - q->y1 = y + b->yoff2 - b->yoff; - } else { - q->x0 = *xpos + b->xoff; - q->y0 = *ypos + b->yoff; - q->x1 = *xpos + b->xoff2; - q->y1 = *ypos + b->yoff2; - } - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// sdf computation -// - -#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) -#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) - -static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) -{ - float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; - float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; - float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; - float roperp = orig[1]*ray[0] - orig[0]*ray[1]; - - float a = q0perp - 2*q1perp + q2perp; - float b = q1perp - q0perp; - float c = q0perp - roperp; - - float s0 = 0., s1 = 0.; - int num_s = 0; - - if (a != 0.0) { - float discr = b*b - a*c; - if (discr > 0.0) { - float rcpna = -1 / a; - float d = (float) STBTT_sqrt(discr); - s0 = (b+d) * rcpna; - s1 = (b-d) * rcpna; - if (s0 >= 0.0 && s0 <= 1.0) - num_s = 1; - if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { - if (num_s == 0) s0 = s1; - ++num_s; - } - } - } else { - // 2*b*s + c = 0 - // s = -c / (2*b) - s0 = c / (-2 * b); - if (s0 >= 0.0 && s0 <= 1.0) - num_s = 1; - } - - if (num_s == 0) - return 0; - else { - float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); - float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; - - float q0d = q0[0]*rayn_x + q0[1]*rayn_y; - float q1d = q1[0]*rayn_x + q1[1]*rayn_y; - float q2d = q2[0]*rayn_x + q2[1]*rayn_y; - float rod = orig[0]*rayn_x + orig[1]*rayn_y; - - float q10d = q1d - q0d; - float q20d = q2d - q0d; - float q0rd = q0d - rod; - - hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; - hits[0][1] = a*s0+b; - - if (num_s > 1) { - hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; - hits[1][1] = a*s1+b; - return 2; - } else { - return 1; - } - } -} - -static int equal(float *a, float *b) -{ - return (a[0] == b[0] && a[1] == b[1]); -} - -static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) -{ - int i; - float orig[2], ray[2] = { 1, 0 }; - float y_frac; - int winding = 0; - - // make sure y never passes through a vertex of the shape - y_frac = (float) STBTT_fmod(y, 1.0f); - if (y_frac < 0.01f) - y += 0.01f; - else if (y_frac > 0.99f) - y -= 0.01f; - - orig[0] = x; - orig[1] = y; - - // test a ray from (-infinity,y) to (x,y) - for (i=0; i < nverts; ++i) { - if (verts[i].type == STBTT_vline) { - int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; - int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; - if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { - float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) - winding += (y0 < y1) ? 1 : -1; - } - } - if (verts[i].type == STBTT_vcurve) { - int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; - int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; - int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; - int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); - int by = STBTT_max(y0,STBTT_max(y1,y2)); - if (y > ay && y < by && x > ax) { - float q0[2],q1[2],q2[2]; - float hits[2][2]; - q0[0] = (float)x0; - q0[1] = (float)y0; - q1[0] = (float)x1; - q1[1] = (float)y1; - q2[0] = (float)x2; - q2[1] = (float)y2; - if (equal(q0,q1) || equal(q1,q2)) { - x0 = (int)verts[i-1].x; - y0 = (int)verts[i-1].y; - x1 = (int)verts[i ].x; - y1 = (int)verts[i ].y; - if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { - float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) - winding += (y0 < y1) ? 1 : -1; - } - } else { - int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); - if (num_hits >= 1) - if (hits[0][0] < 0) - winding += (hits[0][1] < 0 ? -1 : 1); - if (num_hits >= 2) - if (hits[1][0] < 0) - winding += (hits[1][1] < 0 ? -1 : 1); - } - } - } - } - return winding; -} - -static float stbtt__cuberoot( float x ) -{ - if (x<0) - return -(float) STBTT_pow(-x,1.0f/3.0f); - else - return (float) STBTT_pow( x,1.0f/3.0f); -} - -// x^3 + a*x^2 + b*x + c = 0 -static int stbtt__solve_cubic(float a, float b, float c, float* r) -{ - float s = -a / 3; - float p = b - a*a / 3; - float q = a * (2*a*a - 9*b) / 27 + c; - float p3 = p*p*p; - float d = q*q + 4*p3 / 27; - if (d >= 0) { - float z = (float) STBTT_sqrt(d); - float u = (-q + z) / 2; - float v = (-q - z) / 2; - u = stbtt__cuberoot(u); - v = stbtt__cuberoot(v); - r[0] = s + u + v; - return 1; - } else { - float u = (float) STBTT_sqrt(-p/3); - float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative - float m = (float) STBTT_cos(v); - float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; - r[0] = s + u * 2 * m; - r[1] = s - u * (m + n); - r[2] = s - u * (m - n); - - //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? - //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); - //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); - return 3; - } -} - -STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) -{ - float scale_x = scale, scale_y = scale; - int ix0,iy0,ix1,iy1; - int w,h; - unsigned char *data; - - if (scale == 0) return NULL; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); - - // if empty, return NULL - if (ix0 == ix1 || iy0 == iy1) - return NULL; - - ix0 -= padding; - iy0 -= padding; - ix1 += padding; - iy1 += padding; - - w = (ix1 - ix0); - h = (iy1 - iy0); - - if (width ) *width = w; - if (height) *height = h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - // invert for y-downwards bitmaps - scale_y = -scale_y; - - { - int x,y,i,j; - float *precompute; - stbtt_vertex *verts; - int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); - data = (unsigned char *) STBTT_malloc(w * h, info->userdata); - precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); - - for (i=0,j=num_verts-1; i < num_verts; j=i++) { - if (verts[i].type == STBTT_vline) { - float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; - float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; - float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); - precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; - } else if (verts[i].type == STBTT_vcurve) { - float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; - float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; - float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; - float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; - float len2 = bx*bx + by*by; - if (len2 != 0.0f) - precompute[i] = 1.0f / (bx*bx + by*by); - else - precompute[i] = 0.0f; - } else - precompute[i] = 0.0f; - } - - for (y=iy0; y < iy1; ++y) { - for (x=ix0; x < ix1; ++x) { - float val; - float min_dist = 999999.0f; - float sx = (float) x + 0.5f; - float sy = (float) y + 0.5f; - float x_gspace = (sx / scale_x); - float y_gspace = (sy / scale_y); - - int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path - - for (i=0; i < num_verts; ++i) { - float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; - - if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { - float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; - - float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); - if (dist2 < min_dist*min_dist) - min_dist = (float) STBTT_sqrt(dist2); - - // coarse culling against bbox - //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && - // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) - dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; - STBTT_assert(i != 0); - if (dist < min_dist) { - // check position along line - // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) - // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) - float dx = x1-x0, dy = y1-y0; - float px = x0-sx, py = y0-sy; - // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy - // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve - float t = -(px*dx + py*dy) / (dx*dx + dy*dy); - if (t >= 0.0f && t <= 1.0f) - min_dist = dist; - } - } else if (verts[i].type == STBTT_vcurve) { - float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; - float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; - float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); - float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); - float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); - float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); - // coarse culling against bbox to avoid computing cubic unnecessarily - if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { - int num=0; - float ax = x1-x0, ay = y1-y0; - float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; - float mx = x0 - sx, my = y0 - sy; - float res[3] = {0.f,0.f,0.f}; - float px,py,t,it,dist2; - float a_inv = precompute[i]; - if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula - float a = 3*(ax*bx + ay*by); - float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); - float c = mx*ax+my*ay; - if (a == 0.0) { // if a is 0, it's linear - if (b != 0.0) { - res[num++] = -c/b; - } - } else { - float discriminant = b*b - 4*a*c; - if (discriminant < 0) - num = 0; - else { - float root = (float) STBTT_sqrt(discriminant); - res[0] = (-b - root)/(2*a); - res[1] = (-b + root)/(2*a); - num = 2; // don't bother distinguishing 1-solution case, as code below will still work - } - } - } else { - float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point - float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; - float d = (mx*ax+my*ay) * a_inv; - num = stbtt__solve_cubic(b, c, d, res); - } - dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); - if (dist2 < min_dist*min_dist) - min_dist = (float) STBTT_sqrt(dist2); - - if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { - t = res[0], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { - t = res[1], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { - t = res[2], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - } - } - } - if (winding == 0) - min_dist = -min_dist; // if outside the shape, value is negative - val = onedge_value + pixel_dist_scale * min_dist; - if (val < 0) - val = 0; - else if (val > 255) - val = 255; - data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; - } - } - STBTT_free(precompute, info->userdata); - STBTT_free(verts, info->userdata); - } - return data; -} - -STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -////////////////////////////////////////////////////////////////////////////// -// -// font name matching -- recommended not to use this -// - -// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) -{ - stbtt_int32 i=0; - - // convert utf16 to utf8 and compare the results while converting - while (len2) { - stbtt_uint16 ch = s2[0]*256 + s2[1]; - if (ch < 0x80) { - if (i >= len1) return -1; - if (s1[i++] != ch) return -1; - } else if (ch < 0x800) { - if (i+1 >= len1) return -1; - if (s1[i++] != 0xc0 + (ch >> 6)) return -1; - if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; - } else if (ch >= 0xd800 && ch < 0xdc00) { - stbtt_uint32 c; - stbtt_uint16 ch2 = s2[2]*256 + s2[3]; - if (i+3 >= len1) return -1; - c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; - if (s1[i++] != 0xf0 + (c >> 18)) return -1; - if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; - s2 += 2; // plus another 2 below - len2 -= 2; - } else if (ch >= 0xdc00 && ch < 0xe000) { - return -1; - } else { - if (i+2 >= len1) return -1; - if (s1[i++] != 0xe0 + (ch >> 12)) return -1; - if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; - } - s2 += 2; - len2 -= 2; - } - return i; -} - -static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) -{ - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); -} - -// returns results in whatever encoding you request... but note that 2-byte encodings -// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) -{ - stbtt_int32 i,count,stringOffset; - stbtt_uint8 *fc = font->data; - stbtt_uint32 offset = font->fontstart; - stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return NULL; - - count = ttUSHORT(fc+nm+2); - stringOffset = nm + ttUSHORT(fc+nm+4); - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) - && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { - *length = ttUSHORT(fc+loc+8); - return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); - } - } - return NULL; -} - -static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) -{ - stbtt_int32 i; - stbtt_int32 count = ttUSHORT(fc+nm+2); - stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); - - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - stbtt_int32 id = ttUSHORT(fc+loc+6); - if (id == target_id) { - // find the encoding - stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); - - // is this a Unicode encoding? - if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { - stbtt_int32 slen = ttUSHORT(fc+loc+8); - stbtt_int32 off = ttUSHORT(fc+loc+10); - - // check if there's a prefix match - stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); - if (matchlen >= 0) { - // check for target_id+1 immediately following, with same encoding & language - if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { - slen = ttUSHORT(fc+loc+12+8); - off = ttUSHORT(fc+loc+12+10); - if (slen == 0) { - if (matchlen == nlen) - return 1; - } else if (matchlen < nlen && name[matchlen] == ' ') { - ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) - return 1; - } - } else { - // if nothing immediately following - if (matchlen == nlen) - return 1; - } - } - } - - // @TODO handle other encodings - } - } - return 0; -} - -static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) -{ - stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); - stbtt_uint32 nm,hd; - if (!stbtt__isfont(fc+offset)) return 0; - - // check italics/bold/underline flags in macStyle... - if (flags) { - hd = stbtt__find_table(fc, offset, "head"); - if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; - } - - nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return 0; - - if (flags) { - // if we checked the macStyle flags, then just check the family and ignore the subfamily - if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } else { - if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } - - return 0; -} - -static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) -{ - stbtt_int32 i; - for (i=0;;++i) { - stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); - if (off < 0) return off; - if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) - return off; - } -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, - float pixel_height, unsigned char *pixels, int pw, int ph, - int first_char, int num_chars, stbtt_bakedchar *chardata) -{ - return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); -} - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) -{ - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); -} - -STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) -{ - return stbtt_GetNumberOfFonts_internal((unsigned char *) data); -} - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) -{ - return stbtt_InitFont_internal(info, (unsigned char *) data, offset); -} - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) -{ - return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); -} - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) -{ - return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#endif // STB_TRUETYPE_IMPLEMENTATION - - -// FULL VERSION HISTORY -// -// 1.25 (2021-07-11) many fixes -// 1.24 (2020-02-05) fix warning -// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) -// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined -// 1.21 (2019-02-25) fix warning -// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() -// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod -// 1.18 (2018-01-29) add missing function -// 1.17 (2017-07-23) make more arguments const; doc fix -// 1.16 (2017-07-12) SDF support -// 1.15 (2017-03-03) make more arguments const -// 1.14 (2017-01-16) num-fonts-in-TTC function -// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts -// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) allow user-defined fabs() replacement -// fix memory leak if fontsize=0.0 -// fix warning from duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// allow PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes -// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ -// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match -// non-oversampled; STBTT_POINT_SIZE for packed case only -// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling -// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) -// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID -// 0.8b (2014-07-07) fix a warning -// 0.8 (2014-05-25) fix a few more warnings -// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back -// 0.6c (2012-07-24) improve documentation -// 0.6b (2012-07-20) fix a few more warnings -// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, -// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty -// 0.5 (2011-12-09) bugfixes: -// subpixel glyph renderer computed wrong bounding box -// first vertex of shape can be off-curve (FreeSans) -// 0.4b (2011-12-03) fixed an error in the font baking example -// 0.4 (2011-12-01) kerning, subpixel rendering (tor) -// bugfixes for: -// codepoint-to-glyph conversion using table fmt=12 -// codepoint-to-glyph conversion using table fmt=4 -// stbtt_GetBakedQuad with non-square texture (Zer) -// updated Hello World! sample to use kerning and subpixel -// fixed some warnings -// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) -// userdata, malloc-from-userdata, non-zero fill (stb) -// 0.2 (2009-03-11) Fix unsigned/signed char warnings -// 0.1 (2009-03-09) First public release -// - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/modules/ui/turbobadger/tb/utf8/utf8.cpp b/src/modules/ui/turbobadger/tb/utf8/utf8.cpp deleted file mode 100644 index 53e542261..000000000 --- a/src/modules/ui/turbobadger/tb/utf8/utf8.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/** - * @file - */ - -#include "utf8/utf8.h" - -namespace utf8 { - -/** is c the start of a UTF-8 sequence? */ -#define isutf(c) (((c)&0xC0) != 0x80) - -UCS4 decode(const char *&src, const char *srcEnd) { - const char *start = src; - - if (src == srcEnd) { - return 0; - } - - unsigned char ch1 = *(src++); - if (ch1 <= 0x7F) { - return ch1; - } - - // should not have continuation character here - if ((ch1 & 0xC0) != 0x80 && src < srcEnd) { - unsigned char ch2 = *(src++); - // should be continuation - if ((ch2 & 0xC0) != 0x80) { - goto invalid; - } - if ((ch1 & 0xE0) == 0xC0) { - if ((ch2 & 0xC0) == 0x80) { - unsigned int rv = ((ch1 & 0x1F) << 6) + (ch2 & 0x3F); - if (rv >= 0x80) { - return rv; - } - } - goto invalid; - } - if (src < srcEnd) { - unsigned char ch3 = *(src++); - // should be continuation - if ((ch3 & 0xC0) != 0x80) { - goto invalid; - } - if ((ch1 & 0xF0) == 0xE0) { - unsigned rv = ((ch1 & 0x0F) << 12) + ((ch2 & 0x3F) << 6) + (ch3 & 0x3F); - if (rv <= 0x800) { - goto invalid; - } - return rv; - } - if (src < srcEnd) { - unsigned char ch4 = *(src++); - if ((ch4 & 0xC0) != 0x80) { - goto invalid; - } - UCS4 rv = ((ch1 & 0x07) << 18) + ((ch2 & 0x3F) << 12) + ((ch3 & 0x3F) << 6) + (ch4 & 0x3F); - if (rv > 0xFFFF) - return rv; - } - } - } -invalid: - src = start; - return 0xFFFF; -} - -int encode(UCS4 ch, char *dst) { - if (ch < 0x80) { - dst[0] = (char)ch; - return 1; - } - if (ch < 0x800) { - dst[0] = (char)(0xC0 + (ch >> 6)); - dst[1] = (char)(0x80 + (ch & 0x3F)); - return 2; - } - if (ch < 0x10000) { - dst[0] = (char)(0xE0 + (ch >> 12)); - dst[1] = (char)(0x80 + ((ch >> 6) & 0x3F)); - dst[2] = (char)(0x80 + (ch & 0x3F)); - return 3; - } - if (ch <= 0x10FFFF) { - dst[0] = (char)(0xF0 + (ch >> 18)); - dst[1] = (char)(0x80 + ((ch >> 12) & 0x3F)); - dst[2] = (char)(0x80 + ((ch >> 6) & 0x3F)); - dst[3] = (char)(0x80 + (ch & 0x3F)); - return 4; - } - // output UTF-8 encoding of 0xFFFF - dst[0] = (char)0xEF; - dst[1] = (char)0xBF; - dst[2] = (char)0xBF; - return 3; -} - -UCS4 decode_next(const char *str, int *i, int iMax) { - str += *i; - iMax -= *i; - const char *old_str = str; - - // Handle wrapping that could happen if the caller use - // something really large for i_max if src is known to - // be null terminated - const char *str_end = str + iMax; - if (str_end < str) { - str_end = (const char *)(-1); - } - - UCS4 ch = decode(str, str_end); - if (ch == 0xFFFF) // Invalid character! - (*i)++; - else - *i += str - old_str; - return ch; -} - -void move_inc(const char *str, int *i, int iMax) { - (void)((*i < iMax && isutf(str[++(*i)])) || (*i < iMax && isutf(str[++(*i)])) || - (*i < iMax && isutf(str[++(*i)])) || (*i < iMax && (++(*i) != 0))); -} - -void move_dec(const char *str, int *i) { - (void)((*i > 0 && isutf(str[--(*i)])) || (*i > 0 && isutf(str[--(*i)])) || (*i > 0 && isutf(str[--(*i)])) || - (*i > 0 && (--(*i) != 0))); -} - -int count_characters(const char *str, int iMax) { - int count = 0; - int i = 0; - while (i < iMax && decode_next(str, &i, iMax)) - count++; - return count; -} - -} // namespace utf8 diff --git a/src/modules/ui/turbobadger/tb/utf8/utf8.h b/src/modules/ui/turbobadger/tb/utf8/utf8.h deleted file mode 100644 index e930070aa..000000000 --- a/src/modules/ui/turbobadger/tb/utf8/utf8.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file - */ - -#pragma once - -typedef unsigned int UCS4; - -namespace utf8 -{ - -/** Decodes UTF-8 from a string input to a UCS4 character. - @param src buffer in UTF-8 that should be decoded. If the buffer represent - a valid character, the pointer will be incremented to the next character. - @param src_end The end of the string. - @return a UCS4 character, or 0xFFFF if the buffer didn't represent a valid character. -*/ -UCS4 decode(const char *&src, const char *src_end); - -/** Encodes a UCS4 character to UTF-8. - @param ch UCS-4 value. - @param dst buffer to receive UTF-8 encoding (must be at least 8 bytes!) - @return number of bytes needed to represent character -*/ -int encode(UCS4 ch, char *dst); - -/** Decode the next UCS4 character from a UTF-8 string, and update the index variable. - @param str The UTF-8 string. - @param i The index of the current position. This will be increased to the next position. - @param i_max The last position (size of str). -*/ -UCS4 decode_next(const char *str, int *i, int i_max); - -/** Move to the next character in a UTF-8 string. - @param str The UTF-8 string. - @param i The index of the current position. This will be increased to the next position. - @param i_max The last position (size of str). -*/ -void move_inc(const char *str, int *i, int i_max); - -/** Move to the previous character in a UTF-8 string. - @param str The UTF-8 string. - @param i The index of the current position. This will be decreased to the previous position. -*/ -void move_dec(const char *str, int *i); - -/** Count characters before null termination in a UTF-8 string. - Node: Does not include the null termination! - @param str The UTF-8 string. - @param i_max The last position (size of str). -*/ -int count_characters(const char *str, int i_max); - -} // namespace utf8 diff --git a/src/modules/ui/turbobadger/ui_renderer_gl.cpp b/src/modules/ui/turbobadger/ui_renderer_gl.cpp deleted file mode 100644 index ce0bf2957..000000000 --- a/src/modules/ui/turbobadger/ui_renderer_gl.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/** - * @file - */ - -#include -#include "ui_renderer_gl.h" -#include "video/Renderer.h" -#include "core/GLM.h" -#include "core/Common.h" -#include "core/Trace.h" -#include -#include - -namespace tb { - -#ifdef TB_RUNTIME_DEBUG_INFO -static uint32_t tb_dbg_bitmap_validations = 0; -#endif - -UIBitmapGL::UIBitmapGL(UIRendererGL *renderer) : - _renderer(renderer) { -} - -UIBitmapGL::~UIBitmapGL() { - _renderer->flushBitmap(this); - - shutdown(); -} - -void UIBitmapGL::shutdown() { - if (_destroy) { - video::deleteTexture(_texture); - } - _destroy = false; -} - -void UIBitmapGL::bind(video::TextureUnit unit) { - video::bindTexture(unit, _textureConfig.type(), _texture); -} - -bool UIBitmapGL::init(int width, int height, video::Id texture) { - _w = width; - _h = height; - _texture = texture; - _destroy = false; - setData(nullptr); - return true; -} - -bool UIBitmapGL::init(int width, int height, uint32_t *data) { - core_assert(width == TBGetNearestPowerOfTwo(width)); - core_assert(height == TBGetNearestPowerOfTwo(height)); - - _w = width; - _h = height; - - _destroy = true; - - _texture = video::genTexture(); - _textureConfig.format(video::TextureFormat::RGBA); - video::bindTexture(video::TextureUnit::Upload, _textureConfig.type(), _texture); - video::setupTexture(_textureConfig); - setData(data); - - return true; -} - -void UIBitmapGL::setData(uint32_t *data) { - _renderer->flushBitmap(this); - video::bindTexture(video::TextureUnit::Upload, _textureConfig.type(), _texture); - if (data != nullptr) { - video::uploadTexture(_textureConfig.type(), _textureConfig.format(), _w, _h, (const uint8_t*)data, 0); - } - TB_IF_DEBUG_SETTING(RENDER_BATCHES, tb_dbg_bitmap_validations++); -} - -UIRendererGL::UIRendererGL() : - _white(this) { -} - -void UIRendererGL::shutdown() { - _white.shutdown(); - _shader.shutdown(); - _vbo.shutdown(); -} - -void UIRendererGL::onWindowResize(const glm::ivec2& pixelDimensions, const glm::ivec2& screenDimensions) { - _camera.init(glm::ivec2(0), pixelDimensions, screenDimensions); - _camera.update(0.0); - video::ScopedShader scoped(_shader); - _shader.setViewprojection(_camera.projectionMatrix()); -} - -bool UIRendererGL::init(const glm::ivec2& pixelDimensions, const glm::ivec2& screenDimensions) { - if (!_shader.setup()) { - Log::error("Could not load the ui shader"); - return false; - } - - _bufferIndex = _vbo.create(); - if (_bufferIndex < 0) { - Log::error("Failed to create ui vbo"); - return false; - } - - _camera = video::uiCamera(glm::ivec2(0), pixelDimensions, screenDimensions); - - _vbo.addAttribute(_shader.getColorAttribute(_bufferIndex, &Vertex::r, true)); - _vbo.addAttribute(_shader.getTexcoordAttribute(_bufferIndex, &Vertex::u)); - _vbo.addAttribute(_shader.getPosAttribute(_bufferIndex, &Vertex::x)); - - uint32_t data = 0xffffffff; - _white.init(1, 1, &data); - - return true; -} - -void UIRendererGL::beginPaint(int pixelWidth, int pixelHeight) { - core_trace_scoped(UIAppBeginPaint); -#ifdef TB_RUNTIME_DEBUG_INFO - tb_dbg_bitmap_validations = 0; -#endif - - TBRendererBatcher::beginPaint(pixelWidth, pixelHeight); - - _shader.activate(); - _shader.setViewprojection(_camera.projectionMatrix()); - _shader.setModel(glm::mat4(1.0f)); - _shader.setTexture(video::TextureUnit::Zero); - - video::viewport(0, 0, pixelWidth, pixelHeight); - video::scissor(0, 0, pixelWidth, pixelHeight); - - video::enable(video::State::Blend); - video::disable(video::State::DepthTest); - video::enable(video::State::Scissor); - video::blendFunc(video::BlendMode::SourceAlpha, video::BlendMode::OneMinusSourceAlpha); -} - -void UIRendererGL::endPaint() { - core_trace_scoped(UIAppEndPaint); - TBRendererBatcher::endPaint(); - _shader.deactivate(); - -#ifdef TB_RUNTIME_DEBUG_INFO - if (TB_DEBUG_SETTING(RENDER_BATCHES)) { - Log::debug("Frame caused %d bitmap validations.", tb_dbg_bitmap_validations); - } -#endif -} - -void UIRendererGL::bindBitmap(TBBitmap *bitmap) { - if (bitmap == nullptr) { - _white.bind(); - return; - } - static_cast(bitmap)->bind(); -} - -TBBitmap *UIRendererGL::createBitmap(int width, int height, uint32_t *data) { - UIBitmapGL *bitmap = new UIBitmapGL(this); - if (!bitmap->init(width, height, data)) { - delete bitmap; - return nullptr; - } - return bitmap; -} - -void UIRendererGL::flush() { - batch.flush(this); -} - -void UIRendererGL::renderBatch(Batch *batch) { - // TODO: skin ui elements and font is not in the same bitmap - thus we - // are switching bitmaps very often and render batches whenever text - // comes into play. - core_trace_scoped(UIAppRenderBatch); - bindBitmap(batch->bitmap); - core_assert_always(_vbo.update(_bufferIndex, batch->vertex, sizeof(Vertex) * batch->vertex_count)); - - core_assert_always(_vbo.bind()); - video::drawArrays(video::Primitive::Triangles, _vbo.elements(_bufferIndex, 1, sizeof(Vertex))); - _vbo.unbind(); -} - -void UIRendererGL::setClipRect(const TBRect &rect) { - video::scissor(rect.x, rect.y, rect.w, rect.h); -} - -} diff --git a/src/modules/ui/turbobadger/ui_renderer_gl.h b/src/modules/ui/turbobadger/ui_renderer_gl.h deleted file mode 100644 index c81e9f4ec..000000000 --- a/src/modules/ui/turbobadger/ui_renderer_gl.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "video/Renderer.h" -#include "video/Camera.h" -#include "video/Buffer.h" -#include "video/TextureConfig.h" -#include "RenderShaders.h" - -#include - -namespace tb { - -class UIRendererGL; - -class UIBitmapGL: public TBBitmap { -public: - UIBitmapGL(UIRendererGL *renderer); - ~UIBitmapGL(); - - bool init(int width, int height, uint32_t *data); - - bool init(int width, int height, video::Id texture); - - void bind(video::TextureUnit unit = video::TextureUnit::Zero); - - void shutdown(); - - virtual int width() override { - return _w; - } - - virtual int height() override { - return _h; - } - - virtual void setData(uint32_t *data) override; -public: - UIRendererGL *_renderer; - int _w = 0; - int _h = 0; - video::Id _texture = video::InvalidId; - video::TextureConfig _textureConfig; - bool _destroy = false; -}; - -class UIRendererGL: public TBRendererBatcher { -private: - UIBitmapGL _white; - shader::TextureShader _shader; - video::Buffer _vbo; - int32_t _bufferIndex = -1; - - void bindBitmap(TBBitmap *bitmap); - -public: - UIRendererGL(); - - bool init(const glm::ivec2& pixelDimensions, const glm::ivec2& screenDimensions); - void shutdown(); - void onWindowResize(const glm::ivec2& pixelDimensions, const glm::ivec2& screenDimensions); - - virtual void beginPaint(int pixelWidth, int pixelHeight) override; - virtual void endPaint() override; - - virtual TBBitmap *createBitmap(int width, int height, uint32_t *data) override; - - virtual void flush() override; - virtual void renderBatch(Batch *batch) override; - virtual void setClipRect(const TBRect &rect) override; -}; - -} diff --git a/src/modules/ui/turbobadger/ui_widgets.cpp b/src/modules/ui/turbobadger/ui_widgets.cpp deleted file mode 100644 index 54b3ef62d..000000000 --- a/src/modules/ui/turbobadger/ui_widgets.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file - */ - -#include "ui_widgets.h" -#include "core/Log.h" - -ColorWidget::ColorWidget() : - Super(), _color(), _value(0) { -} - -void ColorWidget::setValue(int value) { - Log::info("setValue to %i", value); - if ((uint32_t)value == _value) { - return; - } - _value = value; - const int red = (_value >> 24) & 0xFF; - const int green = (_value >> 16) & 0xFF; - const int blue = (_value >> 8) & 0xFF; - const int alpha = (_value >> 0) & 0xFF; - _color = tb::TBColor(red, green, blue, alpha); - - invalidateSkinStates(); - invalidate(); - - tb::TBWidgetEvent ev(tb::EVENT_TYPE_CHANGED); - invokeEvent(ev); -} - -void ColorWidget::setColor(const char *name) { - if (!name) { - return; - } - _color.setFromString(name, SDL_strlen(name)); - setValue((uint32_t)_color); -} - -void ColorWidget::setColor(int r, int g, int b, int a) { - setValue((uint32_t)tb::TBColor(r, g, b, a)); -} - -void ColorWidget::onPaint(const PaintProps &paintProps) { - tb::TBRect local_rect = getRect(); - local_rect.x = 0; - local_rect.y = 0; - tb::g_tb_skin->paintRectFill(local_rect, _color); -} - -void ColorWidget::onInflate(const tb::INFLATE_INFO &info) { - if (const char *color = info.node->getValueString("color", nullptr)) { - setColor(color); - } - Super::onInflate(info); -} - -NodeConnectorWidget::NodeConnectorWidget() : - Super(), _color() { -} - -void NodeConnectorWidget::onPaint(const PaintProps &paintProps) { - tb::TBRect local_rect = getRect(); - local_rect.x = 0; - local_rect.y = 0; - tb::g_tb_skin->paintRectFill(local_rect, _color); -} - -void NodeConnectorWidget::onInflate(const tb::INFLATE_INFO &info) { - if (const char *color = info.node->getValueString("color", nullptr)) { - _color.setFromString(color, SDL_strlen(color)); - } - Super::onInflate(info); -} - -tb::PreferredSize ImageWidget::onCalculatePreferredContentSize(const tb::SizeConstraints &constraints) { - const tb::PreferredSize& prefSize = Super::onCalculatePreferredContentSize(constraints); - if (prefSize.max_w == 0 || prefSize.max_h == 0) { - return tb::TBImageWidget::onCalculatePreferredContentSize(constraints); - } - return prefSize; -} diff --git a/src/modules/ui/turbobadger/ui_widgets.h b/src/modules/ui/turbobadger/ui_widgets.h deleted file mode 100644 index cfa36e25e..000000000 --- a/src/modules/ui/turbobadger/ui_widgets.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file - */ -#pragma once - -#include "Widget.h" -#include "core/Common.h" - -class ColorWidget: public ui::turbobadger::Widget { -public: - UIWIDGET_SUBCLASS(ColorWidget, ui::turbobadger::Widget); - - ColorWidget(); - - void setColor(const char *); - void setColor(int r, int g, int b, int a); - - const tb::TBColor& getColor() const { - return _color; - } - - void setValue(int value) override; - int getValue() const override { - return (int) _value; - } - - void onInflate(const tb::INFLATE_INFO &info) override; - void onPaint(const PaintProps &paint_props) override; - -private: - tb::TBColor _color; - uint32_t _value; -}; -UIWIDGET_FACTORY(ColorWidget, tb::TBValue::TYPE_INT, tb::WIDGET_Z_TOP) - -class NodeConnectorWidget: public ui::turbobadger::Widget { -public: - UIWIDGET_SUBCLASS(NodeConnectorWidget, ui::turbobadger::Widget); - - NodeConnectorWidget(); - - void onInflate(const tb::INFLATE_INFO &info) override; - void onPaint(const PaintProps &paint_props) override; -private: - tb::TBColor _color; -}; -UIWIDGET_FACTORY(NodeConnectorWidget, tb::TBValue::TYPE_NULL, tb::WIDGET_Z_TOP) - -class ImageWidget: public tb::TBImageWidget { -public: - UIWIDGET_SUBCLASS(ImageWidget, tb::TBImageWidget); - - tb::PreferredSize onCalculatePreferredContentSize(const tb::SizeConstraints &constraints) override; -}; -UIWIDGET_FACTORY(ImageWidget, tb::TBValue::TYPE_NULL, tb::WIDGET_Z_TOP) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 7856059c9..00d1ed726 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -7,7 +7,6 @@ add_subdirectory(testglslcomp) add_subdirectory(testglslgeom) add_subdirectory(testimgui) add_subdirectory(testnuklear) -add_subdirectory(testturbobadger) add_subdirectory(testgpumc) add_subdirectory(testluaui) add_subdirectory(testcamera) diff --git a/src/tests/testturbobadger/CMakeLists.txt b/src/tests/testturbobadger/CMakeLists.txt deleted file mode 100644 index fbe9371e7..000000000 --- a/src/tests/testturbobadger/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -project(testturbobadger) -set(SRCS - Demo01.cpp Demo.h - ListWindow.cpp ListWindow.h - ResourceEditWindow.cpp ResourceEditWindow.h - TestTurbobadger.h TestTurbobadger.cpp -) -set(FILES - testturbobadger/demo01/ui_resources/test_textwindow.tb.txt - testturbobadger/demo01/ui_resources/test_tabcontainer01.tb.txt - testturbobadger/demo01/ui_resources/test_scrollcontainer.tb.txt - testturbobadger/demo01/ui_resources/test_select_advanced.tb.txt - testturbobadger/demo01/ui_resources/test_layout02.tb.txt - testturbobadger/demo01/ui_resources/test_animations.tb.txt - testturbobadger/demo01/ui_resources/test_ui.tb.txt - testturbobadger/demo01/ui_resources/test_layout01.tb.txt - testturbobadger/demo01/ui_resources/test_connections.tb.txt - testturbobadger/demo01/ui_resources/test_list_item.tb.txt - testturbobadger/demo01/ui_resources/test_select.tb.txt - testturbobadger/demo01/ui_resources/test_radio_checkbox.tb.txt - testturbobadger/demo01/ui_resources/resource_edit_test.tb.txt - testturbobadger/demo01/ui_resources/test_toggle_containers.tb.txt - testturbobadger/demo01/ui_resources/test_image_widget.tb.txt - testturbobadger/demo01/ui_resources/resource_edit_window.tb.txt - testturbobadger/demo01/ui_resources/test_scroller_snap.tb.txt - testturbobadger/demo01/ui_resources/test_skin_conditions02.tb.txt - testturbobadger/demo01/ui_resources/test_skin_conditions01.tb.txt - testturbobadger/demo01/ui_resources/test_batching01.tb.txt - testturbobadger/demo01/ui_resources/test_layout03.tb.txt - testturbobadger/demo01/language/lng_en.tb.txt - testturbobadger/demo01/language/lng_sv.tb.txt - testturbobadger/demo01/images/image_3.png - testturbobadger/demo01/images/image_1.png - testturbobadger/demo01/images/image_6.png - testturbobadger/demo01/images/image_2.png - testturbobadger/demo01/images/image_8.png - testturbobadger/demo01/images/image_5.png - testturbobadger/demo01/images/image_7.png - testturbobadger/demo01/images/image_4.png - testturbobadger/demo01/images/image_9.png - testturbobadger/demo01/skin/star_gray.png - testturbobadger/demo01/skin/star_gold@192.png - testturbobadger/demo01/skin/star_gold@384.png - testturbobadger/demo01/skin/icon128.png - testturbobadger/demo01/skin/star_gold.png - testturbobadger/demo01/skin/star_gray@384.png - testturbobadger/demo01/skin/remove.png - testturbobadger/demo01/skin/special_button_pressed.png - testturbobadger/demo01/skin/special_button.png - testturbobadger/demo01/skin/icon16.png - testturbobadger/demo01/skin/testturbobadger-skin.tb.txt - testturbobadger/demo01/skin/star_gold@288.png - testturbobadger/demo01/skin/image_frame.png - testturbobadger/demo01/skin/bg_tile.png - testturbobadger/demo01/skin/star_gray@288.png - testturbobadger/demo01/skin/icon48.png - testturbobadger/demo01/skin/focus_r8.png - testturbobadger/demo01/skin/star_gray@192.png -) -engine_add_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS} FILES ${FILES} WINDOWED NOINSTALL) -engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES turbobadger) diff --git a/src/tests/testturbobadger/Demo.h b/src/tests/testturbobadger/Demo.h deleted file mode 100644 index 976604b53..000000000 --- a/src/tests/testturbobadger/Demo.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @file - */ - -#ifndef DEMO_H -#define DEMO_H - -#include "tb_widgets.h" -#include "tb_widgets_common.h" -#include "tb_widgets_reader.h" -#include "tb_widgets_listener.h" -#include "tb_message_window.h" -#include "tb_msg.h" -#include "tb_scroller.h" - -using namespace tb; - -class DemoWindow : public TBWindow -{ -public: - DemoWindow(TBWidget *root); - bool loadResourceFile(const char *filename); - void loadResourceData(const char *data); - void loadResource(TBNode &node); - - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class EditWindow : public DemoWindow -{ -public: - EditWindow(TBWidget *root); - virtual void onProcessStates(); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class MainWindow : public DemoWindow, public TBMessageHandler -{ -public: - MainWindow(TBWidget *root); - virtual bool onEvent(const TBWidgetEvent &ev); - - // Implement TBMessageHandler - virtual void onMessageReceived(TBMessage *msg); -}; - -class ImageWindow : public DemoWindow -{ -public: - ImageWindow(TBWidget *root); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class PageWindow : public DemoWindow, public TBScrollerSnapListener -{ -public: - PageWindow(TBWidget *root); - virtual bool onEvent(const TBWidgetEvent &ev); - virtual void onScrollSnap(TBWidget *target_widget, int &target_x, int &target_y); -}; - -class AnimationsWindow : public DemoWindow -{ -public: - AnimationsWindow(TBWidget *root); - void animate(); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class LayoutWindow : public DemoWindow -{ -public: - LayoutWindow(TBWidget *root, const char *filename); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class TabContainerWindow : public DemoWindow -{ -public: - TabContainerWindow(TBWidget *root); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class ConnectionWindow : public DemoWindow -{ -public: - ConnectionWindow(TBWidget *root); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -class ScrollContainerWindow : public DemoWindow, public TBMessageHandler -{ -public: - ScrollContainerWindow(TBWidget *root); - virtual bool onEvent(const TBWidgetEvent &ev); - - // Implement TBMessageHandler - virtual void onMessageReceived(TBMessage *msg); -}; - -#endif diff --git a/src/tests/testturbobadger/Demo01.cpp b/src/tests/testturbobadger/Demo01.cpp deleted file mode 100644 index 59f189576..000000000 --- a/src/tests/testturbobadger/Demo01.cpp +++ /dev/null @@ -1,704 +0,0 @@ -/** - * @file - */ - -#include "Demo.h" -#include "ListWindow.h" -#include "ResourceEditWindow.h" -#include -#include -#include "tb_system.h" -#include "tb_language.h" -#include "tb_inline_select.h" -#include "tb_select.h" -#include "tb_menu_window.h" -#include "tb_editfield.h" -#include "tb_tab_container.h" -#include "tb_bitmap_fragment.h" -#include "animation/tb_widget_animation.h" -#include "tb_node_tree.h" -#include "tb_tempbuffer.h" -#include "tb_font_renderer.h" -#include "image/tb_image_manager.h" -#include "utf8/utf8.h" -#include "core/StringUtil.h" - -AdvancedItemSource advanced_source; -TBGenericStringItemSource name_source; -TBGenericStringItemSource popup_menu_source; - -#ifdef TB_SUPPORT_CONSTEXPR - -void const_expr_test() -{ - // Some code here just to see if the compiler really did - // implement constexpr (and not just ignored it) - // Should obviosly only compile if it really works. If not, - // disable TB_SUPPORT_CONSTEXPR in tb_hash.h for your compiler. - TBID id("foo"); - switch(id) - { - case TBIDC("foo"): - break; - case TBIDC("baar"): - break; - default: - break; - } -} - -#endif // TB_SUPPORT_CONSTEXPR - -// == DemoWindow ============================================================== - -DemoWindow::DemoWindow(TBWidget *root) -{ - root->addChild(this); -} - -bool DemoWindow::loadResourceFile(const char *filename) -{ - // We could do g_widgets_reader->LoadFile(this, filename) but we want - // some extra data we store under "WindowInfo", so read into node tree. - TBNode node; - if (!node.readFile(filename)) - return false; - loadResource(node); - return true; -} - -void DemoWindow::loadResourceData(const char *data) -{ - // We could do g_widgets_reader->LoadData(this, filename) but we want - // some extra data we store under "WindowInfo", so read into node tree. - TBNode node; - node.readData(data); - loadResource(node); -} - -void DemoWindow::loadResource(TBNode &node) -{ - g_widgets_reader->loadNodeTree(this, &node); - - // Get title from the WindowInfo section (or use "" if not specified) - setText(node.getValueString("WindowInfo>title", "")); - - const TBRect parent_rect(0, 0, getParent()->getRect().w, getParent()->getRect().h); - const TBDimensionConverter *dc = g_tb_skin->getDimensionConverter(); - TBRect window_rect = getResizeToFitContentRect(); - - // Use specified size or adapt to the preferred content size. - TBNode *tmp = node.getNode("WindowInfo>size"); - if (tmp && tmp->getValue().getArrayLength() == 2) - { - window_rect.w = dc->getPxFromString(tmp->getValue().getArray()->getValue(0)->getString(), window_rect.w); - window_rect.h = dc->getPxFromString(tmp->getValue().getArray()->getValue(1)->getString(), window_rect.h); - } - - // Use the specified position or center in parent. - tmp = node.getNode("WindowInfo>position"); - if (tmp && tmp->getValue().getArrayLength() == 2) - { - window_rect.x = dc->getPxFromString(tmp->getValue().getArray()->getValue(0)->getString(), window_rect.x); - window_rect.y = dc->getPxFromString(tmp->getValue().getArray()->getValue(1)->getString(), window_rect.y); - } - else - window_rect = window_rect.centerIn(parent_rect); - - // Make sure the window is inside the parent, and not larger. - window_rect = window_rect.moveIn(parent_rect).clip(parent_rect); - - setRect(window_rect); - - // Ensure we have focus - now that we've filled the window with possible focusable - // widgets. EnsureFocus was automatically called when the window was activated (by - // adding the window to the root), but then we had nothing to focus. - // Alternatively, we could add the window after setting it up properly. - ensureFocus(); -} - -bool DemoWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_KEY_DOWN && ev.special_key == TB_KEY_ESC) - { - // We could call Die() to fade away and die, but click the close button instead. - // That way the window has a chance of intercepting the close and f.ex ask if it really should be closed. - TBWidgetEvent click_ev(EVENT_TYPE_CLICK); - m_close_button.invokeEvent(click_ev); - return true; - } - return TBWindow::onEvent(ev); -} - -// == EditWindow ============================================================== - -EditWindow::EditWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_textwindow.tb.txt"); -} -void EditWindow::onProcessStates() -{ - // Update the disabled state of undo/redo buttons, and caret info. - - if (TBEditField *edit = getWidgetByIDAndType(TBIDC("editfield"))) - { - if (TBWidget *undo = getWidgetByID("undo")) - undo->setState(WIDGET_STATE_DISABLED, !edit->getStyleEdit()->canUndo()); - if (TBWidget *redo = getWidgetByID("redo")) - redo->setState(WIDGET_STATE_DISABLED, !edit->getStyleEdit()->canRedo()); - if (TBTextField *info = getWidgetByIDAndType(TBIDC("info"))) - { - const core::String& text = core::string::format("Caret ofs: %d", edit->getStyleEdit()->caret.getGlobalOfs()); - info->setText(text); - } - } -} -bool EditWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK) - { - TBEditField *edit = getWidgetByIDAndType(TBIDC("editfield")); - if (!edit) - return false; - - if (ev.target->getID() == TBIDC("clear")) - { - edit->setText(""); - return true; - } - else if (ev.target->getID() == TBIDC("undo")) - { - edit->getStyleEdit()->undo(); - return true; - } - else if (ev.target->getID() == TBIDC("redo")) - { - edit->getStyleEdit()->redo(); - return true; - } - else if (ev.target->getID() == TBIDC("menu")) - { - static TBGenericStringItemSource source; - if (!source.getNumItems()) - { - source.addItem(new TBGenericStringItem("Default font", TBIDC("default font"))); - source.addItem(new TBGenericStringItem("Default font (larger)", TBIDC("large font"))); - source.addItem(new TBGenericStringItem("RGB font (Neon)", TBIDC("rgb font Neon"))); - source.addItem(new TBGenericStringItem("RGB font (Orangutang)", TBIDC("rgb font Orangutang"))); - source.addItem(new TBGenericStringItem("RGB font (Orange)", TBIDC("rgb font Orange"))); - source.addItem(new TBGenericStringItem("-")); - source.addItem(new TBGenericStringItem("Glyph cache stresstest (CJK)", TBIDC("CJK"))); - source.addItem(new TBGenericStringItem("-")); - source.addItem(new TBGenericStringItem("Toggle wrapping", TBIDC("toggle wrapping"))); - source.addItem(new TBGenericStringItem("-")); - source.addItem(new TBGenericStringItem("Align left", TBIDC("align left"))); - source.addItem(new TBGenericStringItem("Align center", TBIDC("align center"))); - source.addItem(new TBGenericStringItem("Align right", TBIDC("align right"))); - } - - if (TBMenuWindow *menu = new TBMenuWindow(ev.target, TBIDC("popup_menu"))) - menu->show(&source, TBPopupAlignment()); - return true; - } - else if (ev.target->getID() == TBIDC("popup_menu")) - { - if (ev.ref_id == TBIDC("default font")) - edit->setFontDescription(TBFontDescription()); - else if (ev.ref_id == TBIDC("large font")) - { - TBFontDescription fd = g_font_manager->getDefaultFontDescription(); - fd.setSize(28); - edit->setFontDescription(fd); - } - else if (ev.ref_id == TBIDC("rgb font Neon")) - { - TBFontDescription fd = edit->getCalculatedFontDescription(); - fd.setID(TBIDC("Neon")); - edit->setFontDescription(fd); - } - else if (ev.ref_id == TBIDC("rgb font Orangutang")) - { - TBFontDescription fd = edit->getCalculatedFontDescription(); - fd.setID(TBIDC("Orangutang")); - edit->setFontDescription(fd); - } - else if (ev.ref_id == TBIDC("rgb font Orange")) - { - TBFontDescription fd = edit->getCalculatedFontDescription(); - fd.setID(TBIDC("Orange")); - edit->setFontDescription(fd); - } - else if (ev.ref_id == TBIDC("CJK")) - { - TBTempBuffer buf; - for (int i = 0, cp = 0x4E00; cp <= 0x9FCC; cp++, i++) - { - char utf8[8]; - int len = utf8::encode(cp, utf8); - buf.append(utf8, len); - if (i % 64 == 63) - buf.append("\n", 1); - } - edit->getStyleEdit()->setText(buf.getData(), buf.getAppendPos()); - } - else if (ev.ref_id == TBIDC("toggle wrapping")) - edit->setWrapping(!edit->getWrapping()); - else if (ev.ref_id == TBIDC("align left")) - edit->setTextAlign(TB_TEXT_ALIGN_LEFT); - else if (ev.ref_id == TBIDC("align center")) - edit->setTextAlign(TB_TEXT_ALIGN_CENTER); - else if (ev.ref_id == TBIDC("align right")) - edit->setTextAlign(TB_TEXT_ALIGN_RIGHT); - return true; - } - } - return DemoWindow::onEvent(ev); -} - -// == LayoutWindow ============================================================ - -LayoutWindow::LayoutWindow(TBWidget *root, const char *filename) : DemoWindow(root) -{ - loadResourceFile(filename); -} - -bool LayoutWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CHANGED && ev.target->getID() == TBIDC("select position")) - { - LAYOUT_POSITION pos = LAYOUT_POSITION_CENTER; - if (TBSelectDropdown *select = getWidgetByIDAndType(TBIDC("select position"))) - pos = static_cast(select->getValue()); - for (int i = 0; i < 3; i++) - if (TBLayout *layout = getWidgetByIDAndType(i + 1)) - layout->setLayoutPosition(pos); - return true; - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("toggle axis")) - { - static AXIS axis = AXIS_Y; - for (int i = 0; i < 3; i++) - if (TBLayout *layout = getWidgetByIDAndType(i + 1)) - layout->setAxis(axis); - axis = axis == AXIS_X ? AXIS_Y : AXIS_X; - if (TBLayout *layout = getWidgetByIDAndType(TBIDC("switch_layout"))) - layout->setAxis(axis); - resizeToFitContent(RESIZE_FIT_CURRENT_OR_NEEDED); - return true; - } - return DemoWindow::onEvent(ev); -} - -// == TabContainerWindow ============================================================ - -TabContainerWindow::TabContainerWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_tabcontainer01.tb.txt"); -} - -bool TabContainerWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("set_align")) - { - if (TBTabContainer *tc = getWidgetByIDAndType(TBIDC("tabcontainer"))) - tc->setAlignment(static_cast(ev.target->data.getInt())); - resizeToFitContent(RESIZE_FIT_CURRENT_OR_NEEDED); - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("toggle_tab_axis")) - { - static AXIS axis = AXIS_X; - axis = axis == AXIS_X ? AXIS_Y : AXIS_X; - if (TBTabContainer *tc = getWidgetByIDAndType(TBIDC("tabcontainer"))) - { - for (TBWidget *child = tc->getTabLayout()->getFirstChild(); child; child = child->getNext()) - { - if (TBButton *button = TBSafeCast(child)) - button->setAxis(axis); - } - } - resizeToFitContent(RESIZE_FIT_CURRENT_OR_NEEDED); - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("start_spinner")) - { - if (TBProgressSpinner *spinner = getWidgetByIDAndType(TBIDC("spinner"))) - spinner->setValue(1); - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("stop_spinner")) - { - if (TBProgressSpinner *spinner = getWidgetByIDAndType(TBIDC("spinner"))) - spinner->setValue(0); - } - return DemoWindow::onEvent(ev); -} - -// == ConnectionWindow ========================================================= - -ConnectionWindow::ConnectionWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_connections.tb.txt"); -} - -bool ConnectionWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("reset-master-volume")) - { - if (TBWidgetValue *val = g_value_group.getValue(TBIDC("master-volume"))) - val->setInt(50); - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("reset-user-name")) - { - if (TBWidgetValue *val = g_value_group.getValue(TBIDC("user-name"))) - val->setText(""); - } - return DemoWindow::onEvent(ev); -} - -// == ScrollContainerWindow =================================================== - -ScrollContainerWindow::ScrollContainerWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_scrollcontainer.tb.txt"); - - if (TBSelectDropdown *select = getWidgetByIDAndType(TBIDC("name dropdown"))) - select->setSource(&name_source); - - if (TBSelectDropdown *select = getWidgetByIDAndType(TBIDC("advanced dropdown"))) - select->setSource(&advanced_source); -} - -bool ScrollContainerWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK) - { - if (ev.target->getID() == TBIDC("add img")) - { - TBButton *button = TBSafeCast(ev.target); - TBSkinImage *skin_image = new TBSkinImage; - skin_image->setSkinBg(TBIDC("Icon16")); - button->getContentRoot()->addChild(skin_image, WIDGET_Z_BOTTOM); - return true; - } - else if (ev.target->getID() == TBIDC("new buttons")) - { - for(int i = 0; i < ev.target->data.getInt(); i++) - { - const core::String& str = core::string::format("Remove %d", i); - TBButton *button = new TBButton; - button->setID(TBIDC("remove button")); - button->setText(str); - ev.target->getParent()->addChild(button); - } - return true; - } - else if (ev.target->getID() == TBIDC("new buttons delayed")) - { - for(int i = 0; i < ev.target->data.getInt(); i++) - { - TBMessageData *data = new TBMessageData(); - data->id1 = ev.target->getParent()->getID(); - data->v1.setInt(i); - postMessageDelayed(TBIDC("new button"), data, 100 + i * 500); - } - return true; - } - else if (ev.target->getID() == TBIDC("remove button")) - { - ev.target->getParent()->removeChild(ev.target); - delete ev.target; - return true; - } - else if (ev.target->getID() == TBIDC("showpopupmenu1")) - { - if (TBMenuWindow *menu = new TBMenuWindow(ev.target, TBIDC("popupmenu1"))) - menu->show(&popup_menu_source, TBPopupAlignment()); - return true; - } - else if (ev.target->getID() == TBIDC("popupmenu1")) - { - const core::String& str = core::string::format("Menu event received!\nref_id: %d", (int)ev.ref_id); - TBMessageWindow *msg_win = new TBMessageWindow(this, TBIDC("popup_dialog")); - msg_win->show("Info", str.c_str()); - return true; - } - } - return DemoWindow::onEvent(ev); -} - -void ScrollContainerWindow::onMessageReceived(TBMessage *msg) -{ - if (msg->message == TBIDC("new button") && msg->data) - { - if (TBWidget *target = getWidgetByID(msg->data->id1)) - { - const core::String& str = core::string::format("Remove %d", msg->data->v1.getInt()); - TBButton *button = new TBButton; - button->setID(TBIDC("remove button")); - button->setText(str); - target->addChild(button); - } - } -} - -// == ImageWindow ============================================================= - -ImageWindow::ImageWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_image_widget.tb.txt"); -} - -bool ImageWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("remove")) - { - TBWidget *image = ev.target->getParent(); - image->getParent()->removeChild(image); - delete image; - return true; - } - return DemoWindow::onEvent(ev); -} - -// == PageWindow ============================================================= - -PageWindow::PageWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_scroller_snap.tb.txt"); - - // Listen to the pagers scroller - if (TBWidget *pager = getWidgetByID(TBIDC("page-scroller"))) - pager->getScroller()->setSnapListener(this); -} - -bool PageWindow::onEvent(const TBWidgetEvent &ev) -{ - return DemoWindow::onEvent(ev); -} - -void PageWindow::onScrollSnap(TBWidget *targetWidget, int &targetX, int &targetY) -{ - int page_w = targetWidget->getPaddingRect().w; - int target_page = (targetX + page_w / 2) / page_w; - targetX = target_page * page_w; -} - -// == AnimationsWindow ======================================================== - -AnimationsWindow::AnimationsWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_animations.tb.txt"); - animate(); -} - -void AnimationsWindow::animate() -{ - // Abort any still unfinished animations. - TBWidgetsAnimationManager::abortAnimations(this); - - ANIMATION_CURVE curve = ANIMATION_CURVE_SLOW_DOWN; - double duration = 500; - bool fade = true; - - if (TBSelectList *curve_select = getWidgetByIDAndType("curve")) - curve = static_cast(curve_select->getValue()); - if (TBInlineSelect *duration_select = getWidgetByIDAndType("duration")) - duration = duration_select->getValueDouble(); - if (TBCheckBox *fade_check = getWidgetByIDAndType("fade")) - fade = fade_check->getValue() ? true : false; - - // Start move animation - if (TBAnimationObject *anim = new TBWidgetAnimationRect(this, getRect().offset(-getRect().x - getRect().w, 0), getRect())) - TBAnimationManager::startAnimation(anim, curve, duration); - // Start fade animation - if (fade) - { - if (TBAnimationObject *anim = new TBWidgetAnimationOpacity(this, TB_ALMOST_ZERO_OPACITY, 1, false)) - TBAnimationManager::startAnimation(anim, ANIMATION_CURVE_SLOW_DOWN, duration); - } -} - -bool AnimationsWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("Animate!")) - animate(); - return DemoWindow::onEvent(ev); -} - -// == MainWindow ============================================================== - -MainWindow::MainWindow(TBWidget *root) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_ui.tb.txt"); - - setOpacity(0.97f); -} - -void MainWindow::onMessageReceived(TBMessage *msg) -{ - if (msg->message == TBIDC("instantmsg")) - { - TBMessageWindow *msg_win = new TBMessageWindow(this, TBIDC("test_dialog")); - msg_win->show("Message window", "Instant message received!"); - } - else if (msg->message == TBIDC("busy")) - { - // Keep the message queue busy by posting another "busy" message. - postMessage(TBIDC("busy"), nullptr); - } - else if (msg->message == TBIDC("delayedmsg")) - { - const core::String& text = core::string::format("Delayed message received!\n\n" - "It was received %d ms after its intended fire time.", - (int)(TBSystem::getTimeMS() - msg->getFireTime())); - TBMessageWindow *msg_win = new TBMessageWindow(this, TBIDC("")); - msg_win->show("Message window", text.c_str()); - } -} - -bool MainWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK) - { - if (ev.target->getID() == TBIDC("new")) - { - new MainWindow(getParentRoot()); - return true; - } - if (ev.target->getID() == TBIDC("msg")) - { - postMessage(TBIDC("instantmsg"), nullptr); - return true; - } - else if (ev.target->getID() == TBIDC("busymsg")) - { - if (ev.target->getValue() == 1) - { - // Post the first "busy" message when we check the checkbox. - core_assert(!getMessageByID(TBIDC("busy"))); - if (!getMessageByID(TBIDC("busy"))) - { - postMessage(TBIDC("busy"), nullptr); - TBMessageWindow *msg_win = new TBMessageWindow(this, TBIDC("test_dialog")); - msg_win->show("Message window", "The message loop is now constantly busy with messages to process.\n\n" - "The main thread should be working hard, but input & animations should still be running smoothly."); - } - } - else - { - // Remove any pending "busy" message when we uncheck the checkbox. - core_assert(getMessageByID(TBIDC("busy"))); - if (TBMessage *busymsg = getMessageByID(TBIDC("busy"))) - deleteMessage(busymsg); - } - return true; - } - else if (ev.target->getID() == TBIDC("delayedmsg")) - { - postMessageDelayed(TBIDC("delayedmsg"), nullptr, 2000); - return true; - } - else if (ev.target->getID() == TBIDC("TBWindow.close")) - { - // Intercept the TBWindow.close message and stop it from bubbling - // to TBWindow (prevent the window from closing) - TBMessageWindow *msg_win = new TBMessageWindow(this, TBIDC("confirm_close_dialog")); - TBMessageWindowSettings settings(TB_MSG_YES_NO, TBIDC("Icon48")); - settings.dimmer = true; - settings.styling = true; - msg_win->show("Are you sure?", "Really close the window?", &settings); - return true; - } - else if (ev.target->getID() == TBIDC("confirm_close_dialog")) - { - if (ev.ref_id == TBIDC("TBMessageWindow.yes")) - close(); - return true; - } - else if (ev.target->getID() == TBIDC("reload skin bitmaps")) - { - int reload_count = 10; - double t1 = TBSystem::getTimeMS(); - for (int i = 0; i < reload_count; i++) - g_tb_skin->reloadBitmaps(); - double t2 = TBSystem::getTimeMS(); - - const core::String& message = core::string::format("Reloading the skin graphics %d times took %dms", reload_count, (int)(t2 - t1)); - TBMessageWindow *msg_win = new TBMessageWindow(ev.target, TBID()); - msg_win->show("GFX load performance", message.c_str()); - return true; - } - else if (ev.target->getID() == TBIDC("test context lost")) - { - g_renderer->invokeContextLost(); - g_renderer->invokeContextRestored(); - TBMessageWindow *msg_win = new TBMessageWindow(ev.target, TBID()); - msg_win->show("Context lost & restore", - "Called InvokeContextLost and InvokeContextRestored.\n\n" - "Does everything look fine?"); - return true; - } - else if (ev.target->getID() == TBIDC("test-layout")) - { - core::String resource_file("demo01/ui_resources/"); - resource_file.append(ev.target->data.getString()); - new LayoutWindow(getParentRoot(), resource_file.c_str()); - return true; - } - else if (ev.target->getID() == TBIDC("test-connections")) - { - new ConnectionWindow(getParentRoot()); - return true; - } - else if (ev.target->getID() == TBIDC("test-list")) - { - new AdvancedListWindow(getParentRoot(), &advanced_source); - return true; - } - else if (ev.target->getID() == TBIDC("test-image")) - { - new ImageWindow(getParentRoot()); - return true; - } - else if (ev.target->getID() == TBIDC("test-page")) - { - new PageWindow(getParentRoot()); - return true; - } - else if (ev.target->getID() == TBIDC("test-animations")) - { - new AnimationsWindow(getParentRoot()); - return true; - } - else if (ev.target->getID() == TBIDC("test-scroll-container")) - { - new ScrollContainerWindow(getParentRoot()); - return true; - } - else if (ev.target->getID() == TBIDC("test-skin-conditions")) - { - (new DemoWindow(getParentRoot()))->loadResourceFile("demo01/ui_resources/test_skin_conditions01.tb.txt"); - (new DemoWindow(getParentRoot()))->loadResourceFile("demo01/ui_resources/test_skin_conditions02.tb.txt"); - return true; - } - else if (ev.target->getID() == TBIDC("test-resource-edit")) - { - ResourceEditWindow *res_edit_win = new ResourceEditWindow(); - res_edit_win->load("demo01/ui_resources/resource_edit_test.tb.txt"); - getParent()->addChild(res_edit_win); - return true; - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("debug settings")) - { -#ifdef TB_RUNTIME_DEBUG_INFO - ShowDebugInfoSettingsWindow(getParentRoot()); -#else - TBMessageWindow *msg_win = new TBMessageWindow(ev.target, TBID()); - msg_win->show("Debug settings", - "Debug settings is only available in builds " - "compiled with TB_RUNTIME_DEBUG_INFO defined.\n\n" - "Debug builds enable this by default."); -#endif - return true; - } - } - return DemoWindow::onEvent(ev); -} diff --git a/src/tests/testturbobadger/ListWindow.cpp b/src/tests/testturbobadger/ListWindow.cpp deleted file mode 100644 index d0a419dc1..000000000 --- a/src/tests/testturbobadger/ListWindow.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "ListWindow.h" - -// == AdvancedItemWidget ====================================================== - -AdvancedItemWidget::AdvancedItemWidget(AdvancedItem *item, AdvancedItemSource *source, - TBSelectItemViewer *sourceViewer, int index) - : m_source(source) - , m_source_viewer(sourceViewer) - , m_index(index) -{ - setSkinBg(TBIDC("TBSelectItem")); - setLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY); - setLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); - setPaintOverflowFadeout(false); - - g_widgets_reader->loadFile(getContentRoot(), "demo01/ui_resources/test_list_item.tb.txt"); - TBCheckBox *checkbox = getWidgetByIDAndType(TBIDC("check")); - TBTextField *name = getWidgetByIDAndType(TBIDC("name")); - TBTextField *info = getWidgetByIDAndType(TBIDC("info")); - checkbox->setValue(item->getChecked() ? true : false); - name->setText(item->str); - info->setText(item->getMale() ? "Male" : "Female"); -} - -bool AdvancedItemWidget::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("check")) - { - AdvancedItem *item = m_source->getItem(m_index); - item->setChecked(ev.target->getValue() ? true : false); - - m_source->invokeItemChanged(m_index, m_source_viewer); - return true; - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("delete")) - { - m_source->deleteItem(m_index); - return true; - } - return TBLayout::onEvent(ev); -} - -// == AdvancedItemSource ====================================================== - -bool AdvancedItemSource::filter(int index, const char *filter) -{ - // Override this method so we can return hits for our extra data too. - - if (TBSelectItemSource::filter(index, filter)) - return true; - - AdvancedItem *item = getItem(index); - return stristr(item->getMale() ? "Male" : "Female", filter) ? true : false; -} - -TBWidget *AdvancedItemSource::createItemWidget(int index, TBSelectItemViewer *viewer) -{ - if (TBLayout *layout = new AdvancedItemWidget(getItem(index), this, viewer, index)) - return layout; - return nullptr; -} - -// == ListWindow ============================================================== - -ListWindow::ListWindow(TBWidget *root, TBSelectItemSource *source) : DemoWindow(root) -{ - loadResourceFile("demo01/ui_resources/test_select.tb.txt"); - if (TBSelectList *select = getWidgetByIDAndType("list")) - { - select->setSource(source); - select->getScrollContainer()->setScrollMode(SCROLL_MODE_Y_AUTO); - } -} - -bool ListWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CHANGED && ev.target->getID() == TBIDC("filter")) - { - if (TBSelectList *select = getWidgetByIDAndType("list")) - select->setFilter(ev.target->getText()); - return true; - } - return DemoWindow::onEvent(ev); -} - -// == AdvancedListWindow ============================================================== - -AdvancedListWindow::AdvancedListWindow(TBWidget *root, AdvancedItemSource *source) - : DemoWindow(root) - , m_source(source) -{ - loadResourceFile("demo01/ui_resources/test_select_advanced.tb.txt"); - if (TBSelectList *select = getWidgetByIDAndType("list")) - { - select->setSource(source); - select->getScrollContainer()->setScrollMode(SCROLL_MODE_X_AUTO_Y_AUTO); - } -} - -bool AdvancedListWindow::onEvent(const TBWidgetEvent &ev) -{ - TBSelectList *select = getWidgetByIDAndType("list"); - if (select && ev.type == EVENT_TYPE_CHANGED && ev.target->getID() == TBIDC("filter")) - { - select->setFilter(ev.target->getText()); - return true; - } - else if (select && ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("add")) - { - const core::String& name = getTextByID(TBIDC("add_name")); - if (!name.empty()) - m_source->addItem(new AdvancedItem(name.c_str(), TBIDC("boy_item"), true)); - return true; - } - else if (select && ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("delete all")) - { - m_source->deleteAllItems(); - return true; - } - return DemoWindow::onEvent(ev); -} diff --git a/src/tests/testturbobadger/ListWindow.h b/src/tests/testturbobadger/ListWindow.h deleted file mode 100644 index 703f590b6..000000000 --- a/src/tests/testturbobadger/ListWindow.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef LISTWINDOW_DEMO_H -#define LISTWINDOW_DEMO_H - -#include "Demo.h" -#include "tb_select.h" - -class AdvancedItemSource; - -/** Shows a list of items from *any* type of source. */ -class ListWindow : public DemoWindow -{ -public: - ListWindow(TBWidget *root, TBSelectItemSource *source); - virtual bool onEvent(const TBWidgetEvent &ev); -}; - -/** Shows a list of items from a source of type AdvancedItemSource. */ -class AdvancedListWindow : public DemoWindow -{ -public: - AdvancedListWindow(TBWidget *root, AdvancedItemSource *source); - virtual bool onEvent(const TBWidgetEvent &ev); -private: - AdvancedItemSource *m_source; -}; - -/** AdvancedItem adds extra info to a string item. */ -class AdvancedItem : public TBGenericStringItem -{ -public: - AdvancedItem(const char *str, const TBID &id, bool male) - : TBGenericStringItem(str, id) - , m_checked(false) - , m_male(male) {} - void setChecked(bool checked) { m_checked = checked; } - bool getChecked() const { return m_checked; } - bool getMale() const { return m_male; } -private: - core::String m_info; - bool m_checked; - bool m_male; -}; - -/** AdvancedItemSource provides items of type AdvancedItem and makes sure - the viewer is populated with the customized widget for each item. */ -class AdvancedItemSource : public TBSelectItemSourceList -{ -public: - virtual bool filter(int index, const char *filter); - virtual TBWidget *createItemWidget(int index, TBSelectItemViewer *viewer); -}; - -/** AdvancedItemWidget is the widget representing a AdvancedItem. - On changes to the item, it calls InvokeItemChanged on the source, so that all - viewers of the source are updated to reflect the change. */ -class AdvancedItemWidget : public TBLayout -{ -public: - AdvancedItemWidget(AdvancedItem *item, AdvancedItemSource *source, TBSelectItemViewer *source_viewer, int index); - virtual bool onEvent(const TBWidgetEvent &ev); -private: - AdvancedItemSource *m_source; - TBSelectItemViewer *m_source_viewer; - int m_index; -}; - -#endif // LISTWINDOW_DEMO_H diff --git a/src/tests/testturbobadger/ResourceEditWindow.cpp b/src/tests/testturbobadger/ResourceEditWindow.cpp deleted file mode 100644 index c7455c4cd..000000000 --- a/src/tests/testturbobadger/ResourceEditWindow.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "ResourceEditWindow.h" -#include "tb_widgets_reader.h" -#include "tb_message_window.h" -#include "tb_system.h" -#include "tb_select.h" -#include "tb_editfield.h" -#include "tb_tempbuffer.h" -#include "tb_scroll_container.h" -#include -#include "core/StringUtil.h" - -// == ResourceItem ==================================================================================== - -ResourceItem::ResourceItem(TBWidget *widget, const char *str) - : TBGenericStringItem(str) - , m_widget(widget) -{ -} - -// == ResourceEditWindow ============================================================================== - -ResourceEditWindow::ResourceEditWindow() - : m_widget_list(nullptr) - , m_scroll_container(nullptr) - , m_build_container(nullptr) - , m_source_edit(nullptr) -{ - // Register as global listener to intercept events in the build container - TBWidgetListener::addGlobalListener(this); - - g_widgets_reader->loadFile(this, "demo01/ui_resources/resource_edit_window.tb.txt"); - - m_scroll_container = getWidgetByIDAndType(TBIDC("scroll_container")); - m_build_container = m_scroll_container->getContentRoot(); - m_source_edit = getWidgetByIDAndType(TBIDC("source_edit")); - - m_widget_list = getWidgetByIDAndType(TBIDC("widget_list")); - m_widget_list->setSource(&m_widget_list_source); - - setRect(TBRect(100, 50, 900, 600)); -} - -ResourceEditWindow::~ResourceEditWindow() -{ - TBWidgetListener::removeGlobalListener(this); - - // avoid assert - m_widget_list->setSource(nullptr); -} - -void ResourceEditWindow::load(const char *resourceFile) -{ - m_resource_filename = resourceFile; - setText(resourceFile); - - TBTempBuffer buffer; - if (buffer.appendFile(m_resource_filename.c_str())) - m_source_edit->setText(buffer.getData(), buffer.getAppendPos()); - else // Error, clear and show message - { - m_source_edit->setText(""); - const core::String& text = core::string::format("Could not load file %s", resourceFile); - if (TBMessageWindow *msg_win = new TBMessageWindow(getParentRoot(), TBIDC(""))) - msg_win->show("Error loading resource", text.c_str()); - } - - refreshFromSource(); -} - -void ResourceEditWindow::refreshFromSource() -{ - // Clear old widgets - while (TBWidget *child = m_build_container->getFirstChild()) - { - m_build_container->removeChild(child); - delete child; - } - - // Create new widgets from source - g_widgets_reader->loadData(m_build_container, m_source_edit->getText()); - - // Force focus back in case the edited resource has autofocus. - // FIX: It would be better to prevent the focus change instead! - m_source_edit->setFocus(WIDGET_FOCUS_REASON_UNKNOWN); -} - -void ResourceEditWindow::updateWidgetList(bool immediately) -{ - if (!immediately) - { - TBID id = TBIDC("update_widget_list"); - if (!getMessageByID(id)) - postMessage(id, nullptr); - } - else - { - m_widget_list_source.deleteAllItems(); - addWidgetListItemsRecursive(m_build_container, 0); - - m_widget_list->invalidateList(); - } -} - -void ResourceEditWindow::addWidgetListItemsRecursive(TBWidget *widget, int depth) -{ - if (depth > 0) // Ignore the root - { - // Add a new ResourceItem for this widget - const char *classname = widget->getClassName(); - if (!*classname) - classname = ""; - const core::String& str = core::string::format("%*s%s", depth - 1, "", classname); - - if (ResourceItem *item = new ResourceItem(widget, str.c_str())) - m_widget_list_source.addItem(item); - } - - for (TBWidget *child = widget->getFirstChild(); child; child = child->getNext()) - addWidgetListItemsRecursive(child, depth + 1); -} - -ResourceEditWindow::ITEM_INFO ResourceEditWindow::getItemFromWidget(TBWidget *widget) -{ - ITEM_INFO item_info = { nullptr, -1 }; - for (int i = 0; i < m_widget_list_source.getNumItems(); i++) - if (m_widget_list_source.getItem(i)->getWidget() == widget) - { - item_info.index = i; - item_info.item = m_widget_list_source.getItem(i); - break; - } - return item_info; -} - -void ResourceEditWindow::setSelectedWidget(TBWidget *widget) -{ - m_selected_widget.set(widget); - ITEM_INFO item_info = getItemFromWidget(widget); - if (item_info.item) - m_widget_list->setValue(item_info.index); -} - -bool ResourceEditWindow::onEvent(const TBWidgetEvent &ev) -{ - if (ev.type == EVENT_TYPE_CHANGED && ev.target->getID() == TBIDC("widget_list_search")) - { - m_widget_list->setFilter(ev.target->getText()); - return true; - } - else if (ev.type == EVENT_TYPE_CHANGED && ev.target == m_widget_list) - { - if (m_widget_list->getValue() >= 0 && m_widget_list->getValue() < m_widget_list_source.getNumItems()) - if (ResourceItem *item = m_widget_list_source.getItem(m_widget_list->getValue())) - setSelectedWidget(item->getWidget()); - } - else if (ev.type == EVENT_TYPE_CHANGED && ev.target == m_source_edit) - { - refreshFromSource(); - return true; - } - else if (ev.type == EVENT_TYPE_CLICK && ev.target->getID() == TBIDC("test")) - { - // Create a window containing the current layout, resize and center it. - if (TBWindow *win = new TBWindow()) - { - win->setText("Test window"); - g_widgets_reader->loadData(win->getContentRoot(), m_source_edit->getText()); - TBRect bounds(0, 0, getParent()->getRect().w, getParent()->getRect().h); - win->setRect(win->getResizeToFitContentRect().centerIn(bounds).moveIn(bounds).clip(bounds)); - getParent()->addChild(win); - } - return true; - } - else if (ev.target->getID() == TBIDC("constrained")) - { - m_scroll_container->setAdaptContentSize(ev.target->getValue() ? true : false); - return true; - } - else if (ev.type == EVENT_TYPE_FILE_DROP) - { - return onDropFileEvent(ev); - } - return TBWindow::onEvent(ev); -} - -void ResourceEditWindow::onPaintChildren(const PaintProps &paintProps) -{ - TBWindow::onPaintChildren(paintProps); - - // Paint the selection of the selected widget - if (TBWidget *selected_widget = getSelectedWidget()) - { - TBRect widget_rect(0, 0, selected_widget->getRect().w, selected_widget->getRect().h); - selected_widget->convertToRoot(widget_rect.x, widget_rect.y); - convertFromRoot(widget_rect.x, widget_rect.y); - g_tb_skin->paintRect(widget_rect, TBColor(255, 205, 0), 1); - } -} - -void ResourceEditWindow::onMessageReceived(TBMessage *msg) -{ - if (msg->message == TBIDC("update_widget_list")) - updateWidgetList(true); -} - -bool ResourceEditWindow::onWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) -{ - // Intercept all events to widgets in the build container - if (m_build_container->isAncestorOf(ev.target)) - { - // Let events through if alt is pressed so we can test some - // functionality right in the editor (like toggle hidden UI). - if (ev.modifierkeys & TB_ALT) - return false; - - // Select widget when clicking - if (ev.type == EVENT_TYPE_POINTER_DOWN) - setSelectedWidget(ev.target); - - if (ev.type == EVENT_TYPE_FILE_DROP) - onDropFileEvent(ev); - return true; - } - return false; -} - -void ResourceEditWindow::onWidgetAdded(TBWidget *parent, TBWidget *child) -{ - if (m_build_container && m_build_container->isAncestorOf(child)) - updateWidgetList(false); -} - -void ResourceEditWindow::onWidgetRemove(TBWidget *parent, TBWidget *child) -{ - if (m_build_container && m_build_container->isAncestorOf(child)) - updateWidgetList(false); -} - -bool ResourceEditWindow::onDropFileEvent(const TBWidgetEvent &ev) -{ - const TBWidgetEventFileDrop *fd_event = TBSafeCast(&ev); - if (fd_event->files.getNumItems() > 0) - load(*fd_event->files.get(0)); - return true; -} diff --git a/src/tests/testturbobadger/ResourceEditWindow.h b/src/tests/testturbobadger/ResourceEditWindow.h deleted file mode 100644 index 71ffe05c6..000000000 --- a/src/tests/testturbobadger/ResourceEditWindow.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef ResourceEditWindow_H -#define ResourceEditWindow_H - -#include "tb_widgets.h" -#include "tb_select.h" -#include "tb_widgets_common.h" -#include "tb_widgets_listener.h" -#include "tb_editfield.h" -#include "tb_msg.h" - -using namespace tb; - -class ResourceItem : public TBGenericStringItem -{ -public: - ResourceItem(TBWidget *widget, const char *str); - TBWidget *getWidget() { return m_widget; } -private: - TBWidget *m_widget; -}; - -class ResourceEditWindow : public TBWindow, public TBMessageHandler, public TBWidgetListener -{ -public: - // For safe typecasting - TBOBJECT_SUBCLASS(ResourceEditWindow, TBWindow); - - ResourceEditWindow(); - ~ResourceEditWindow(); - - void updateWidgetList(bool immediately); - - struct ITEM_INFO { - ResourceItem *item; - int index; - }; - ITEM_INFO getItemFromWidget(TBWidget *widget); - TBWidget *getSelectedWidget() { return m_selected_widget.get(); } - void setSelectedWidget(TBWidget *widget); - - void load(const char *resource_file); - void load(const core::String& resource_file) { - load(resource_file.c_str()); - } - void refreshFromSource(); - - // == TBWindow ====================================================================== - virtual bool onEvent(const TBWidgetEvent &ev) override; - virtual void onPaintChildren(const PaintProps &paint_props) override; - - // == TBMessageHandler ============================================================== - virtual void onMessageReceived(TBMessage *msg) override; - - // == TBWidgetListener ======================================================== - virtual bool onWidgetInvokeEvent(TBWidget *widget, const TBWidgetEvent &ev) override; - virtual void onWidgetAdded(TBWidget *parent, TBWidget *child) override; - virtual void onWidgetRemove(TBWidget *parent, TBWidget *child) override; -private: - TBSelectList *m_widget_list; - TBSelectItemSourceList m_widget_list_source; - TBScrollContainer *m_scroll_container; - TBWidget *m_build_container; - TBEditField *m_source_edit; - core::String m_resource_filename; - TBWidgetSafePointer m_selected_widget; - void addWidgetListItemsRecursive(TBWidget *widget, int depth); - bool onDropFileEvent(const TBWidgetEvent &ev); -}; - -#endif // ResourceEditWindow_H diff --git a/src/tests/testturbobadger/TestTurbobadger.cpp b/src/tests/testturbobadger/TestTurbobadger.cpp deleted file mode 100644 index d1c905b16..000000000 --- a/src/tests/testturbobadger/TestTurbobadger.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file - */ - -#include "TestTurbobadger.h" -#include "testcore/TestAppMain.h" -#include "ui/turbobadger/TurboBadger.h" -#include "Demo.h" -#include "ListWindow.h" -#include "ResourceEditWindow.h" - -extern AdvancedItemSource advanced_source; -extern tb::TBGenericStringItemSource name_source; -extern tb::TBGenericStringItemSource popup_menu_source; - -const char *girl_names[] = { - "Maja", "Alice", "Julia", "Linnéa", "Wilma", "Ella", "Elsa", "Emma", "Alva", "Olivia", "Molly", "Ebba", "Klara", "Nellie", "Agnes", - "Isabelle", "Ida", "Elin", "Ellen", "Moa", "Emilia", "Nova", "Alma", "Saga", "Amanda", "Isabella", "Lilly", "Alicia", "Astrid", - "Matilda", "Tuva", "Tilde", "Stella", "Felicia", "Elvira", "Tyra", "Hanna", "Sara", "Vera", "Thea", "Freja", "Lova", "Selma", - "Meja", "Signe", "Ester", "Lovisa", "Ellie", "Lea", "Tilda", "Tindra", "Sofia", "Nora", "Nathalie", "Leia", "Filippa", "Siri", - "Emelie", "Inez", "Edith", "Stina", "Liv", "Lisa", "Linn", "Tove", "Emmy", "Livia", "Jasmine", "Evelina", "Cornelia", "Märta", - "Svea", "Ingrid", "My", "Rebecca", "Joline", "Mira", "Ronja", "Hilda", "Melissa", "Anna", "Frida", "Maria", "Iris", "Josefine", - "Elise", "Elina", "Greta", "Vilda", "Minna", "Lina", "Hedda", "Nicole", "Kajsa", "Majken", "Sofie", "Annie", "Juni", "Novalie", "Hedvig", nullptr }; -const char *boy_names[] = { - "Oscar", "William", "Lucas", "Elias", "Alexander", "Hugo", "Oliver", "Theo", "Liam", "Leo", "Viktor", "Erik", "Emil", - "Isak", "Axel", "Filip", "Anton", "Gustav", "Edvin", "Vincent", "Arvid", "Albin", "Ludvig", "Melvin", "Noah", "Charlie", "Max", - "Elliot", "Viggo", "Alvin", "Alfred", "Theodor", "Adam", "Olle", "Wilmer", "Benjamin", "Simon", "Nils", "Noel", "Jacob", "Leon", - "Rasmus", "Kevin", "Linus", "Casper", "Gabriel", "Jonathan", "Milo", "Melker", "Felix", "Love", "Ville", "Sebastian", "Sixten", - "Carl", "Malte", "Neo", "David", "Joel", "Adrian", "Valter", "Josef", "Jack", "Hampus", "Samuel", "Mohammed", "Alex", "Tim", - "Daniel", "Vilgot", "Wilhelm", "Harry", "Milton", "Maximilian", "Robin", "Sigge", "Måns", "Eddie", "Elton", "Vidar", "Hjalmar", - "Loke", "Elis", "August", "John", "Hannes", "Sam", "Frank", "Svante", "Marcus", "Mio", "Otto", "Ali", "Johannes", "Fabian", - "Ebbe", "Aron", "Julian", "Elvin", "Ivar", nullptr }; - -TestTurbobadger::TestTurbobadger(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider) : - Super(metric, filesystem, eventBus, timeProvider) { - init(ORGANISATION, "testturbobadger"); -} - -app::AppState TestTurbobadger::onInit() { - _applicationSkin = "ui/skin/skin.tb.txt"; - app::AppState state = Super::onInit(); - - // Load language file - if (!tb::g_tb_lng->load("demo01/language/lng_en.tb.txt")) { - Log::warn("Could not load translation lng_en.tb.txt"); - } - - // Block new animations during Init. - tb::TBAnimationBlocker anim_blocker; - - // TBSelectList and TBSelectDropdown widgets have a default item source that are fed with any items - // specified in the resource files. But it is also possible to set any source which can save memory - // and improve performance. Then you don't have to populate each instance with its own set of items, - // for widgets that occur many times in a UI, always with the same items. - // Here we prepare the name source, that is used in a few places. - for (int i = 0; boy_names[i]; i++) - advanced_source.addItem(new AdvancedItem(boy_names[i++], TBIDC("boy_item"), true)); - for (int i = 0; girl_names[i]; i++) - advanced_source.addItem(new AdvancedItem(girl_names[i++], TBIDC("girl_item"), false)); - for (int i = 0; girl_names[i]; i++) - name_source.addItem(new TBGenericStringItem(girl_names[i++], TBIDC("girl_item"))); - for (int i = 0; boy_names[i]; i++) - name_source.addItem(new TBGenericStringItem(boy_names[i++], TBIDC("boy_item"))); - advanced_source.setSort(TB_SORT_ASCENDING); - name_source.setSort(TB_SORT_ASCENDING); - - // Prepare a source with submenus (with eternal recursion) so we can test sub menu support. - popup_menu_source.addItem(new TBGenericStringItem("Option 1", TBIDC("opt 1"))); - popup_menu_source.addItem(new TBGenericStringItem("Option 2", TBIDC("opt 2"))); - popup_menu_source.addItem(new TBGenericStringItem("-")); - popup_menu_source.addItem(new TBGenericStringItem("Same submenu", &popup_menu_source)); - popup_menu_source.addItem(new TBGenericStringItem("Long submenu", &name_source)); - // Give the first item a skin image - popup_menu_source.getItem(0)->setSkinImage(TBIDC("Icon16")); - - new MainWindow(_root); - - new EditWindow(_root); - - new ListWindow(_root, &name_source); - - new AdvancedListWindow(_root, &advanced_source); - - new TabContainerWindow(_root); - - return state; -} - -TEST_APP(TestTurbobadger) diff --git a/src/tests/testturbobadger/TestTurbobadger.h b/src/tests/testturbobadger/TestTurbobadger.h deleted file mode 100644 index 4bacdce6b..000000000 --- a/src/tests/testturbobadger/TestTurbobadger.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/UIApp.h" - -class TestTurbobadger: public ui::turbobadger::UIApp { -private: - using Super = ui::turbobadger::UIApp; -public: - TestTurbobadger(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider); - - app::AppState onInit(); -}; diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index e16c58507..bf3bfe411 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(shadertool) add_subdirectory(computeshadertool) add_subdirectory(databasetool) -add_subdirectory(uitool) if (TOOLS) if (VOXEDIT) @@ -24,11 +23,6 @@ if (TOOLS) else() message(STATUS "Don't build mapview") endif() - if (NOISETOOL) - add_subdirectory(noisetool) - else() - message(STATUS "Don't build noisetool") - endif() add_subdirectory(aidebug) else() message(STATUS "Don't build the tools") diff --git a/src/tools/noisetool/CMakeLists.txt b/src/tools/noisetool/CMakeLists.txt deleted file mode 100644 index 2ff58a4a9..000000000 --- a/src/tools/noisetool/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -project(noisetool) -set(SRCS - NoiseTool.h NoiseTool.cpp - NoiseData.h - NoiseType.h NoiseType.cpp - - ui/NoiseToolWindow.h ui/NoiseToolWindow.cpp - ui/NoiseDataNodeWindow.h ui/NoiseDataNodeWindow.cpp - - ui/noisedata/NoiseDataNodeWidget.h ui/noisedata/NoiseDataNodeWidget.cpp - ui/noisedata/NoiseDataItemWidget.h ui/noisedata/NoiseDataItemWidget.cpp -) - -set(UI_WINDOWS - noisetool/ui/window/noisetool-nodes.tb.txt - noisetool/ui/window/noisetool-main.tb.txt -) - -set(FILES - ${UI_WINDOWS} - noisetool/ui/widget/noisetool-noisedata-list.tb.txt - noisetool/ui/widget/noisetool-noisedata-node.tb.txt - noisetool/ui/widget/noisetool-noisedata-item.tb.txt - noisetool/ui/skin/noisetool-skin.tb.txt - noisetool/ui/lang/en.tb.txt -) - -engine_add_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS} FILES ${FILES} WINDOWED) -engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES turbobadger frontend) -check_ui_turbobadger(${PROJECT_NAME} ${UI_WINDOWS}) diff --git a/src/tools/noisetool/NoiseData.h b/src/tools/noisetool/NoiseData.h deleted file mode 100644 index 489a051bf..000000000 --- a/src/tools/noisetool/NoiseData.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "NoiseType.h" - -struct NoiseData { - float separation = 5.0f; // poisson - float frequency = 0.0f; - float offset = 0.0f; - float lacunarity = 0.0f; - int octaves = 0; - float gain = 0.0f; - uint64_t millis = 0; - uint64_t endmillis = 0; - int seed = 0; - float ridgedOffset = 1.0f; // ridged noise - NoiseType noiseType = NoiseType::Max; - bool enableDistance = false; // voronoi - - tb::TBImage graph; - tb::TBImage noise; -}; diff --git a/src/tools/noisetool/NoiseTool.cpp b/src/tools/noisetool/NoiseTool.cpp deleted file mode 100644 index 551ce3752..000000000 --- a/src/tools/noisetool/NoiseTool.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file - */ - -#include "NoiseTool.h" -#include "ui/NoiseToolWindow.h" -#include "ui/noisedata/NoiseDataItemWidget.h" -#include "io/Filesystem.h" -#include "metric/Metric.h" -#include "core/EventBus.h" -#include "core/TimeProvider.h" - -NoiseTool::NoiseTool(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider) : - Super(metric, filesystem, eventBus, timeProvider) { - init(ORGANISATION, "noisetool"); -} - -void NoiseTool::add(uint32_t dataId, const NoiseData& data) { - auto pair = _noiseData.insert(std::make_pair(dataId, data)); - if (pair.second) { - const char* name = getNoiseTypeName(data.noiseType); - _noiseItemSource->addItem(new NoiseItem(name, dataId, data)); - } -} - -void NoiseTool::remove(uint32_t dataId) { - if (_noiseData.erase(dataId) <= 0) { - return; - } - const int n = _noiseItemSource->getNumItems(); - for (int i = 0; i < n; ++i) { - if (_noiseItemSource->getItemID(i) == dataId) { - _noiseItemSource->deleteItem(i); - return; - } - } -} - -app::AppState NoiseTool::onInit() { - app::AppState state = Super::onInit(); - if (state != app::AppState::Running) { - return state; - } - - _noiseItemSource = new NoiseItemSource(this); - - _window = new NoiseToolWindow(this); - if (!_window->init()) { - return app::AppState::InitFailure; - } - - return state; -} - -void NoiseTool::onRenderUI() { - _window->update(); -} - -int main(int argc, char *argv[]) { - const core::EventBusPtr& eventBus = std::make_shared(); - const io::FilesystemPtr& filesystem = std::make_shared(); - const core::TimeProviderPtr& timeProvider = std::make_shared(); - const metric::MetricPtr& metric = std::make_shared(); - NoiseTool app(metric, filesystem, eventBus, timeProvider); - return app.startMainLoop(argc, argv); -} diff --git a/src/tools/noisetool/NoiseTool.h b/src/tools/noisetool/NoiseTool.h deleted file mode 100644 index 031dcce79..000000000 --- a/src/tools/noisetool/NoiseTool.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/UIApp.h" -#include "NoiseData.h" -#include - -class NoiseItemSource; -class NoiseToolWindow; - -typedef std::map NoiseDataMap; - -// TODO: implement combinations - -/** - * @brief This tool provides a UI to create noise images on-the-fly. - * - * @ingroup Tools - */ -class NoiseTool: public ui::turbobadger::UIApp { -private: - using Super = ui::turbobadger::UIApp; - NoiseDataMap _noiseData; - NoiseToolWindow* _window = nullptr; - NoiseItemSource* _noiseItemSource = nullptr; -public: - NoiseTool(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider); - - void add(uint32_t dataId, const NoiseData& data); - void remove(uint32_t dataId); - - NoiseItemSource* noiseItemSource(); - - const NoiseDataMap& noiseData() const; - - app::AppState onInit() override; - void onRenderUI() override; -}; - -inline const NoiseDataMap& NoiseTool::noiseData() const { - return _noiseData; -} - -inline NoiseItemSource* NoiseTool::noiseItemSource() { - return _noiseItemSource; -} diff --git a/src/tools/noisetool/NoiseType.cpp b/src/tools/noisetool/NoiseType.cpp deleted file mode 100644 index 534fca0f1..000000000 --- a/src/tools/noisetool/NoiseType.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file - */ - -#include "NoiseType.h" -#include "core/ArrayLength.h" - -static const char *NoiseTypeStr[] = { - "double noise", - "simplex noise", - "ridged noise", - "flow noise (rot. gradients)", - "fbm", - "fbm cascade", - "fbm analytical derivatives", - "flow noise fbm (time)", - "ridged multi fractal (time)", - "ridged multi fractal", - "ridged multi fractal cascade", - "iq noise", - "analytical derivatives", - "noise curl noise (time)", - "worley noise", - "worley noise fbm", - "voronoi", - "swissTurbulence", - "jordanTurbulence", - "poissonDiskDistribution" -}; -static_assert(lengthof(NoiseTypeStr) == (int)NoiseType::Max, "String array size doesn't match noise types"); - -const char *getNoiseTypeName(NoiseType t) { - return NoiseTypeStr[(int)t]; -} diff --git a/src/tools/noisetool/NoiseType.h b/src/tools/noisetool/NoiseType.h deleted file mode 100644 index b5acdb4aa..000000000 --- a/src/tools/noisetool/NoiseType.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file - */ - -#pragma once - -enum class NoiseType { - doubleNoise, - simplexNoise, - ridgedNoise, - flowNoise, - fbm, - fbmCascade, - fbmAnalyticalDerivatives, - flowNoiseFbm, - ridgedMFTime, - ridgedMF, - ridgedMFCascade, - iqNoise, - analyticalDerivatives, - noiseCurlNoise, - worleyNoise, - worleyNoiseFbm, - voronoi, - swissTurbulence, - jordanTurbulence, - poissonDiskDistribution, - - Max -}; - -extern const char* getNoiseTypeName(NoiseType t); diff --git a/src/tools/noisetool/ui/NoiseDataNodeWindow.cpp b/src/tools/noisetool/ui/NoiseDataNodeWindow.cpp deleted file mode 100644 index bbcf38336..000000000 --- a/src/tools/noisetool/ui/NoiseDataNodeWindow.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file - */ - -#include "NoiseDataNodeWindow.h" - -#include "../NoiseTool.h" -#include "noisedata/NoiseDataNodeWidget.h" -#include "noisedata/NoiseDataItemWidget.h" -#include "ui/turbobadger/UIApp.h" - -NoiseDataNodeWindow::NoiseDataNodeWindow(NoiseTool* tool) : - Super(tool), _noiseTool(tool) { -} - -bool NoiseDataNodeWindow::init() { - if (!loadResourceFile("ui/window/noisetool-nodes.tb.txt")) { - Log::error("Failed to init the main window: Could not load the ui definition"); - return false; - } - - _nodesWidget = getWidgetByType("nodes"); - if (_nodesWidget == nullptr) { - Log::error("Could not find nodes widget"); - return false; - } - - // TEST TEST TEST - NoiseItemSource* source = _noiseTool->noiseItemSource(); - const int n = source->getNumItems(); - for (int index = 0; index < n; ++index) { - NoiseItem* item = source->getItem(index); - NoiseDataNodeWidget *itemWidget = new NoiseDataNodeWidget(item); - _nodesWidget->getContentRoot()->addChild(itemWidget); - } - return true; -} - -bool NoiseDataNodeWindow::onEvent(const tb::TBWidgetEvent &ev) { - return Super::onEvent(ev); -} diff --git a/src/tools/noisetool/ui/NoiseDataNodeWindow.h b/src/tools/noisetool/ui/NoiseDataNodeWindow.h deleted file mode 100644 index e262f8a2d..000000000 --- a/src/tools/noisetool/ui/NoiseDataNodeWindow.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/Window.h" - -class NoiseTool; -class NoiseDataItemWidget; - -class NoiseDataNodeWindow: public ui::turbobadger::Window { -private: - using Super = ui::turbobadger::Window; - - tb::TBWidget* _nodesWidget = nullptr; - NoiseTool* _noiseTool; -public: - NoiseDataNodeWindow(NoiseTool* tool); - bool init(); - - bool onEvent(const tb::TBWidgetEvent &ev) override; -}; diff --git a/src/tools/noisetool/ui/NoiseToolWindow.cpp b/src/tools/noisetool/ui/NoiseToolWindow.cpp deleted file mode 100644 index 1158ba049..000000000 --- a/src/tools/noisetool/ui/NoiseToolWindow.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/** - * @file - */ - -#include "NoiseToolWindow.h" -#include "../NoiseTool.h" -#include "noisedata/NoiseDataItemWidget.h" -#include "noise/Noise.h" -#include "ui/turbobadger/UIApp.h" -#include "core/Color.h" -#include "core/TimeProvider.h" -#include "core/concurrent/ThreadPool.h" -#include "math/Rect.h" -#include "NoiseDataNodeWindow.h" -#include "noise/PoissonDiskDistribution.h" -#include "noise/Simplex.h" - -#define IMAGE_PREFIX "2d" -#define GRAPH_PREFIX "graph" - -NoiseToolWindow::NoiseToolWindow(NoiseTool* tool) : - Super(tool), _noiseTool(tool) { - for (int i = 0; i < (int)NoiseType::Max; ++i) { - addStringItem(_noiseTypeSource, getNoiseTypeName((NoiseType)i)); - } -} - -NoiseToolWindow::~NoiseToolWindow() { - if (_noiseType != nullptr) { - _noiseType->setSource(nullptr); - } - if (_select != nullptr) { - _select->setSource(nullptr); - } - delete[] _graphBufferBackground; - _graphBufferBackground = nullptr; - _noise.shutdown(); -} - -bool NoiseToolWindow::init() { - if (!_noise.init()) { - return false; - } - if (!loadResourceFile("ui/window/noisetool-main.tb.txt")) { - Log::error("Failed to init the main window: Could not load the ui definition"); - return false; - } - _noiseType = getWidgetByType("type"); - if (_noiseType == nullptr) { - Log::error("Failed to init the main window: No type widget found"); - return false; - } - _noiseType->setSource(&_noiseTypeSource); - - _select = getWidgetByType("list"); - if (_select == nullptr) { - return false; - } - _select->setSource(_noiseTool->noiseItemSource()); - _select->getScrollContainer()->setScrollMode(tb::SCROLL_MODE_X_AUTO_Y_AUTO); - - const tb::TBRect& rect = _select->getPaddingRect(); - _noiseHeight = rect.h; - _noiseWidth = rect.w - 60; - const size_t graphBufferSize = _noiseWidth * _graphHeight * BPP; - - _graphBufferBackground = new uint8_t[graphBufferSize]; - const int graphBufOffset = index(0, int(_graphHeight / 2)); - memset(&_graphBufferBackground[graphBufOffset], core::Color::getRGBA(core::Color::Gray), _noiseWidth * BPP); - - for (int i = 0; i < _graphHeight; ++i) { - uint8_t* gbuf = &_graphBufferBackground[index(10, i)]; - *((uint32_t*)gbuf) = core::Color::getRGBA(core::Color::Gray); - } - - return true; -} - -void NoiseToolWindow::updateForNoiseType(NoiseType type) { - setActive("enabledistance", type == NoiseType::voronoi); - setActive("seed", type == NoiseType::voronoi); - setActive("separation", type == NoiseType::poissonDiskDistribution); - setActive("lacunarity", type == NoiseType::fbm || type == NoiseType::ridgedMFTime || type == NoiseType::ridgedMF || type == NoiseType::worleyNoiseFbm || type == NoiseType::swissTurbulence); - setActive("octaves", type == NoiseType::fbm || type == NoiseType::ridgedMFTime || type == NoiseType::ridgedMF || type == NoiseType::iqNoise || type == NoiseType::worleyNoiseFbm || type == NoiseType::swissTurbulence || type == NoiseType::jordanTurbulence); - setActive("gain", type == NoiseType::fbm || type == NoiseType::ridgedMFTime || type == NoiseType::ridgedMF || type == NoiseType::iqNoise || type == NoiseType::worleyNoiseFbm || type == NoiseType::swissTurbulence); - setActive("ridgedoffset", type == NoiseType::ridgedMFTime || type == NoiseType::ridgedMF); - setActive("offset", true); - setActive("frequency", true); -} - -float NoiseToolWindow::getNoise(int x, int y, const NoiseData& data) { - const NoiseType noiseType = data.noiseType; - const glm::vec2 position(data.offset + x * data.frequency, data.offset + y * data.frequency); - switch (noiseType) { - case NoiseType::doubleNoise: { - const glm::ivec3 p3(position.x, position.y, 0); - return _noise.doubleValueNoise(p3, 0); - } - case NoiseType::simplexNoise: - return noise::noise(position); - case NoiseType::ridgedNoise: - return noise::ridgedNoise(position); - case NoiseType::flowNoise: - return noise::flowNoise(position, data.millis); - case NoiseType::fbm: - return noise::fBm(position, data.octaves, data.lacunarity, data.gain); - case NoiseType::fbmCascade: - return noise::fBm(noise::fBm(position)); - case NoiseType::fbmAnalyticalDerivatives: - return noise::fBm(noise::dfBm(position)); - case NoiseType::flowNoiseFbm: { - const glm::vec3 p3(position, data.millis * 0.1f); - const float fbm = noise::fBm(p3, data.octaves, data.lacunarity, data.gain); - return noise::flowNoise(position + fbm, data.millis); - } - case NoiseType::ridgedMFTime: { - const glm::vec3 p3(position, data.millis * 0.1f); - return noise::ridgedMF(p3, data.ridgedOffset, data.octaves, data.lacunarity, data.gain); - } - case NoiseType::ridgedMF: - return noise::ridgedMF(position, data.ridgedOffset, data.octaves, data.lacunarity, data.gain); - case NoiseType::ridgedMFCascade: { - const float n = noise::ridgedMF(position, data.ridgedOffset, data.octaves, data.lacunarity, data.gain); - return noise::ridgedMF(n, data.ridgedOffset, data.octaves, data.lacunarity, data.gain); - } - case NoiseType::iqNoise: - return noise::iqMatfBm(position, data.octaves, glm::mat2(2.3f, -1.5f, 1.5f, 2.3f), data.gain); - case NoiseType::analyticalDerivatives: { - const glm::vec3& n = noise::dnoise(position); - return (n.y + n.z) * 0.5f; - } - case NoiseType::noiseCurlNoise: { - const glm::vec2& n = noise::curlNoise(position, data.millis); - return noise::noise(glm::vec2(position.x + n.x, position.y + n.x)); - } - case NoiseType::voronoi: { - const glm::dvec3 p3(position.x, position.y, 0.0); - return _noise.voronoi(p3, data.enableDistance, 1.0, data.seed); - } - case NoiseType::worleyNoise: - return noise::worleyNoise(position); - case NoiseType::worleyNoiseFbm: - return noise::worleyfBm(position, data.octaves, data.lacunarity, data.gain); - case NoiseType::swissTurbulence: - return _noise.swissTurbulence(position, 0.0f, data.octaves, data.lacunarity, data.gain); - case NoiseType::jordanTurbulence: - // float gain0, float gain, float warp0, float warp, float damp0, float damp, float damp_scale; - return _noise.jordanTurbulence(position, 0.0f, data.octaves, data.lacunarity, data.gain); - case NoiseType::poissonDiskDistribution: - case NoiseType::Max: - break; - } - return 0.0f; -} - -bool NoiseToolWindow::onEvent(const tb::TBWidgetEvent &ev) { - const tb::TBID& id = ev.target->getID(); - if (ev.type == tb::EVENT_TYPE_CLICK) { - if (id == TBIDC("ok")) { - generateImage(); - return true; - } else if (id == TBIDC("all")) { - generateAll(); - return true; - } else if (id == TBIDC("quit")) { - close(); - return true; - } else if (id == TBIDC("nodes")) { - NoiseDataNodeWindow* window = new NoiseDataNodeWindow(_noiseTool); - window->init(); - return true; - } - } - - if (ev.type == tb::EVENT_TYPE_CHANGED) { - if (id == TBIDC("filter") && _select != nullptr) { - _select->setFilter(ev.target->getText()); - return true; - } else if (id == TBIDC("type")) { - const int type = getSelectedId("type"); - if (type >= 0 && type < (int)NoiseType::Max) { - NoiseType noiseType = (NoiseType)type; - updateForNoiseType(noiseType); - } - return true; - } - } - return Super::onEvent(ev); -} - -void NoiseToolWindow::generateImage() { - const int type = getSelectedId("type"); - if (type < 0 || type >= (int)NoiseType::Max) { - return; - } - - generateImage((NoiseType)type); -} - -void NoiseToolWindow::generateAll() { - for (int i = 0; i < (int)NoiseType::Max; ++i) { - NoiseType type = (NoiseType)i; - generateImage(type); - } -} - -void NoiseToolWindow::generateImage(NoiseType type) { - core_trace_scoped(GenerateImage); - Log::info("Generate noise for %s", getNoiseTypeName(type)); - NoiseData data; - data.enableDistance = isToggled("enabledistance"); - data.separation = getFloat("separation"); - data.seed = getInt("seed"); - data.offset = getFloat("offset"); - data.lacunarity = getFloat("lacunarity"); - data.octaves = getInt("octaves"); - data.gain = getFloat("gain"); - data.frequency = getFloat("frequency"); - data.ridgedOffset = getFloat("ridgedoffset"); - data.noiseType = type; - - _noiseTool->threadPool().enqueue([this, data] () { - const size_t noiseBufferSize = _noiseWidth * _noiseHeight * BPP; - const size_t graphBufferSize = _noiseWidth * _graphHeight * BPP; - QueueData qd; - qd.data = data; - qd.data.millis = core::TimeProvider::systemMillis(); - qd.noiseBuffer = new uint8_t[noiseBufferSize]; - qd.graphBuffer = new uint8_t[graphBufferSize]; - - uint8_t* noiseBuffer = qd.noiseBuffer; - uint8_t* graphBuffer = qd.graphBuffer; - - core_memset(noiseBuffer, 255, noiseBufferSize); - core_memcpy(graphBuffer, _graphBufferBackground, graphBufferSize); - - if (qd.data.noiseType == NoiseType::poissonDiskDistribution) { - const math::Rect area(0, 0, _noiseWidth - 1, _noiseHeight - 1); - const std::vector& distrib = noise::poissonDiskDistribution(qd.data.separation, area); - for (const glm::vec2& v : distrib) { - const int x = v.x; - const int y = v.y; - uint8_t* buf = &noiseBuffer[index(x, y)]; - *((uint32_t*)buf) = core::Color::getRGBA(core::Color::Black); - } - } else { - const int h = _graphHeight - 1; - for (int y = 0; y < _noiseHeight; ++y) { - for (int x = 0; x < _noiseWidth; ++x) { - const float n = getNoise(x, y, qd.data); - const float cn = noise::norm(n); - const uint8_t c = cn * 255; - uint8_t* buf = &noiseBuffer[index(x, y)]; - memset(buf, c, BPP - 1); - if (y == 0 && x < _noiseWidth) { - const int gy = h - (cn * h); - const int idx = index(x, gy); - uint8_t* gbuf = &graphBuffer[idx]; - *((uint32_t*)gbuf) = core::Color::getRGBA(core::Color::Red); - } - } - } - } - - qd.data.endmillis = core::TimeProvider::systemMillis(); - this->_queue.push(qd); - }); -} - -void NoiseToolWindow::update() { - QueueData qd; - if (!_queue.pop(qd)) { - return; - } - NoiseData& data = qd.data; - - const core::String& buf = core::string::format("-%f-%i-%f-%i-%f-%i-%f-%f-%f", - data.ridgedOffset, (int)data.noiseType, data.separation, (int)data.enableDistance, - data.offset, data.octaves, data.lacunarity, data.gain, data.frequency); - - const core::String imageBuf = IMAGE_PREFIX + buf; - data.noise = tb::g_image_manager->getImage(imageBuf.c_str(), (uint32_t*)qd.noiseBuffer, _noiseWidth, _noiseHeight); - - const core::String graphBuf = GRAPH_PREFIX + buf; - data.graph = tb::g_image_manager->getImage(graphBuf.c_str(), (uint32_t*)qd.graphBuffer, _noiseWidth, _graphHeight); - - _noiseTool->add(TBIDC(graphBuf.c_str()), data); - - const int n = _select->getSource()->getNumItems(); - _select->setValue(n - 1); - Log::info("Generating noise for %s took %" SDL_PRIu64 "ms", getNoiseTypeName(data.noiseType), data.endmillis - data.millis); - delete [] qd.noiseBuffer; - delete [] qd.graphBuffer; -} - -void NoiseToolWindow::onDie() { - Super::onDie(); - requestQuit(); -} diff --git a/src/tools/noisetool/ui/NoiseToolWindow.h b/src/tools/noisetool/ui/NoiseToolWindow.h deleted file mode 100644 index 52a543ddf..000000000 --- a/src/tools/noisetool/ui/NoiseToolWindow.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/Window.h" -#include "ui/turbobadger/ui_widgets.h" -#include "core/Common.h" -#include "core/collection/ConcurrentPriorityQueue.h" -#include "noise/Noise.h" -#include "../NoiseData.h" - -class NoiseTool; - -class NoiseToolWindow: public ui::turbobadger::Window { -private: - static constexpr int BPP = 4; - static_assert(BPP == sizeof(uint32_t), "This code heavily relies on RGBA being 32bit"); - using Super = ui::turbobadger::Window; - - noise::Noise _noise; - NoiseTool* _noiseTool; - tb::TBSelectList* _select = nullptr; - - tb::TBSelectDropdown* _noiseType = nullptr; - tb::TBGenericStringItemSource _noiseTypeSource; - - struct QueueData { - NoiseData data; - uint8_t *noiseBuffer; - uint8_t *graphBuffer; - - inline bool operator<(const QueueData& rhs) const { - return noiseBuffer < rhs.noiseBuffer; - } - }; - core::ConcurrentPriorityQueue _queue; - - int _noiseWidth = 768; - int _noiseHeight = 1024; - const int _graphHeight = 65; - uint8_t *_graphBufferBackground = nullptr; - - /** - * @return the noise in the range [-1.0 - 1.0] - */ - float getNoise(int x, int y, const NoiseData& _data); - int index(int x, int y) const; - void generateImage(); - void updateForNoiseType(NoiseType type); - void generateImage(NoiseType type); - void generateAll(); -public: - NoiseToolWindow(NoiseTool* tool); - ~NoiseToolWindow(); - bool init(); - void update(); - - bool onEvent(const tb::TBWidgetEvent &ev) override; - void onDie() override; -}; - -inline int NoiseToolWindow::index(int x, int y) const { - core_assert_msg(x >= 0, "x is smaller than 0: %i", x); - core_assert_msg(x < _noiseWidth * BPP, "x is out of bounds: %i", x); - core_assert_msg(y >= 0, "y is smaller than 0: %i", y); - return x * BPP + y * _noiseWidth * BPP; -} diff --git a/src/tools/noisetool/ui/noisedata/NoiseDataItemWidget.cpp b/src/tools/noisetool/ui/noisedata/NoiseDataItemWidget.cpp deleted file mode 100644 index 5e45be2b7..000000000 --- a/src/tools/noisetool/ui/noisedata/NoiseDataItemWidget.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @file - */ - -#include "NoiseDataItemWidget.h" -#include "../../NoiseTool.h" -#include "ui/turbobadger/ui_widgets.h" -#include "core/StringUtil.h" - -NoiseItemSource::NoiseItemSource(NoiseTool* tool) : - _tool(tool) { -} - -bool NoiseItemSource::filter(int index, const char *filter) { - if (TBSelectItemSource::filter(index, filter)) { - return true; - } - const NoiseItem* item = getItem(index); - const NoiseData& data = item->data(); - if (data.octaves == core::string::toInt(filter)) { - return true; - } - return false; -} - -tb::TBWidget *NoiseItemSource::createItemWidget(int index, tb::TBSelectItemViewer *viewer) { - NoiseItem* item = getItem(index); - NoiseDataItemWidget *widget = new NoiseDataItemWidget(_tool, item, this, index); - return widget; -} - -#define NOISEDATADETAIL(text, type) \ - if (tb::TBTextField *widget = getWidgetByIDAndType(TBIDC(#type))) { \ - const NoiseData& data = item->data(); \ - const core::String& str = core::string::format(text, data.type); \ - widget->setText(str); \ - } - -#define NOISEDATADETAILDATA(text, id, data) \ - if (tb::TBTextField *widget = getWidgetByIDAndType(TBIDC(id))) { \ - const core::String& str = core::string::format(text, data); \ - widget->setText(str); \ - } - -NoiseDataItemWidget::NoiseDataItemWidget(NoiseTool* tool, NoiseItem *item, NoiseItemSource *source, int index) : - Super(), _source(source), _index(index), _tool(tool) { - setSkinBg(TBIDC("TBSelectItem")); - setLayoutDistribution(tb::LAYOUT_DISTRIBUTION_GRAVITY); - setLayoutDistributionPosition(tb::LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); - setPaintOverflowFadeout(false); - - core_assert_always(tb::g_widgets_reader->loadFile(getContentRoot(), "ui/widget/noisetool-noisedata-item.tb.txt")); - if (tb::TBTextField *name = getWidgetByIDAndType(TBIDC("name"))) { - name->setText(item->str); - } - NOISEDATADETAIL("Frequency: %f", frequency); - NOISEDATADETAIL("Lacunarity: %f", lacunarity); - NOISEDATADETAIL("Octaves: %i", octaves); - NOISEDATADETAIL("Gain: %f", gain); - NOISEDATADETAILDATA("Millis: %lu", "millis", item->data().endmillis - item->data().millis); - - if (ImageWidget *widget = getWidgetByIDAndType(TBIDC("noise"))) { - widget->setImage(item->data().noise); - } - if (ImageWidget *widget = getWidgetByIDAndType(TBIDC("graph"))) { - widget->setImage(item->data().graph); - } -} - -#undef NOISEDATADETAILDATA - -bool NoiseDataItemWidget::onEvent(const tb::TBWidgetEvent &ev) { - if (ev.type == tb::EVENT_TYPE_CLICK) { - if (ev.target->getID() == TBIDC("delete")) { - const tb::TBID& id = _source->getItemID(_index); - _tool->remove(id); - return true; - } - } - return Super::onEvent(ev); -} - -NoiseDataList::NoiseDataList() { - setLayoutDistribution(tb::LAYOUT_DISTRIBUTION_GRAVITY); - setLayoutDistributionPosition(tb::LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); - setPaintOverflowFadeout(false); - setAxis(tb::AXIS::AXIS_Y); - - core_assert_always(tb::g_widgets_reader->loadFile(getContentRoot(), "ui/widget/noisetool-noisedata-list.tb.txt")); - - _select = getWidgetByIDAndType("list"); - if (_select != nullptr) { - NoiseItemSource* source = ((NoiseTool*)app::App::getInstance())->noiseItemSource(); - _select->setSource(source); - _select->getScrollContainer()->setScrollMode(tb::SCROLL_MODE_X_AUTO_Y_AUTO); - } else { - Log::error("Could not find list widget"); - } -} - -NoiseDataList::~NoiseDataList() { - if (_select != nullptr) { - _select->setSource(nullptr); - } -} - -bool NoiseDataList::onEvent(const tb::TBWidgetEvent &ev) { - if (_select != nullptr) { - const tb::TBID& id = ev.target->getID(); - if (ev.type == tb::EVENT_TYPE_CHANGED && id == TBIDC("filter")) { - _select->setFilter(ev.target->getText()); - return true; - } - } - return Super::onEvent(ev); -} - -static NoiseDataListFactory noiseDataList_wf; diff --git a/src/tools/noisetool/ui/noisedata/NoiseDataItemWidget.h b/src/tools/noisetool/ui/noisedata/NoiseDataItemWidget.h deleted file mode 100644 index 142034aeb..000000000 --- a/src/tools/noisetool/ui/noisedata/NoiseDataItemWidget.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/TurboBadger.h" -#include "../../NoiseData.h" - -class NoiseTool; - -class NoiseItem: public tb::TBGenericStringItem { -public: - NoiseItem(const char* str, const tb::TBID& id, const NoiseData& data) : - tb::TBGenericStringItem(str, id), _data(data) { - } - const NoiseData& data() const { - return _data; - } - -private: - NoiseData _data; -}; - -class NoiseItemSource: public tb::TBSelectItemSourceList { -private: - NoiseTool* _tool; - -public: - NoiseItemSource(NoiseTool* tool); - - virtual bool filter(int index, const char *filter); - virtual tb::TBWidget *createItemWidget(int index, tb::TBSelectItemViewer *viewer); -}; - -class NoiseDataItemWidget: public tb::TBLayout { -private: - using Super = tb::TBLayout; - NoiseItemSource *_source; - int _index; - NoiseTool* _tool; -public: - NoiseDataItemWidget(NoiseTool* tool, NoiseItem *item, NoiseItemSource *source, int index); - - bool onEvent(const tb::TBWidgetEvent &ev) override; -}; - -class NoiseDataList : public tb::TBLayout { -private: - tb::TBSelectList* _select; -public: - UIWIDGET_SUBCLASS(NoiseDataList, tb::TBLayout); - - NoiseDataList(); - ~NoiseDataList(); - - bool onEvent(const tb::TBWidgetEvent &ev) override; -}; - -UIWIDGET_FACTORY(NoiseDataList, tb::TBValue::TYPE_NULL, tb::WIDGET_Z_TOP) diff --git a/src/tools/noisetool/ui/noisedata/NoiseDataNodeWidget.cpp b/src/tools/noisetool/ui/noisedata/NoiseDataNodeWidget.cpp deleted file mode 100644 index b93590b3f..000000000 --- a/src/tools/noisetool/ui/noisedata/NoiseDataNodeWidget.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @file - */ - -#include "NoiseDataNodeWidget.h" -#include "NoiseDataItemWidget.h" -#include "ui/turbobadger/ui_widgets.h" -#include "core/Color.h" -#include "core/StringUtil.h" - -#define NOISEDATADETAIL(text, type) \ - if (tb::TBTextField *widget = getWidgetByIDAndType(TBIDC(#type))) { \ - const NoiseData& data = item->data(); \ - const core::String& str = core::string::format(text, data.type); \ - widget->setText(str); \ - } else { \ - Log::warn("Could not get widget with id " #type); \ - } - -NoiseDataNodeWidget::NoiseDataNodeWidget(NoiseItem *item) : - Super() { - setLayoutDistribution(tb::LAYOUT_DISTRIBUTION_GRAVITY); - setLayoutDistributionPosition(tb::LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP); - setIgnoreInput(false); - - core_assert_always(tb::g_widgets_reader->loadFile(getContentRoot(), "ui/widget/noisetool-noisedata-node.tb.txt")); - setItem(item); -} - -void NoiseDataNodeWidget::setItem(NoiseItem* item) { - tb::TBTextField *name = getWidgetByIDAndType(TBIDC("name")); - if (item == nullptr) { - if (name != nullptr) { - name->setText("Empty"); - } - return; - } - if (name != nullptr) { - name->setText(item->str); - } - NOISEDATADETAIL("Frequency: %f", frequency); - NOISEDATADETAIL("Lacunarity: %f", lacunarity); - NOISEDATADETAIL("Octaves: %i", octaves); - NOISEDATADETAIL("Gain: %f", gain); -} - -#undef NOISEDATADETAIL - -bool NoiseDataNodeWidget::onEvent(const tb::TBWidgetEvent &ev) { - if (ev.type == tb::EVENT_TYPE_POINTER_DOWN) { - _clicked = true; - } else if (ev.type == tb::EVENT_TYPE_POINTER_UP) { - int x = ev.target_x; - int y = ev.target_y; - convertToRoot(x, y); - createNewNodeAtPosition(x, y); - _clicked = false; - } - return Super::onEvent(ev); -} - -void NoiseDataNodeWidget::createNewNodeAtPosition(int x, int y) { - NoiseDataNodeWidget* w = new NoiseDataNodeWidget(nullptr); - w->setRect(tb::TBRect(x, y, 20, 20)); - getParent()->addChild(w); - const tb::TBRect& rect = w->getRect(); - Log::info("x: %i, y: %i, w: %i, h: %i", rect.x, rect.y, rect.w, rect.h); -} - -void NoiseDataNodeWidget::onPaintChildren(const PaintProps &paintProps) { - Super::onPaintChildren(paintProps); - if (!_clicked) { - return; - } - // render line from clicked pos to current mouse pos - tb::TBRect local_rect = getRect(); - local_rect.x = 0; - local_rect.y = 0; - const glm::ivec4 color(core::Color::Cyan * 255.0f); - const tb::TBColor tbColor(color.r, color.g, color.b, color.a); - tb::g_tb_skin->paintRectFill(local_rect, tbColor); -} diff --git a/src/tools/noisetool/ui/noisedata/NoiseDataNodeWidget.h b/src/tools/noisetool/ui/noisedata/NoiseDataNodeWidget.h deleted file mode 100644 index afe3603fb..000000000 --- a/src/tools/noisetool/ui/noisedata/NoiseDataNodeWidget.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "ui/turbobadger/TurboBadger.h" - -class NoiseItem; - -class NoiseDataNodeWidget: public tb::TBLayout { -private: - using Super = tb::TBLayout; - - bool _clicked = false; - void createNewNodeAtPosition(int x, int y); - void setItem(NoiseItem* item); -public: - NoiseDataNodeWidget(NoiseItem *item); - - bool onEvent(const tb::TBWidgetEvent &ev) override; - void onPaintChildren(const PaintProps &paintProps) override; -}; - - diff --git a/src/tools/uitool/CMakeLists.txt b/src/tools/uitool/CMakeLists.txt deleted file mode 100644 index 1b38b247d..000000000 --- a/src/tools/uitool/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -project(uitool) -set(SRCS - UITool.h UITool.cpp -) -engine_add_build_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS} NOINSTALL) -engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES turbobadger) diff --git a/src/tools/uitool/UITool.cpp b/src/tools/uitool/UITool.cpp deleted file mode 100644 index 3989714a5..000000000 --- a/src/tools/uitool/UITool.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file - */ - -#include "UITool.h" -#include "ui/turbobadger/Window.h" -#include "ui/turbobadger/FontUtil.h" -#include "io/Filesystem.h" - -UITool::UITool(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider) : - Super(metric, filesystem, eventBus, timeProvider) { - _initialLogLevel = SDL_LOG_PRIORITY_WARN; - init(ORGANISATION, "uitool"); -} - -app::AppState UITool::onInit() { - const app::AppState state = Super::onInit(); - if (state != app::AppState::Running) { - return state; - } - - if (_argc != 2) { - _exitCode = 1; - Log::error("Usage: %s ", _argv[0]); - return app::AppState::InitFailure; - } - - if (!tb::tb_core_init(&_renderer)) { - Log::error("failed to initialize the ui"); - return app::AppState::InitFailure; - } - if (!tb::g_tb_lng->load("ui/lang/en.tb.txt")) { - Log::warn("could not load the translation"); - } - if (!tb::g_tb_skin->load("../shared/ui/skin/skin.tb.txt", nullptr)) { - Log::error("could not load the skin from shared dir"); - return app::AppState::InitFailure; - } - ui::turbobadger::initFonts(); - - _root.setRect(tb::TBRect(0, 0, 800, 600)); - _root.setSkinBg(TBIDC("background")); - - return state; -} - -app::AppState UITool::onRunning() { - ui::turbobadger::Window window((ui::turbobadger::Window*) nullptr); - _root.addChild(&window); - if (!window.loadResourceFile(_argv[1])) { - _exitCode = 1; - Log::error("Failed to parse ui file '%s'", _argv[1]); - } - _root.removeChild(&window); - - return app::AppState::Cleanup; -} - -app::AppState UITool::onCleanup() { - tb::TBAnimationManager::abortAllAnimations(); - - tb::tb_core_shutdown(); - - return Super::onCleanup(); -} - -CONSOLE_APP(UITool) diff --git a/src/tools/uitool/UITool.h b/src/tools/uitool/UITool.h deleted file mode 100644 index 6e669d46a..000000000 --- a/src/tools/uitool/UITool.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file - */ - -#pragma once - -#include "app/CommandlineApp.h" -#include "ui/turbobadger/TurboBadger.h" -#include "ui/turbobadger/UIDummies.h" - -/** - * @brief This tool validates the turbobadger ui files (*.tb.txt) - * - * @ingroup Tools - */ -class UITool: public app::CommandlineApp { -private: - using Super = app::CommandlineApp; - DummyRenderer _renderer; - tb::TBWidget _root; -public: - UITool(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider); - - app::AppState onInit() override; - app::AppState onRunning() override; - app::AppState onCleanup() override; -};