diff --git a/CMakeLists.txt b/CMakeLists.txt
index d614dcae..620112c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,6 +6,16 @@ cmake_minimum_required(VERSION 3.1)
# we require C++11 - this set appropriate flags for compilers, which may not be portable
set(CMAKE_CXX_STANDARD 14)
+option(GWK_USE_HUNTER "Use huter to build dependencies" OFF)
+if(GWK_USE_HUNTER)
+ include(${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/HunterGate.cmake)
+ HunterGate(
+ URL "https://github.com/ruslo/hunter/archive/v0.23.158.tar.gz"
+ SHA1 "4f3d8287a546091d1968ee80e02f121b312fceea"
+ )
+ cmake_policy(SET CMP0074 NEW)
+endif()
+
project(gwork)
set(GWK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/cmake/Config.cmake b/cmake/Config.cmake
index b4676df3..93318d84 100644
--- a/cmake/Config.cmake
+++ b/cmake/Config.cmake
@@ -152,18 +152,25 @@ if(RENDER_OPENGL)
if (USE_GLFW)
message(STATUS "Configuring GLFW3...")
- find_package(GLFW REQUIRED)
- if (APPLE)
- set(GLFW_DEPENDENCIES "-framework OpenGL")
- elseif(UNIX)
- set(GLFW_DEPENDENCIES "-lGL")
- elseif(WIN32)
- find_package(OpenGL)
- set(GLFW_DEPENDENCIES ${OPENGL_gl_LIBRARY})
+ if(GWK_USE_HUNTER)
+ hunter_add_package(glfw)
+ find_package(glfw3 REQUIRED)
+
+ set(GWK_RENDER_LIBRARIES glfw)
+ else()
+ find_package(GLFW REQUIRED)
+ if (APPLE)
+ set(GLFW_DEPENDENCIES "-framework OpenGL")
+ elseif(UNIX)
+ set(GLFW_DEPENDENCIES "-lGL")
+ elseif(WIN32)
+ find_package(OpenGL)
+ set(GLFW_DEPENDENCIES ${OPENGL_gl_LIBRARY})
+ endif()
+
+ set(GWK_RENDER_INCLUDES "${GLFW_INCLUDE_DIR}")
+ set(GWK_RENDER_LIBRARIES ${GLFW_LIBRARIES} ${GLFW_DEPENDENCIES})
endif()
-
- set(GWK_RENDER_INCLUDES "${GLFW_INCLUDE_DIR}")
- set(GWK_RENDER_LIBRARIES ${GLFW_LIBRARIES} ${GLFW_DEPENDENCIES})
endif()
endif(RENDER_OPENGL)
@@ -172,25 +179,42 @@ if(RENDER_OPENGL_CORE)
set(GWK_INPUT_NAME "GLFW3")
set(GWK_PLATFORM_NAME "Cross")
- find_package(glm REQUIRED)
- find_package(GLEW REQUIRED)
+ if(GWK_USE_HUNTER)
+ hunter_add_package(glm)
+ find_package(glm CONFIG REQUIRED)
- set(GWK_RENDER_INCLUDES "${GLM_INCLUDE_DIR}" "${GLEW_INCLUDE_DIR}")
- set(GWK_RENDER_LIBRARIES ${GLM_LIBRARIES} ${GLEW_LIBRARIES})
+ hunter_add_package(glew)
+ find_package(glew CONFIG REQUIRED)
+
+ set(GWK_RENDER_LIBRARIES glm glew::glew)
+ else()
+ find_package(glm REQUIRED)
+ find_package(GLEW REQUIRED)
+ set(GWK_RENDER_INCLUDES "${GLM_INCLUDE_DIR}" "${GLEW_INCLUDE_DIR}")
+ set(GWK_RENDER_LIBRARIES ${GLM_LIBRARIES} ${GLEW_LIBRARIES})
+ endif()
+
if(USE_GLFW)
- find_package(GLFW REQUIRED)
- if (APPLE)
- set(GLFW_DEPENDENCIES "-framework OpenGL")
- elseif(UNIX)
- set(GLFW_DEPENDENCIES "-lGL")
- elseif(WIN32)
- find_package(OpenGL)
- set(GLFW_DEPENDENCIES ${OPENGL_gl_LIBRARY})
+ if(GWK_USE_HUNTER)
+ hunter_add_package(glfw)
+ find_package(glfw3 REQUIRED)
+
+ set(GWK_RENDER_LIBRARIES ${GWK_RENDER_LIBRARIES} glfw)
+ else()
+ find_package(GLFW REQUIRED)
+ if (APPLE)
+ set(GLFW_DEPENDENCIES "-framework OpenGL")
+ elseif(UNIX)
+ set(GLFW_DEPENDENCIES "-lGL")
+ elseif(WIN32)
+ find_package(OpenGL)
+ set(GLFW_DEPENDENCIES ${OPENGL_gl_LIBRARY})
+ endif()
+
+ set(GWK_RENDER_INCLUDES "${GWK_RENDER_INCLUDES}" "${GLFW_INCLUDE_DIR}")
+ set(GWK_RENDER_LIBRARIES ${GWK_RENDER_LIBRARIES} ${GLFW_LIBRARIES} ${GLFW_DEPENDENCIES})
endif()
-
- set(GWK_RENDER_INCLUDES "${GWK_RENDER_INCLUDES}" "${GLFW_INCLUDE_DIR}")
- set(GWK_RENDER_LIBRARIES ${GWK_RENDER_LIBRARIES} ${GLFW_LIBRARIES} ${GLFW_DEPENDENCIES})
endif()
endif()
@@ -198,11 +222,22 @@ if(RENDER_SDL2)
set(GWK_RENDER_NAME "SDL2")
set(GWK_INPUT_NAME "SDL2")
set(GWK_PLATFORM_NAME "Cross")
- find_package(SDL2 REQUIRED)
- find_package(SDL2_ttf REQUIRED)
- find_package(SDL2_image REQUIRED)
- set(GWK_RENDER_INCLUDES ${SDL2_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR})
- set(GWK_RENDER_LIBRARIES ${SDL2_LIBRARY} ${SDL2_IMAGE_LIBRARIES} ${SDL2_TTF_LIBRARIES})
+ if(GWK_USE_HUNTER)
+ hunter_add_package(SDL2)
+ find_package(SDL2 CONFIG REQUIRED)
+ hunter_add_package(SDL_ttf)
+ find_package(SDL_ttf CONFIG REQUIRED)
+ hunter_add_package(SDL_image)
+ find_package(SDL_image CONFIG REQUIRED)
+
+ set(GWK_RENDER_LIBRARIES SDL2::SDL2 SDL_ttf::SDL_ttf SDL_image::SDL_image)
+ else()
+ find_package(SDL2 REQUIRED)
+ find_package(SDL2_ttf REQUIRED)
+ find_package(SDL2_image REQUIRED)
+ set(GWK_RENDER_INCLUDES ${SDL2_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR})
+ set(GWK_RENDER_LIBRARIES ${SDL2_LIBRARY} ${SDL2_IMAGE_LIBRARIES} ${SDL2_TTF_LIBRARIES})
+ endif()
endif(RENDER_SDL2)
if(RENDER_SFML2)
@@ -231,9 +266,15 @@ if(RENDER_SW)
set(GWK_PLATFORM_NAME "Cross")
if(SW_VIEWER)
set(GWK_INPUT_NAME "SDL2")
- find_package(SDL2 REQUIRED)
- set(GWK_RENDER_INCLUDES ${SDL2_INCLUDE_DIR})
- set(GWK_RENDER_LIBRARIES ${SDL2_LIBRARY})
+ if(GWK_USE_HUNTER)
+ hunter_add_package(SDL2)
+ find_package(SDL2 CONFIG REQUIRED)
+ set(GWK_RENDER_LIBRARIES SDL2::SDL2)
+ else()
+ find_package(SDL2 REQUIRED)
+ set(GWK_RENDER_INCLUDES ${SDL2_INCLUDE_DIR})
+ set(GWK_RENDER_LIBRARIES ${SDL2_LIBRARY})
+ endif()
else()
set(GWK_INPUT_NAME "Null")
set(GWK_RENDER_INCLUDES "")
diff --git a/cmake/Hunter/HunterGate.cmake b/cmake/Hunter/HunterGate.cmake
new file mode 100644
index 00000000..887557a5
--- /dev/null
+++ b/cmake/Hunter/HunterGate.cmake
@@ -0,0 +1,540 @@
+# Copyright (c) 2013-2018, Ruslan Baratov
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This is a gate file to Hunter package manager.
+# Include this file using `include` command and add package you need, example:
+#
+# cmake_minimum_required(VERSION 3.2)
+#
+# include("cmake/HunterGate.cmake")
+# HunterGate(
+# URL "https://github.com/path/to/hunter/archive.tar.gz"
+# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
+# )
+#
+# project(MyProject)
+#
+# hunter_add_package(Foo)
+# hunter_add_package(Boo COMPONENTS Bar Baz)
+#
+# Projects:
+# * https://github.com/hunter-packages/gate/
+# * https://github.com/ruslo/hunter
+
+option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
+
+if(HUNTER_ENABLED)
+ if(CMAKE_VERSION VERSION_LESS "3.2")
+ message(
+ FATAL_ERROR
+ "At least CMake version 3.2 required for Hunter dependency management."
+ " Update CMake or set HUNTER_ENABLED to OFF."
+ )
+ endif()
+endif()
+
+include(CMakeParseArguments) # cmake_parse_arguments
+
+option(HUNTER_STATUS_PRINT "Print working status" ON)
+option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
+option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
+
+set(HUNTER_WIKI "https://github.com/ruslo/hunter/wiki")
+
+function(hunter_gate_status_print)
+ if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
+ foreach(print_message ${ARGV})
+ message(STATUS "[hunter] ${print_message}")
+ endforeach()
+ endif()
+endfunction()
+
+function(hunter_gate_status_debug)
+ if(HUNTER_STATUS_DEBUG)
+ foreach(print_message ${ARGV})
+ string(TIMESTAMP timestamp)
+ message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}")
+ endforeach()
+ endif()
+endfunction()
+
+function(hunter_gate_wiki wiki_page)
+ message("------------------------------ WIKI -------------------------------")
+ message(" ${HUNTER_WIKI}/${wiki_page}")
+ message("-------------------------------------------------------------------")
+ message("")
+ message(FATAL_ERROR "")
+endfunction()
+
+function(hunter_gate_internal_error)
+ message("")
+ foreach(print_message ${ARGV})
+ message("[hunter ** INTERNAL **] ${print_message}")
+ endforeach()
+ message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
+ message("")
+ hunter_gate_wiki("error.internal")
+endfunction()
+
+function(hunter_gate_fatal_error)
+ cmake_parse_arguments(hunter "" "WIKI" "" "${ARGV}")
+ string(COMPARE EQUAL "${hunter_WIKI}" "" have_no_wiki)
+ if(have_no_wiki)
+ hunter_gate_internal_error("Expected wiki")
+ endif()
+ message("")
+ foreach(x ${hunter_UNPARSED_ARGUMENTS})
+ message("[hunter ** FATAL ERROR **] ${x}")
+ endforeach()
+ message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
+ message("")
+ hunter_gate_wiki("${hunter_WIKI}")
+endfunction()
+
+function(hunter_gate_user_error)
+ hunter_gate_fatal_error(${ARGV} WIKI "error.incorrect.input.data")
+endfunction()
+
+function(hunter_gate_self root version sha1 result)
+ string(COMPARE EQUAL "${root}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("root is empty")
+ endif()
+
+ string(COMPARE EQUAL "${version}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("version is empty")
+ endif()
+
+ string(COMPARE EQUAL "${sha1}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("sha1 is empty")
+ endif()
+
+ string(SUBSTRING "${sha1}" 0 7 archive_id)
+
+ if(EXISTS "${root}/cmake/Hunter")
+ set(hunter_self "${root}")
+ else()
+ set(
+ hunter_self
+ "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked"
+ )
+ endif()
+
+ set("${result}" "${hunter_self}" PARENT_SCOPE)
+endfunction()
+
+# Set HUNTER_GATE_ROOT cmake variable to suitable value.
+function(hunter_gate_detect_root)
+ # Check CMake variable
+ string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty)
+ if(not_empty)
+ set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE)
+ hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable")
+ return()
+ endif()
+
+ # Check environment variable
+ string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty)
+ if(not_empty)
+ set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE)
+ hunter_gate_status_debug("HUNTER_ROOT detected by environment variable")
+ return()
+ endif()
+
+ # Check HOME environment variable
+ string(COMPARE NOTEQUAL "$ENV{HOME}" "" result)
+ if(result)
+ set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE)
+ hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable")
+ return()
+ endif()
+
+ # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)
+ if(WIN32)
+ string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result)
+ if(result)
+ set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE)
+ hunter_gate_status_debug(
+ "HUNTER_ROOT set using SYSTEMDRIVE environment variable"
+ )
+ return()
+ endif()
+
+ string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result)
+ if(result)
+ set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE)
+ hunter_gate_status_debug(
+ "HUNTER_ROOT set using USERPROFILE environment variable"
+ )
+ return()
+ endif()
+ endif()
+
+ hunter_gate_fatal_error(
+ "Can't detect HUNTER_ROOT"
+ WIKI "error.detect.hunter.root"
+ )
+endfunction()
+
+function(hunter_gate_download dir)
+ string(
+ COMPARE
+ NOTEQUAL
+ "$ENV{HUNTER_DISABLE_AUTOINSTALL}"
+ ""
+ disable_autoinstall
+ )
+ if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)
+ hunter_gate_fatal_error(
+ "Hunter not found in '${dir}'"
+ "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'"
+ "Settings:"
+ " HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
+ " HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
+ WIKI "error.run.install"
+ )
+ endif()
+ string(COMPARE EQUAL "${dir}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("Empty 'dir' argument")
+ endif()
+
+ string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("HUNTER_GATE_SHA1 empty")
+ endif()
+
+ string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("HUNTER_GATE_URL empty")
+ endif()
+
+ set(done_location "${dir}/DONE")
+ set(sha1_location "${dir}/SHA1")
+
+ set(build_dir "${dir}/Build")
+ set(cmakelists "${dir}/CMakeLists.txt")
+
+ hunter_gate_status_debug("Locking directory: ${dir}")
+ file(LOCK "${dir}" DIRECTORY GUARD FUNCTION)
+ hunter_gate_status_debug("Lock done")
+
+ if(EXISTS "${done_location}")
+ # while waiting for lock other instance can do all the job
+ hunter_gate_status_debug("File '${done_location}' found, skip install")
+ return()
+ endif()
+
+ file(REMOVE_RECURSE "${build_dir}")
+ file(REMOVE_RECURSE "${cmakelists}")
+
+ file(MAKE_DIRECTORY "${build_dir}") # check directory permissions
+
+ # Disabling languages speeds up a little bit, reduces noise in the output
+ # and avoids path too long windows error
+ file(
+ WRITE
+ "${cmakelists}"
+ "cmake_minimum_required(VERSION 3.2)\n"
+ "project(HunterDownload LANGUAGES NONE)\n"
+ "include(ExternalProject)\n"
+ "ExternalProject_Add(\n"
+ " Hunter\n"
+ " URL\n"
+ " \"${HUNTER_GATE_URL}\"\n"
+ " URL_HASH\n"
+ " SHA1=${HUNTER_GATE_SHA1}\n"
+ " DOWNLOAD_DIR\n"
+ " \"${dir}\"\n"
+ " TLS_VERIFY\n"
+ " ${HUNTER_TLS_VERIFY}\n"
+ " SOURCE_DIR\n"
+ " \"${dir}/Unpacked\"\n"
+ " CONFIGURE_COMMAND\n"
+ " \"\"\n"
+ " BUILD_COMMAND\n"
+ " \"\"\n"
+ " INSTALL_COMMAND\n"
+ " \"\"\n"
+ ")\n"
+ )
+
+ if(HUNTER_STATUS_DEBUG)
+ set(logging_params "")
+ else()
+ set(logging_params OUTPUT_QUIET)
+ endif()
+
+ hunter_gate_status_debug("Run generate")
+
+ # Need to add toolchain file too.
+ # Otherwise on Visual Studio + MDD this will fail with error:
+ # "Could not find an appropriate version of the Windows 10 SDK installed on this machine"
+ if(EXISTS "${CMAKE_TOOLCHAIN_FILE}")
+ get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
+ set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}")
+ else()
+ # 'toolchain_arg' can't be empty
+ set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=")
+ endif()
+
+ string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make)
+ if(no_make)
+ set(make_arg "")
+ else()
+ # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM
+ set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
+ endif()
+
+ execute_process(
+ COMMAND
+ "${CMAKE_COMMAND}"
+ "-H${dir}"
+ "-B${build_dir}"
+ "-G${CMAKE_GENERATOR}"
+ "${toolchain_arg}"
+ ${make_arg}
+ WORKING_DIRECTORY "${dir}"
+ RESULT_VARIABLE download_result
+ ${logging_params}
+ )
+
+ if(NOT download_result EQUAL 0)
+ hunter_gate_internal_error(
+ "Configure project failed."
+ "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}"
+ "In directory ${dir}"
+ )
+ endif()
+
+ hunter_gate_status_print(
+ "Initializing Hunter workspace (${HUNTER_GATE_SHA1})"
+ " ${HUNTER_GATE_URL}"
+ " -> ${dir}"
+ )
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" --build "${build_dir}"
+ WORKING_DIRECTORY "${dir}"
+ RESULT_VARIABLE download_result
+ ${logging_params}
+ )
+
+ if(NOT download_result EQUAL 0)
+ hunter_gate_internal_error("Build project failed")
+ endif()
+
+ file(REMOVE_RECURSE "${build_dir}")
+ file(REMOVE_RECURSE "${cmakelists}")
+
+ file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}")
+ file(WRITE "${done_location}" "DONE")
+
+ hunter_gate_status_debug("Finished")
+endfunction()
+
+# Must be a macro so master file 'cmake/Hunter' can
+# apply all variables easily just by 'include' command
+# (otherwise PARENT_SCOPE magic needed)
+macro(HunterGate)
+ if(HUNTER_GATE_DONE)
+ # variable HUNTER_GATE_DONE set explicitly for external project
+ # (see `hunter_download`)
+ set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
+ endif()
+
+ # First HunterGate command will init Hunter, others will be ignored
+ get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)
+
+ if(NOT HUNTER_ENABLED)
+ # Empty function to avoid error "unknown function"
+ function(hunter_add_package)
+ endfunction()
+
+ set(
+ _hunter_gate_disabled_mode_dir
+ "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode"
+ )
+ if(EXISTS "${_hunter_gate_disabled_mode_dir}")
+ hunter_gate_status_debug(
+ "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}"
+ )
+ list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}")
+ endif()
+ elseif(_hunter_gate_done)
+ hunter_gate_status_debug("Secondary HunterGate (use old settings)")
+ hunter_gate_self(
+ "${HUNTER_CACHED_ROOT}"
+ "${HUNTER_VERSION}"
+ "${HUNTER_SHA1}"
+ _hunter_self
+ )
+ include("${_hunter_self}/cmake/Hunter")
+ else()
+ set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}")
+
+ string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name)
+ if(_have_project_name)
+ hunter_gate_fatal_error(
+ "Please set HunterGate *before* 'project' command. "
+ "Detected project: ${PROJECT_NAME}"
+ WIKI "error.huntergate.before.project"
+ )
+ endif()
+
+ cmake_parse_arguments(
+ HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV}
+ )
+
+ string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1)
+ string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url)
+ string(
+ COMPARE
+ NOTEQUAL
+ "${HUNTER_GATE_UNPARSED_ARGUMENTS}"
+ ""
+ _have_unparsed
+ )
+ string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global)
+ string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath)
+
+ if(_have_unparsed)
+ hunter_gate_user_error(
+ "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}"
+ )
+ endif()
+ if(_empty_sha1)
+ hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory")
+ endif()
+ if(_empty_url)
+ hunter_gate_user_error("URL suboption of HunterGate is mandatory")
+ endif()
+ if(_have_global)
+ if(HUNTER_GATE_LOCAL)
+ hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)")
+ endif()
+ if(_have_filepath)
+ hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)")
+ endif()
+ endif()
+ if(HUNTER_GATE_LOCAL)
+ if(_have_global)
+ hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)")
+ endif()
+ if(_have_filepath)
+ hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)")
+ endif()
+ endif()
+ if(_have_filepath)
+ if(_have_global)
+ hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)")
+ endif()
+ if(HUNTER_GATE_LOCAL)
+ hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)")
+ endif()
+ endif()
+
+ hunter_gate_detect_root() # set HUNTER_GATE_ROOT
+
+ # Beautify path, fix probable problems with windows path slashes
+ get_filename_component(
+ HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE
+ )
+ hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}")
+ if(NOT HUNTER_ALLOW_SPACES_IN_PATH)
+ string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces)
+ if(NOT _contain_spaces EQUAL -1)
+ hunter_gate_fatal_error(
+ "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
+ "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
+ "(Use at your own risk!)"
+ WIKI "error.spaces.in.hunter.root"
+ )
+ endif()
+ endif()
+
+ string(
+ REGEX
+ MATCH
+ "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*"
+ HUNTER_GATE_VERSION
+ "${HUNTER_GATE_URL}"
+ )
+ string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty)
+ if(_is_empty)
+ set(HUNTER_GATE_VERSION "unknown")
+ endif()
+
+ hunter_gate_self(
+ "${HUNTER_GATE_ROOT}"
+ "${HUNTER_GATE_VERSION}"
+ "${HUNTER_GATE_SHA1}"
+ _hunter_self
+ )
+
+ set(_master_location "${_hunter_self}/cmake/Hunter")
+ if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter")
+ # Hunter downloaded manually (e.g. by 'git clone')
+ set(_unused "xxxxxxxxxx")
+ set(HUNTER_GATE_SHA1 "${_unused}")
+ set(HUNTER_GATE_VERSION "${_unused}")
+ else()
+ get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE)
+ set(_done_location "${_archive_id_location}/DONE")
+ set(_sha1_location "${_archive_id_location}/SHA1")
+
+ # Check Hunter already downloaded by HunterGate
+ if(NOT EXISTS "${_done_location}")
+ hunter_gate_download("${_archive_id_location}")
+ endif()
+
+ if(NOT EXISTS "${_done_location}")
+ hunter_gate_internal_error("hunter_gate_download failed")
+ endif()
+
+ if(NOT EXISTS "${_sha1_location}")
+ hunter_gate_internal_error("${_sha1_location} not found")
+ endif()
+ file(READ "${_sha1_location}" _sha1_value)
+ string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal)
+ if(NOT _is_equal)
+ hunter_gate_internal_error(
+ "Short SHA1 collision:"
+ " ${_sha1_value} (from ${_sha1_location})"
+ " ${HUNTER_GATE_SHA1} (HunterGate)"
+ )
+ endif()
+ if(NOT EXISTS "${_master_location}")
+ hunter_gate_user_error(
+ "Master file not found:"
+ " ${_master_location}"
+ "try to update Hunter/HunterGate"
+ )
+ endif()
+ endif()
+ include("${_master_location}")
+ set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
+ endif()
+endmacro()
diff --git a/source/gwork/CMakeLists.txt b/source/gwork/CMakeLists.txt
index db563713..b34b5bb8 100644
--- a/source/gwork/CMakeLists.txt
+++ b/source/gwork/CMakeLists.txt
@@ -85,6 +85,7 @@ set(GWK_HEADERS
include/Gwork/Controls/WindowCanvas.h
include/Gwork/Controls/WindowControl.h
# layout
+ include/Gwork/Controls/Layout/Layout.h
include/Gwork/Controls/Layout/Position.h
include/Gwork/Controls/Layout/Table.h
include/Gwork/Controls/Layout/Tile.h
@@ -170,6 +171,8 @@ set(GWK_SOURCES
source/Controls/VerticalSlider.cpp
source/Controls/WindowCanvas.cpp
source/Controls/WindowControl.cpp
+ # layout
+ source/Controls/Layout/Layout.cpp
# dialog
source/Controls/Dialog/FileOpen.cpp
source/Controls/Dialog/FileSave.cpp
@@ -213,6 +216,8 @@ source_group("${GWK_SOURCE_FOLDER}\\Controls"
REGULAR_EXPRESSION "source/Controls/[^/]+\\.cpp")
source_group("${GWK_SOURCE_FOLDER}\\Controls\\Dialog"
REGULAR_EXPRESSION "source/Controls/Dialog/[^/]+\\.cpp")
+source_group("${GWK_SOURCE_FOLDER}\\Controls\\Layout"
+ REGULAR_EXPRESSION "source/Controls/Layout/[^/]+\\.cpp")
# Gwork library
add_library(Gwork STATIC ${GWK_HEADERS} ${GWK_SOURCES} ${GWK_REFLECT_SOURCES})
diff --git a/source/gwork/include/Gwork/Controls/Base.h b/source/gwork/include/Gwork/Controls/Base.h
index a8b5981d..bbb4f6d9 100644
--- a/source/gwork/include/Gwork/Controls/Base.h
+++ b/source/gwork/include/Gwork/Controls/Base.h
@@ -26,6 +26,7 @@ namespace Gwk
namespace Controls
{
class Canvas;
+ class LayoutItem;
//
//! This is the base class for all Gwork controls.
@@ -33,6 +34,7 @@ namespace Gwk
class GWK_EXPORT Base : public Event::Handler
{
public:
+ friend class LayoutItem;
typedef std::list List;
@@ -84,6 +86,8 @@ namespace Gwk
protected:
virtual void AddChild(Controls::Base* child);
+ //used to add a child that is part of a layout not directly attached to parent
+ virtual void AddChildOfLayout(Base* child);
virtual void RemoveChild(Controls::Base* parent);
virtual void OnChildAdded(Controls::Base* child);
virtual void OnChildRemoved(Controls::Base* child);
@@ -102,6 +106,12 @@ namespace Gwk
virtual void Dock(Position dock);
virtual Position GetDock() const;
+ virtual SizeFlags GetSizeFlags();
+ virtual void SetSizeFlags(SizeFlags sizeFlags);
+
+ virtual void SetLayout(LayoutItem *layoutItem);
+ virtual LayoutItem *GetLayout();
+
virtual void RestrictToParent(bool restrict) { m_bRestrictToParent = restrict; }
virtual bool ShouldRestrictToParent() { return m_bRestrictToParent; }
@@ -151,6 +161,14 @@ namespace Gwk
virtual const Gwk::Rect& GetBounds() const { return m_bounds; }
+// virtual void SetNeeded(const Gwk::Rect &needed) { m_needed=needed; }
+// virtual const Gwk::Rect &GetNeeded() const { return m_needed; }
+ virtual Size GetMinimumSize();
+ virtual Size GetMaximumSize();
+
+ virtual void SetPreferredSize(const Size &size) { m_preferredSize=size; }
+ virtual const Size GetPreferredSize() const { return m_preferredSize; }
+
virtual Controls::Base* GetControlAt(int x, int y, bool bOnlyIfMouseEnabled = true);
protected:
@@ -333,16 +351,6 @@ namespace Gwk
virtual void UpdateCursor();
- virtual Gwk::Point GetMinimumSize()
- {
- return Gwk::Point(1, 1);
- }
-
- virtual Gwk::Point GetMaximumSize()
- {
- return Gwk::Point(4096, 4096);
- }
-
virtual void SetTooltipText(const Gwk::String& strText);
virtual void SetTooltip(Base* tooltip)
@@ -409,6 +417,7 @@ namespace Gwk
Base::List Children;
protected:
+ void Init(Base* parent, const Gwk::String& Name);
//! The logical parent.
//! It's usually what you expect, the control you've parented it to.
@@ -428,10 +437,17 @@ namespace Gwk
Base* m_toolTip;
Skin::Base* m_skin;
-
+
+// Gwk::Rect m_needed;
Gwk::Rect m_bounds;
Gwk::Rect m_renderBounds;
+ SizeFlags m_sizeFlags;
+ Size m_preferredSize;
+ Size m_minimumSize;
+ Size m_maximumSize;
+
+ LayoutItem *m_layoutItem;
Padding m_padding;
Margin m_margin;
@@ -465,9 +481,19 @@ namespace Gwk
}
void InvalidateChildren(bool bRecursive = false);
+
+ virtual bool HasAlignment() { return false; }
void SetPosition(Position pos, int xpadding = 0, int ypadding = 0);
+ virtual void CalculateSize(Skin::Base *skin, Dim dim);
+ virtual void Arrange(Skin::Base *skin, Dim dim);
+
protected:
+ bool ProcessLayout(Skin::Base *skin, Dim dim);
+ Size SizeOfChildren(Skin::Base *skin, Dim dim);
+
+ void ArrangeHorizontal(Skin::Base *skin);
+ void ArrangeVertical(Skin::Base *skin);
virtual void RecurseLayout(Skin::Base* skin);
virtual void Layout(Skin::Base* skin);
@@ -657,7 +683,7 @@ namespace Gwk
typedef BASENAME ParentClass; \
typedef THISNAME ThisClass;
-// To be placed in the controls .h definition.
+ // To be placed in the controls .h definition.
#define GWK_CONTROL(THISNAME, BASENAME) \
public: \
GWK_CLASS(THISNAME, BASENAME) \
diff --git a/source/gwork/include/Gwork/Controls/CollapsibleCategory.h b/source/gwork/include/Gwork/Controls/CollapsibleCategory.h
index 70b0654b..4a8a9beb 100644
--- a/source/gwork/include/Gwork/Controls/CollapsibleCategory.h
+++ b/source/gwork/include/Gwork/Controls/CollapsibleCategory.h
@@ -55,6 +55,7 @@ namespace Gwk
Gwk::Event::Listener onSelection;
protected:
+ void CalculateSize(Skin::Base *skin, Dim dim) override;
virtual void OnSelection(Event::Info info);
diff --git a/source/gwork/include/Gwork/Controls/Label.h b/source/gwork/include/Gwork/Controls/Label.h
index 11dddd0b..1efd7e11 100644
--- a/source/gwork/include/Gwork/Controls/Label.h
+++ b/source/gwork/include/Gwork/Controls/Label.h
@@ -40,6 +40,7 @@ namespace Gwk
virtual void SizeToContents();
+ bool HasAlignment() override { return true; }
virtual void SetAlignment(Position area);
virtual Position GetAlignment();
diff --git a/source/gwork/include/Gwork/Controls/Layout/Layout.h b/source/gwork/include/Gwork/Controls/Layout/Layout.h
new file mode 100644
index 00000000..24d5d2a1
--- /dev/null
+++ b/source/gwork/include/Gwork/Controls/Layout/Layout.h
@@ -0,0 +1,118 @@
+#pragma once
+#ifndef GWK_CONTROLS_LAYOUT_BASE_H
+#define GWK_CONTROLS_LAYOUT_BASE_H
+
+#include
+#include
+
+namespace Gwk
+{
+namespace Controls
+{
+
+enum LayoutType
+{
+ Horizontal,
+ Vertical
+};
+
+class LayoutItem
+{
+public:
+ LayoutItem();
+ LayoutItem(LayoutType type);
+ ~LayoutItem();
+
+ void SetParent(Base *parent);
+ Base *GetParent() { return m_parent; }
+ void SetLayoutParent(LayoutItem *layoutParent);
+ LayoutItem *GetLayoutParent() { return m_layoutParent; }
+
+ void AddControl(Base *control);
+ void RemoveControl(Base *control);
+ LayoutItem *FindControl(Base *control);
+
+ void AddLayout(LayoutItem *item);
+ void RemoveLayout(LayoutItem *item);
+
+ Base *GetControl();
+ void SetControl(Base *control);
+ void UpdateControl();
+
+ void CalculateSize(Skin::Base *skin, Dim dim);
+ void Arrange(Skin::Base *skin, Dim dim);
+
+ const Gwk::Rect &GetBounds() const { return m_bounds; }
+ const void SetBounds(Gwk::Rect &bounds) { m_bounds=bounds; }
+
+ void SetPosition(int x, int y) { m_bounds.x=x; m_bounds.y=y; }
+
+ const SizeFlags &GetSizeFlags() const { return m_sizeFlags; }
+ const Size &GetMinimumSize() const { return m_minimumSize; }
+ const Size &GetMaximumSize() const { return m_maximumSize; }
+ const Size &GetPreferredSize() const { return m_preferredSize; }
+
+private:
+ void CalculateHorizontal();
+ void CalculateMaxHeight();
+ void CalculateVertical();
+ void CalculateMaxWidth();
+
+ void UpdateControlSize(Dim dim);
+
+ void ArrangeHorizontal();
+ void ArrangeVerticalPos();
+ void ArrangeVertical();
+ void ArrangeHorizontalPos();
+
+ Base *m_parent;
+ LayoutItem *m_layoutParent;
+ std::vector m_children;
+ Base *m_control;
+
+ LayoutType m_type;
+ Gwk::Rect m_bounds;
+ Margin m_margin;
+ Margin m_padding;
+ int m_spacing;
+
+ SizeFlags m_sizeFlags;
+ Size m_minimumSize;
+ Size m_maximumSize;
+ Size m_preferredSize;
+};
+
+//class LayoutBase:public LayoutItem
+//{
+//public:
+// LayoutBase(Layout type);
+//
+// int GetSpacing();
+// void SetSpacing(int value);
+//
+// int GetMargin();
+// void SetMargin(int value);
+// void SetMargins(int left, int top, int right, int bottom);
+//
+// Size GetMinimumSize();
+// Size GetMaximumSize();
+//
+// void DoThink();
+//};
+
+class HorizontalLayout:public LayoutItem
+{
+public:
+ HorizontalLayout():LayoutItem(LayoutType::Horizontal) {}
+};
+
+class VerticalLayout:public LayoutItem
+{
+public:
+ VerticalLayout():LayoutItem(LayoutType::Vertical) {}
+};
+
+}//namespace Controls
+}//namespace Gwk
+
+#endif//GWK_CONTROLS_LAYOUT_BASE_H
diff --git a/source/gwork/include/Gwork/Controls/Property/ColorSelector.h b/source/gwork/include/Gwork/Controls/Property/ColorSelector.h
index 2e970ed5..d78c37de 100644
--- a/source/gwork/include/Gwork/Controls/Property/ColorSelector.h
+++ b/source/gwork/include/Gwork/Controls/Property/ColorSelector.h
@@ -55,6 +55,7 @@ namespace Gwk
m_button = new Controls::Internal::ColorButton(m_textBox);
m_button->Dock(Position::Right);
m_button->SetWidth(20);
+ m_button->SetSizeFlags({SizeFlag::Fixed, SizeFlag::Elastic});
m_button->onPress.Add(this, &ThisClass::OnButtonPress);
m_button->SetMargin(Margin(1, 1, 1, 2));
}
diff --git a/source/gwork/include/Gwork/Controls/RadioButton.h b/source/gwork/include/Gwork/Controls/RadioButton.h
index fc99f0f7..c3284e10 100644
--- a/source/gwork/include/Gwork/Controls/RadioButton.h
+++ b/source/gwork/include/Gwork/Controls/RadioButton.h
@@ -43,7 +43,7 @@ namespace Gwk
GWK_CONTROL_INLINE(LabeledRadioButton, Base)
{
- SetSize(200, 19);
+// SetSize(200, 19);
m_radioButton = new RadioButton(this);
m_radioButton->Dock(Position::Left);
m_radioButton->SetMargin(Margin(0, 2, 2, 2));
@@ -79,6 +79,16 @@ namespace Gwk
return m_label;
}
+ virtual void SetText(const String& str, bool bDoEvents=true)
+ {
+ m_label->SetText(str, bDoEvents);
+ }
+
+ virtual const String& GetText() const
+ {
+ return m_label->GetText();
+ }
+
bool OnKeySpace(bool bDown) override
{
if (bDown)
diff --git a/source/gwork/include/Gwork/Controls/ResizableControl.h b/source/gwork/include/Gwork/Controls/ResizableControl.h
index 64bff60f..cd33cced 100644
--- a/source/gwork/include/Gwork/Controls/ResizableControl.h
+++ b/source/gwork/include/Gwork/Controls/ResizableControl.h
@@ -39,12 +39,12 @@ namespace Gwk
return m_bClampMovement;
}
- virtual void SetMinimumSize(const Gwk::Point& minSize)
+ virtual void SetMinimumSize(const Size &minSize)
{
m_minimumSize = minSize;
}
- Gwk::Point GetMinimumSize() override
+ Size GetMinimumSize() override
{
return m_minimumSize;
}
@@ -68,7 +68,7 @@ namespace Gwk
void OnResizedInternal(Event::Info);
- Gwk::Point m_minimumSize;
+ Size m_minimumSize;
bool m_bClampMovement;
bool m_bResizable;
diff --git a/source/gwork/include/Gwork/Controls/Text.h b/source/gwork/include/Gwork/Controls/Text.h
index 4e7249b8..e1199545 100644
--- a/source/gwork/include/Gwork/Controls/Text.h
+++ b/source/gwork/include/Gwork/Controls/Text.h
@@ -30,7 +30,7 @@ namespace Gwk
void Render(Skin::Base* skin) override;
void Layout(Skin::Base* skin) override;
- void RefreshSize();
+ Size RefreshSize(bool update=true);
void SetFont(const Gwk::Font& font);
@@ -93,13 +93,16 @@ namespace Gwk
virtual int GetCharPosOnLine(int i);
virtual unsigned NumLines();
+ void CalculateSize(Skin::Base *skin, Dim dim) override;
+ void Arrange(Skin::Base *skin, Dim dim) override;
+
protected:
virtual void SplitWords(const Gwk::String& s, std::vector& elems);
private:
- virtual void RefreshSizeWrap();
+ virtual Size RefreshSizeWrap(bool update=true);
Gwk::String m_string;
const Gwk::Font* m_font;
diff --git a/source/gwork/include/Gwork/UiTypes.h b/source/gwork/include/Gwork/UiTypes.h
index 8b505ff8..489904e2 100644
--- a/source/gwork/include/Gwork/UiTypes.h
+++ b/source/gwork/include/Gwork/UiTypes.h
@@ -15,6 +15,8 @@
#endif
#include
+#include
+#include
namespace Gwk
{
@@ -88,6 +90,7 @@ namespace Gwk
CenterV = (1<<5),
CenterH = (1<<6),
Fill = (1<<7),
+ Fixed = (1<<8),
Center = CenterV | CenterH
};
@@ -101,6 +104,86 @@ namespace Gwk
return (static_cast(a) & static_cast(b)) != 0;
}
+ enum class Dim
+ {
+ X,
+ Y
+ };
+
+ enum class SizeFlag
+ {
+ Fixed=0, //Will not change size
+ Expand=1, //Will only expand
+ Shrink=2, //Will only shrink
+ Elastic=3, //Expands or shrinks as needed
+ Bloat=4 //Will take all available space
+ };
+
+ inline std::string SizeFlagName(SizeFlag sizeFlag)
+ {
+ switch(sizeFlag)
+ {
+ case SizeFlag::Fixed:
+ return "Fixed";
+ break;
+ case SizeFlag::Expand:
+ return "Expand";
+ break;
+ case SizeFlag::Shrink:
+ return "Shrink";
+ break;
+ case SizeFlag::Elastic:
+ return "Elastic";
+ break;
+ case SizeFlag::Bloat:
+ return "Bloat";
+ break;
+ default:
+ return "Unknown";
+ break;
+ }
+ return "Unknown";
+ }
+
+ class SizeFlags
+ {
+ public:
+ SizeFlags():horizontal(SizeFlag::Expand), vertical(SizeFlag::Expand) {}
+ SizeFlags(SizeFlag horizontal, SizeFlag vertical):horizontal(horizontal), vertical(vertical) {}
+
+ union
+ {
+ struct{
+ SizeFlag horizontal;
+ SizeFlag vertical;
+ };
+ SizeFlag value[2];
+ };
+
+ };
+
+ inline SizeFlag Merge(SizeFlag flag1, SizeFlag flag2)
+ {
+ typedef std::underlying_type::type Type;
+ Type _flag1=static_cast(flag1);
+ Type _flag2=static_cast(flag2);
+
+ if(_flag1+_flag2==3)
+ return SizeFlag::Elastic;
+ else
+ return static_cast(std::max(_flag1, _flag2));
+ }
+
+
+
+ inline int AddWithOverflow(int value1, int value2)
+ {
+ if((value2>0) && (value1>std::numeric_limits::max()-value2))
+ return std::numeric_limits::max();
+ if((value2<0) && (value1::min()-value2))
+ return 0;
+ return value1+value2;
+ }
} // namespace Gwk
#endif // ifndef GWK_UITYPES_H
diff --git a/source/gwork/source/Controls/Base.cpp b/source/gwork/source/Controls/Base.cpp
index c40161c0..f03f4a7e 100644
--- a/source/gwork/source/Controls/Base.cpp
+++ b/source/gwork/source/Controls/Base.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -22,6 +23,9 @@
# include
#endif
+#include
+#include
+
namespace Gwk {
@@ -29,6 +33,15 @@ namespace Controls {
Base::Base(Base* parent, const Gwk::String& Name)
{
+ Init(parent, Name);
+}
+
+void Base::Init(Base* parent, const Gwk::String& Name)
+{
+ m_sizeFlags={SizeFlag::Expand, SizeFlag::Expand};
+ m_minimumSize={0, 0};
+ m_maximumSize={std::numeric_limits::max(), std::numeric_limits::max()};
+ m_layoutItem=nullptr;
m_parent = nullptr;
m_actualParent = nullptr;
m_innerPanel = nullptr;
@@ -157,6 +170,48 @@ Position Base::GetDock() const
return m_dock;
}
+SizeFlags Base::GetSizeFlags()
+{
+ return m_sizeFlags;
+}
+
+void Base::SetSizeFlags(SizeFlags sizeFlags)
+{
+ if((m_sizeFlags.horizontal==sizeFlags.horizontal)&&
+ (m_sizeFlags.vertical==sizeFlags.vertical))
+ return;
+
+ m_sizeFlags=sizeFlags;
+ Invalidate();
+ InvalidateParent();
+}
+
+void Base::SetLayout(LayoutItem *layoutItem)
+{
+ if(m_innerPanel)
+ {
+ m_innerPanel->SetLayout(layoutItem);
+ return;
+ }
+
+ //library owns items so we need to delete
+ if(m_layoutItem!=nullptr)
+ delete m_layoutItem;
+
+ m_layoutItem=layoutItem;
+ m_layoutItem->SetParent(this);
+
+ for(Base *child:Children)
+ m_layoutItem->AddControl(child);
+}
+
+LayoutItem *Base::GetLayout()
+{
+ if(m_innerPanel)
+ return m_innerPanel->GetLayout();
+ return m_layoutItem;
+}
+
bool Base::Hidden() const
{
return m_bHidden;
@@ -219,6 +274,9 @@ void Base::SetPosition(Position pos, int xpadding, int ypadding)
if (pos & Position::CenterV)
y = bounds.y + (bounds.h - Height())/2 + ypadding;
+
+ x=std::max(0, x);
+ y=std::max(0, y);
SetPos(x, y);
}
@@ -302,19 +360,34 @@ void Base::AddChild(Base* child)
}
Children.push_back(child);
+
+ if(m_layoutItem)
+ m_layoutItem->AddControl(child);
+
OnChildAdded(child);
child->m_actualParent = this;
}
+void Base::AddChildOfLayout(Base* child)
+{
+ Children.push_back(child);
+
+ OnChildAdded(child);
+ child->m_actualParent=this;
+}
+
void Base::RemoveChild(Base* child)
{
// If we removed our innerpanel, remove our pointer to it
if (m_innerPanel == child)
m_innerPanel = nullptr;
- if (m_innerPanel)
+ if(m_innerPanel)
m_innerPanel->RemoveChild(child);
+ if(m_layoutItem)
+ m_layoutItem->RemoveControl(child);
+
Children.remove(child);
OnChildRemoved(child);
}
@@ -427,7 +500,41 @@ bool Base::SetBounds(const Gwk::Rect& bounds)
bool Base::SetBounds(int x, int y, int w, int h)
{
- return SetBounds(Rect(x,y,w,h));
+ return SetBounds(Rect(x, y, w, h));
+}
+
+Size Base::GetMinimumSize()
+{
+ Size minimumSize=m_minimumSize;
+
+ if(m_sizeFlags.horizontal==SizeFlag::Fixed)
+ minimumSize.width=m_bounds.w;
+ else if((m_sizeFlags.horizontal == SizeFlag::Expand) || (m_sizeFlags.horizontal == SizeFlag::Bloat))
+ minimumSize.width=std::max(minimumSize.width, m_preferredSize.width);
+
+ if(m_sizeFlags.vertical==SizeFlag::Fixed)
+ minimumSize.height=m_bounds.h;
+ else if((m_sizeFlags.vertical==SizeFlag::Expand)||(m_sizeFlags.vertical==SizeFlag::Bloat))
+ minimumSize.height=std::max(minimumSize.height, m_preferredSize.height);
+
+ return minimumSize;
+}
+
+Size Base::GetMaximumSize()
+{
+ Size maximumSize=m_maximumSize;
+
+ if(m_sizeFlags.horizontal==SizeFlag::Fixed)
+ maximumSize.width=m_bounds.w;
+ else if(m_sizeFlags.horizontal == SizeFlag::Shrink)
+ maximumSize.width=std::max(maximumSize.width, m_preferredSize.width);
+
+ if(m_sizeFlags.vertical==SizeFlag::Fixed)
+ maximumSize.height=m_bounds.h;
+ else if(m_sizeFlags.horizontal == SizeFlag::Shrink)
+ maximumSize.height=std::max(maximumSize.height, m_preferredSize.height);
+
+ return maximumSize;
}
void Base::OnBoundsChanged(Gwk::Rect oldBounds)
@@ -752,107 +859,442 @@ void Base::Layout(Skin::Base* skin)
skin->GetRender()->GetCTT()->CreateControlCacheTexture(this, this->GetBounds().GetSize());
}
-void Base::RecurseLayout(Skin::Base* skin)
+std::string info(Base *control, Dim dim)
{
- if (m_skin)
- skin = m_skin;
+ const Rect &bounds=control->GetBounds();
+ const Size &preferredSize=control->GetPreferredSize();
+ const Margin &margin=control->GetMargin();
+ const Padding &padding=control->GetPadding();
+ const SizeFlags &sizeFlags=control->GetSizeFlags();
- if (Hidden())
+ std::ostringstream output;
+
+ output<GetTypeName()<<" : "<GetName()<<"\n";
+ if(dim==Dim::X)
+ output<<" X dim\n";
+ else
+ output<<" Y dim\n";
+
+ output<<" Bounds: "<CalculateSize(skin, dim);
+
+ const Size preferredSize=m_layoutItem->GetPreferredSize();
+
+ if(dim==Dim::X)
+ {
+ m_preferredSize.width=preferredSize.width+m_padding.left+m_padding.right;
+ }
+ else
+ {
+ m_preferredSize.height=preferredSize.height+m_padding.top+m_padding.bottom;
+ }
+ return true;
+ }
+ return false;
+}
+
+Size Base::SizeOfChildren(Skin::Base *skin, Dim dim)
+{
+ Size preferredSize=m_preferredSize;
+
+ if(dim==Dim::X)
+ {
+ int width=0;
+ int dockWidth=0;
+ int innerWidth=0;
+
+ for(auto&& child:Children)
+ {
+ if(child->Hidden())
+ continue;
+
+ child->CalculateSize(skin, dim);
+
+ Position dock=child->GetDock();
+ const Size &preferred=child->GetPreferredSize();
+ const Margin &margin=child->GetMargin();
+ int childWidth=preferred.width+margin.left+margin.right;
+
+ if((dock & Position::Top)||(dock & Position::Bottom))
+ {
+ if(childWidth>width)
+ width=childWidth;
+ }
+ else if((dock & Position::Left)||(dock & Position::Right))
+ {
+ dockWidth+=childWidth;
+ }
+ else if(dock & Position::Fill)
+ {
+ innerWidth+=childWidth;
+ }
+ else
+ {
+ const Rect &bounds=child->GetBounds();
+
+ if(!HasAlignment())
+ childWidth+=bounds.x;
+
+ if(childWidth>width)
+ width=childWidth;
+ }
+ }
+ dockWidth+=innerWidth;
+ width=std::max(width, dockWidth);
+ width+=m_padding.left+m_padding.right;
+
+ switch(m_sizeFlags.horizontal)
+ {
+ case SizeFlag::Fixed:
+ preferredSize.width=m_bounds.w;
+ break;
+ case SizeFlag::Shrink:
+ preferredSize.width=std::min(width, m_maximumSize.width);
+ break;
+ case SizeFlag::Expand:
+ preferredSize.width=std::max(width, m_minimumSize.width);
+ break;
+ case SizeFlag::Elastic:
+ preferredSize.width=width;
+ break;
+ case SizeFlag::Bloat:
+ preferredSize.width=std::max(width, m_minimumSize.width);
+ break;
+ }
+ }
+ else
+ {
+ int height=0;
+ int dockHeight=0;
+ int innerHeight=0;
+
+ for(auto&& child:Children)
+ {
+ Label *label=dynamic_cast