From 26abbee1b6dc29b218a8fd8608b37c11b7eae993 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 25 May 2019 03:47:52 +0700 Subject: [PATCH] add flat.hpp module --- .gitmodules | 3 + headers/3rdparty/flat.hpp/flat_map.hpp | 523 ++++++++++++++++++++ headers/3rdparty/flat.hpp/flat_multimap.hpp | 519 +++++++++++++++++++ headers/3rdparty/flat.hpp/flat_multiset.hpp | 421 ++++++++++++++++ headers/3rdparty/flat.hpp/flat_set.hpp | 425 ++++++++++++++++ headers/enduro2d/base/types.hpp | 17 + modules/flat.hpp | 1 + modules/glfw | 2 +- modules/imgui | 2 +- scripts/update_modules.sh | 3 + sources/3rdparty/imgui/imgui.cpp | 80 ++- sources/3rdparty/imgui/imgui.h | 1 + sources/3rdparty/imgui/imgui_demo.cpp | 1 + sources/3rdparty/imgui/imgui_internal.h | 7 +- sources/3rdparty/imgui/imgui_widgets.cpp | 134 +++-- 15 files changed, 2064 insertions(+), 75 deletions(-) create mode 100644 headers/3rdparty/flat.hpp/flat_map.hpp create mode 100644 headers/3rdparty/flat.hpp/flat_multimap.hpp create mode 100644 headers/3rdparty/flat.hpp/flat_multiset.hpp create mode 100644 headers/3rdparty/flat.hpp/flat_set.hpp create mode 160000 modules/flat.hpp diff --git a/.gitmodules b/.gitmodules index 9d4c0fa7..ae3fc556 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "modules/miniz"] path = modules/miniz url = https://github.com/richgel999/miniz +[submodule "modules/flat.hpp"] + path = modules/flat.hpp + url = https://github.com/BlackMATov/flat.hpp diff --git a/headers/3rdparty/flat.hpp/flat_map.hpp b/headers/3rdparty/flat.hpp/flat_map.hpp new file mode 100644 index 00000000..54baa505 --- /dev/null +++ b/headers/3rdparty/flat.hpp/flat_map.hpp @@ -0,0 +1,523 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace flat_hpp +{ + namespace detail + { + template < typename Value, typename Compare > + class flat_map_compare : public Compare { + public: + flat_map_compare() = default; + + flat_map_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Value::first_type& l, + const typename Value::first_type& r) const + { + return Compare::operator()(l, r); + } + + bool operator()( + const typename Value::first_type& l, + const Value& r) const + { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Value& l, + const typename Value::first_type& r) const + { + return Compare::operator()(l.first, r); + } + + bool operator()(const Value& l, const Value& r) const { + return Compare::operator()(l.first, r.first); + } + }; + } + + template < typename Key + , typename Value + , typename Compare = std::less + , typename Container = std::vector> > + class flat_map + : private detail::flat_map_compare< + typename Container::value_type, + Compare> + { + using base_type = detail::flat_map_compare< + typename Container::value_type, + Compare>; + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + class value_compare : private key_compare { + public: + bool operator()(const value_type& l, const value_type& r) const { + return key_compare::operator()(l.first, r.first); + } + protected: + friend class flat_map; + explicit value_compare(key_compare compare) + : key_compare(std::move(compare)) {} + }; + public: + flat_map() + noexcept(std::is_nothrow_default_constructible_v + && std::is_nothrow_default_constructible_v) {} + + explicit flat_map(const Compare& c) + : base_type(c) {} + + template < typename Allocator > + explicit flat_map(const Allocator& a) + : data_(a) {} + + template < typename Allocator > + flat_map(const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) {} + + template < typename InputIter > + flat_map(InputIter first, InputIter last) { + insert(first, last); + } + + template < typename InputIter > + flat_map(InputIter first, InputIter last, const Compare& c) + : base_type(c) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_map(InputIter first, InputIter last, const Allocator& a) + : data_(a) { + insert(first, last); + } + + template < typename InputIter , typename Allocator > + flat_map(InputIter first, InputIter last, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(first, last); + } + + flat_map(std::initializer_list ilist) { + insert(ilist); + } + + flat_map(std::initializer_list ilist, const Compare& c) + : base_type(c) { + insert(ilist); + } + + template < typename Allocator > + flat_map(std::initializer_list ilist, const Allocator& a) + : data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_map(std::initializer_list ilist, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_map(flat_map&& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(std::move(other.data_), a) {} + + template < typename Allocator > + flat_map(const flat_map& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(other.data_, a) {} + + flat_map(flat_map&& other) = default; + flat_map(const flat_map& other) = default; + + flat_map& operator=(flat_map&& other) = default; + flat_map& operator=(const flat_map& other) = default; + + flat_map& operator=(std::initializer_list ilist) { + flat_map(ilist).swap(*this); + return *this; + } + + iterator begin() + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return data_.cbegin(); + } + + iterator end() + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return data_.cend(); + } + + reverse_iterator rbegin() + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return data_.crbegin(); + } + + reverse_iterator rend() + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return data_.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return data_.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return data_.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return data_.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type()).first->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type()).first->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + if ( iter != end() ) { + return iter->second; + } + throw std::out_of_range("flat_map::at: key not found"); + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + if ( iter != end() ) { + return iter->second; + } + throw std::out_of_range("flat_map::at: key not found"); + } + + std::pair insert(value_type&& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || this->operator()(value, *iter) + ? std::make_pair(data_.insert(iter, std::move(value)), true) + : std::make_pair(iter, false); + } + + std::pair insert(const value_type& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || this->operator()(value, *iter) + ? std::make_pair(data_.insert(iter, value), true) + : std::make_pair(iter, false); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || this->operator()(*(hint - 1), value)) + && (hint == end() || this->operator()(value, *hint)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)).first; + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || this->operator()(*(hint - 1), value)) + && (hint == end() || this->operator()(value, *hint)) + ? data_.insert(hint, value) + : insert(value).first; + } + + template < typename InputIter > + void insert(InputIter first, InputIter last) { + while ( first != last ) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template < typename... Args > + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template < typename... Args > + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + void clear() + noexcept(noexcept(std::declval().clear())) { + data_.clear(); + } + + iterator erase(const_iterator iter) { + return data_.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return data_.erase(first, last); + } + + size_type erase(const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; + } + + void swap(flat_map& other) + noexcept(std::is_nothrow_swappable_v + && std::is_nothrow_swappable_v) + { + using std::swap; + swap( + static_cast(*this), + static_cast(other)); + swap(data_, other.data_); + } + + size_type count(const key_type& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, iter->first) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, iter->first) + ? iter + : end(); + } + + std::pair equal_range(const key_type& key) { + const base_type& comp = *this; + return std::equal_range(begin(), end(), key, comp); + } + + std::pair equal_range(const key_type& key) const { + const base_type& comp = *this; + return std::equal_range(begin(), end(), key, comp); + } + + iterator lower_bound(const key_type& key) { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + + const_iterator lower_bound(const key_type& key) const { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + + iterator upper_bound(const key_type& key) { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + + const_iterator upper_bound(const key_type& key) const { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + + key_compare key_comp() const { + return *this; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + private: + container_type data_; + }; +} + +namespace flat_hpp +{ + template < typename Key + , typename Value + , typename Compare + , typename Container > + void swap( + flat_map& l, + flat_map& r) + noexcept(noexcept(l.swap(r))) + { + l.swap(r); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator==( + const flat_map& l, + const flat_map& r) + { + return l.size() == r.size() + && std::equal(l.begin(), l.end(), r.begin()); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator!=( + const flat_map& l, + const flat_map& r) + { + return !(l == r); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator<( + const flat_map& l, + const flat_map& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator>( + const flat_map& l, + const flat_map& r) + { + return r < l; + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator<=( + const flat_map& l, + const flat_map& r) + { + return !(r < l); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator>=( + const flat_map& l, + const flat_map& r) + { + return !(l < r); + } +} diff --git a/headers/3rdparty/flat.hpp/flat_multimap.hpp b/headers/3rdparty/flat.hpp/flat_multimap.hpp new file mode 100644 index 00000000..c20d395a --- /dev/null +++ b/headers/3rdparty/flat.hpp/flat_multimap.hpp @@ -0,0 +1,519 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace flat_hpp +{ + namespace detail + { + template < typename Value, typename Compare > + class flat_multimap_compare : public Compare { + public: + flat_multimap_compare() = default; + + flat_multimap_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Value::first_type& l, + const typename Value::first_type& r) const + { + return Compare::operator()(l, r); + } + + bool operator()( + const typename Value::first_type& l, + const Value& r) const + { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Value& l, + const typename Value::first_type& r) const + { + return Compare::operator()(l.first, r); + } + + bool operator()(const Value& l, const Value& r) const { + return Compare::operator()(l.first, r.first); + } + }; + } + + template < typename Key + , typename Value + , typename Compare = std::less + , typename Container = std::vector> > + class flat_multimap + : private detail::flat_multimap_compare< + typename Container::value_type, + Compare> + { + using base_type = detail::flat_multimap_compare< + typename Container::value_type, + Compare>; + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + class value_compare : private key_compare { + public: + bool operator()(const value_type& l, const value_type& r) const { + return key_compare::operator()(l.first, r.first); + } + protected: + friend class flat_multimap; + explicit value_compare(key_compare compare) + : key_compare(std::move(compare)) {} + }; + public: + flat_multimap() + noexcept(std::is_nothrow_default_constructible_v + && std::is_nothrow_default_constructible_v) {} + + explicit flat_multimap(const Compare& c) + : base_type(c) {} + + template < typename Allocator > + explicit flat_multimap(const Allocator& a) + : data_(a) {} + + template < typename Allocator > + flat_multimap(const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) {} + + template < typename InputIter > + flat_multimap(InputIter first, InputIter last) { + insert(first, last); + } + + template < typename InputIter > + flat_multimap(InputIter first, InputIter last, const Compare& c) + : base_type(c) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_multimap(InputIter first, InputIter last, const Allocator& a) + : data_(a) { + insert(first, last); + } + + template < typename InputIter , typename Allocator > + flat_multimap(InputIter first, InputIter last, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(first, last); + } + + flat_multimap(std::initializer_list ilist) { + insert(ilist); + } + + flat_multimap(std::initializer_list ilist, const Compare& c) + : base_type(c) { + insert(ilist); + } + + template < typename Allocator > + flat_multimap(std::initializer_list ilist, const Allocator& a) + : data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_multimap(std::initializer_list ilist, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_multimap(flat_multimap&& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(std::move(other.data_), a) {} + + template < typename Allocator > + flat_multimap(const flat_multimap& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(other.data_, a) {} + + flat_multimap(flat_multimap&& other) = default; + flat_multimap(const flat_multimap& other) = default; + + flat_multimap& operator=(flat_multimap&& other) = default; + flat_multimap& operator=(const flat_multimap& other) = default; + + flat_multimap& operator=(std::initializer_list ilist) { + flat_multimap(ilist).swap(*this); + return *this; + } + + iterator begin() + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return data_.cbegin(); + } + + iterator end() + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return data_.cend(); + } + + reverse_iterator rbegin() + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return data_.crbegin(); + } + + reverse_iterator rend() + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return data_.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return data_.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return data_.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return data_.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type())->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type())->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + if ( iter != end() ) { + return iter->second; + } + throw std::out_of_range("flat_multimap::at: key not found"); + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + if ( iter != end() ) { + return iter->second; + } + throw std::out_of_range("flat_multimap::at: key not found"); + } + + iterator insert(value_type&& value) { + const iterator iter = upper_bound(value.first); + return data_.insert(iter, std::move(value)); + } + + iterator insert(const value_type& value) { + const iterator iter = upper_bound(value.first); + return data_.insert(iter, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || !this->operator()(value, *(hint - 1))) + && (hint == end() || !this->operator()(*hint, value)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || !this->operator()(value, *(hint - 1))) + && (hint == end() || !this->operator()(*hint, value)) + ? data_.insert(hint, value) + : insert(value); + } + + template < typename InputIter > + void insert(InputIter first, InputIter last) { + while ( first != last ) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template < typename... Args > + iterator emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template < typename... Args > + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + void clear() + noexcept(noexcept(std::declval().clear())) { + data_.clear(); + } + + iterator erase(const_iterator iter) { + return data_.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return data_.erase(first, last); + } + + size_type erase(const key_type& key) { + const auto p = equal_range(key); + size_type r = std::distance(p.first, p.second); + erase(p.first, p.second); + return r; + } + + void swap(flat_multimap& other) + noexcept(std::is_nothrow_swappable_v + && std::is_nothrow_swappable_v) + { + using std::swap; + swap( + static_cast(*this), + static_cast(other)); + swap(data_, other.data_); + } + + size_type count(const key_type& key) const { + const auto p = equal_range(key); + return std::distance(p.first, p.second); + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, iter->first) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, iter->first) + ? iter + : end(); + } + + std::pair equal_range(const key_type& key) { + const base_type& comp = *this; + return std::equal_range(begin(), end(), key, comp); + } + + std::pair equal_range(const key_type& key) const { + const base_type& comp = *this; + return std::equal_range(begin(), end(), key, comp); + } + + iterator lower_bound(const key_type& key) { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + + const_iterator lower_bound(const key_type& key) const { + const base_type& comp = *this; + return std::lower_bound(begin(), end(), key, comp); + } + + iterator upper_bound(const key_type& key) { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + + const_iterator upper_bound(const key_type& key) const { + const base_type& comp = *this; + return std::upper_bound(begin(), end(), key, comp); + } + + key_compare key_comp() const { + return *this; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + private: + container_type data_; + }; +} + +namespace flat_hpp +{ + template < typename Key + , typename Value + , typename Compare + , typename Container > + void swap( + flat_multimap& l, + flat_multimap& r) + noexcept(noexcept(l.swap(r))) + { + l.swap(r); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator==( + const flat_multimap& l, + const flat_multimap& r) + { + return l.size() == r.size() + && std::equal(l.begin(), l.end(), r.begin()); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator!=( + const flat_multimap& l, + const flat_multimap& r) + { + return !(l == r); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator<( + const flat_multimap& l, + const flat_multimap& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator>( + const flat_multimap& l, + const flat_multimap& r) + { + return r < l; + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator<=( + const flat_multimap& l, + const flat_multimap& r) + { + return !(r < l); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator>=( + const flat_multimap& l, + const flat_multimap& r) + { + return !(l < r); + } +} diff --git a/headers/3rdparty/flat.hpp/flat_multiset.hpp b/headers/3rdparty/flat.hpp/flat_multiset.hpp new file mode 100644 index 00000000..e6e91117 --- /dev/null +++ b/headers/3rdparty/flat.hpp/flat_multiset.hpp @@ -0,0 +1,421 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace flat_hpp +{ + template < typename Key + , typename Compare = std::less + , typename Container = std::vector > + class flat_multiset : private Compare { + using base_type = Compare; + public: + using key_type = Key; + using value_type = Key; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using value_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::const_iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::const_reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + public: + flat_multiset() + noexcept(std::is_nothrow_default_constructible_v + && std::is_nothrow_default_constructible_v) {} + + explicit flat_multiset(const Compare& c) + : base_type(c) {} + + template < typename Allocator > + explicit flat_multiset(const Allocator& a) + : data_(a) {} + + template < typename Allocator > + flat_multiset(const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) {} + + template < typename InputIter > + flat_multiset(InputIter first, InputIter last) { + insert(first, last); + } + + template < typename InputIter > + flat_multiset(InputIter first, InputIter last, const Compare& c) + : base_type(c) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_multiset(InputIter first, InputIter last, const Allocator& a) + : data_(a) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_multiset(InputIter first, InputIter last, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(first, last); + } + + flat_multiset(std::initializer_list ilist) { + insert(ilist); + } + + flat_multiset(std::initializer_list ilist, const Compare& c) + : base_type(c) { + insert(ilist); + } + + template < typename Allocator > + flat_multiset(std::initializer_list ilist, const Allocator& a) + : data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_multiset(std::initializer_list ilist, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_multiset(flat_multiset&& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(std::move(other.data_), a) {} + + template < typename Allocator > + flat_multiset(const flat_multiset& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(other.data_, a) {} + + flat_multiset(flat_multiset&& other) = default; + flat_multiset(const flat_multiset& other) = default; + + flat_multiset& operator=(flat_multiset&& other) = default; + flat_multiset& operator=(const flat_multiset& other) = default; + + flat_multiset& operator=(std::initializer_list ilist) { + flat_multiset(ilist).swap(*this); + return *this; + } + + iterator begin() + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return data_.cbegin(); + } + + iterator end() + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return data_.cend(); + } + + reverse_iterator rbegin() + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return data_.crbegin(); + } + + reverse_iterator rend() + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return data_.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return data_.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return data_.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return data_.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + + iterator insert(value_type&& value) { + const iterator iter = upper_bound(value); + return data_.insert(iter, std::move(value)); + } + + iterator insert(const value_type& value) { + const iterator iter = upper_bound(value); + return data_.insert(iter, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || !this->operator()(value, *(hint - 1))) + && (hint == end() || !this->operator()(*hint, value)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || !this->operator()(value, *(hint - 1))) + && (hint == end() || !this->operator()(*hint, value)) + ? data_.insert(hint, value) + : insert(value); + } + + template < typename InputIter > + void insert(InputIter first, InputIter last) { + while ( first != last ) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template < typename... Args > + iterator emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template < typename... Args > + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + void clear() + noexcept(noexcept(std::declval().clear())) { + data_.clear(); + } + + iterator erase(const_iterator iter) { + return data_.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return data_.erase(first, last); + } + + size_type erase(const key_type& key) { + const auto p = equal_range(key); + size_type r = std::distance(p.first, p.second); + erase(p.first, p.second); + return r; + } + + void swap(flat_multiset& other) + noexcept(std::is_nothrow_swappable_v + && std::is_nothrow_swappable_v) + { + using std::swap; + swap( + static_cast(*this), + static_cast(other)); + swap(data_, other.data_); + } + + size_type count(const key_type& key) const { + const auto p = equal_range(key); + return std::distance(p.first, p.second); + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, key_comp()); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, key_comp()); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + key_compare key_comp() const { + return *this; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + private: + container_type data_; + }; +} + +namespace flat_hpp +{ + template < typename Key + , typename Compare + , typename Container > + void swap( + flat_multiset& l, + flat_multiset& r) + noexcept(noexcept(l.swap(r))) + { + l.swap(r); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator==( + const flat_multiset& l, + const flat_multiset& r) + { + return l.size() == r.size() + && std::equal(l.begin(), l.end(), r.begin()); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator!=( + const flat_multiset& l, + const flat_multiset& r) + { + return !(l == r); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator<( + const flat_multiset& l, + const flat_multiset& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator>( + const flat_multiset& l, + const flat_multiset& r) + { + return r < l; + } + + template < typename Key + , typename Compare + , typename Container > + bool operator<=( + const flat_multiset& l, + const flat_multiset& r) + { + return !(r < l); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator>=( + const flat_multiset& l, + const flat_multiset& r) + { + return !(l < r); + } +} diff --git a/headers/3rdparty/flat.hpp/flat_set.hpp b/headers/3rdparty/flat.hpp/flat_set.hpp new file mode 100644 index 00000000..d5cf454a --- /dev/null +++ b/headers/3rdparty/flat.hpp/flat_set.hpp @@ -0,0 +1,425 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace flat_hpp +{ + template < typename Key + , typename Compare = std::less + , typename Container = std::vector > + class flat_set : private Compare { + using base_type = Compare; + public: + using key_type = Key; + using value_type = Key; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using value_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::const_iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::const_reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + public: + flat_set() + noexcept(std::is_nothrow_default_constructible_v + && std::is_nothrow_default_constructible_v) {} + + explicit flat_set(const Compare& c) + : base_type(c) {} + + template < typename Allocator > + explicit flat_set(const Allocator& a) + : data_(a) {} + + template < typename Allocator > + flat_set(const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) {} + + template < typename InputIter > + flat_set(InputIter first, InputIter last) { + insert(first, last); + } + + template < typename InputIter > + flat_set(InputIter first, InputIter last, const Compare& c) + : base_type(c) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_set(InputIter first, InputIter last, const Allocator& a) + : data_(a) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_set(InputIter first, InputIter last, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(first, last); + } + + flat_set(std::initializer_list ilist) { + insert(ilist); + } + + flat_set(std::initializer_list ilist, const Compare& c) + : base_type(c) { + insert(ilist); + } + + template < typename Allocator > + flat_set(std::initializer_list ilist, const Allocator& a) + : data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_set(std::initializer_list ilist, const Compare& c, const Allocator& a) + : base_type(c) + , data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_set(flat_set&& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(std::move(other.data_), a) {} + + template < typename Allocator > + flat_set(const flat_set& other, const Allocator& a) + : base_type(static_cast(other)) + , data_(other.data_, a) {} + + flat_set(flat_set&& other) = default; + flat_set(const flat_set& other) = default; + + flat_set& operator=(flat_set&& other) = default; + flat_set& operator=(const flat_set& other) = default; + + flat_set& operator=(std::initializer_list ilist) { + flat_set(ilist).swap(*this); + return *this; + } + + iterator begin() + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return data_.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return data_.cbegin(); + } + + iterator end() + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return data_.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return data_.cend(); + } + + reverse_iterator rbegin() + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return data_.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return data_.crbegin(); + } + + reverse_iterator rend() + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return data_.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return data_.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return data_.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return data_.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return data_.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + + std::pair insert(value_type&& value) { + const iterator iter = lower_bound(value); + return iter == end() || this->operator()(value, *iter) + ? std::make_pair(data_.insert(iter, std::move(value)), true) + : std::make_pair(iter, false); + } + + std::pair insert(const value_type& value) { + const iterator iter = lower_bound(value); + return iter == end() || this->operator()(value, *iter) + ? std::make_pair(data_.insert(iter, value), true) + : std::make_pair(iter, false); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || this->operator()(*(hint - 1), value)) + && (hint == end() || this->operator()(value, *hint)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)).first; + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || this->operator()(*(hint - 1), value)) + && (hint == end() || this->operator()(value, *hint)) + ? data_.insert(hint, value) + : insert(value).first; + } + + template < typename InputIter > + void insert(InputIter first, InputIter last) { + while ( first != last ) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template < typename... Args > + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template < typename... Args > + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + void clear() + noexcept(noexcept(std::declval().clear())) { + data_.clear(); + } + + iterator erase(const_iterator iter) { + return data_.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return data_.erase(first, last); + } + + size_type erase(const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; + } + + void swap(flat_set& other) + noexcept(std::is_nothrow_swappable_v + && std::is_nothrow_swappable_v) + { + using std::swap; + swap( + static_cast(*this), + static_cast(other)); + swap(data_, other.data_); + } + + size_type count(const key_type& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !this->operator()(key, *iter) + ? iter + : end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, key_comp()); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, key_comp()); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, key_comp()); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, key_comp()); + } + + key_compare key_comp() const { + return *this; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + private: + container_type data_; + }; +} + +namespace flat_hpp +{ + template < typename Key + , typename Compare + , typename Container > + void swap( + flat_set& l, + flat_set& r) + noexcept(noexcept(l.swap(r))) + { + l.swap(r); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator==( + const flat_set& l, + const flat_set& r) + { + return l.size() == r.size() + && std::equal(l.begin(), l.end(), r.begin()); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator!=( + const flat_set& l, + const flat_set& r) + { + return !(l == r); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator<( + const flat_set& l, + const flat_set& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator>( + const flat_set& l, + const flat_set& r) + { + return r < l; + } + + template < typename Key + , typename Compare + , typename Container > + bool operator<=( + const flat_set& l, + const flat_set& r) + { + return !(r < l); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator>=( + const flat_set& l, + const flat_set& r) + { + return !(l < r); + } +} diff --git a/headers/enduro2d/base/types.hpp b/headers/enduro2d/base/types.hpp index 3a613220..9f01f6f6 100644 --- a/headers/enduro2d/base/types.hpp +++ b/headers/enduro2d/base/types.hpp @@ -8,6 +8,9 @@ #include "_base.hpp" +#include <3rdparty/flat.hpp/flat_set.hpp> +#include <3rdparty/flat.hpp/flat_map.hpp> + namespace e2d { using f32 = float; @@ -73,3 +76,17 @@ namespace e2d , typename Traits = std::char_traits > using basic_string_view = std::basic_string_view; } + +namespace e2d +{ + template < typename Key + , typename Compare = std::less<> + , typename Container = std::vector > + using flat_set = flat_hpp::flat_set; + + template < typename Key + , typename Value + , typename Compare = std::less<> + , typename Container = std::vector> > + using flat_map = flat_hpp::flat_map; +} diff --git a/modules/flat.hpp b/modules/flat.hpp new file mode 160000 index 00000000..a592e0ed --- /dev/null +++ b/modules/flat.hpp @@ -0,0 +1 @@ +Subproject commit a592e0ed7cac5f879cf2428d1f44088a2b7424c6 diff --git a/modules/glfw b/modules/glfw index 57bf6b2f..d834f01c 160000 --- a/modules/glfw +++ b/modules/glfw @@ -1 +1 @@ -Subproject commit 57bf6b2f75e46369e83b6d727541991533b1dc2d +Subproject commit d834f01ca43c0f5ddd31b00a7fc2f48abbafa3da diff --git a/modules/imgui b/modules/imgui index 7bc03f71..958d75c0 160000 --- a/modules/imgui +++ b/modules/imgui @@ -1 +1 @@ -Subproject commit 7bc03f7155c67b5081a8e154b3c642fa5db5b33b +Subproject commit 958d75c00ac83a98b6423e523d9b222bc94a0224 diff --git a/scripts/update_modules.sh b/scripts/update_modules.sh index 70cafaad..9a4651a8 100755 --- a/scripts/update_modules.sh +++ b/scripts/update_modules.sh @@ -23,6 +23,9 @@ cp -fv $MODULES_DIR/catch2/single_include/catch2/catch.hpp $UNTESTS_DIR/catch/ca mkdir -p $HEADERS_RDPARTY_DIR/ecs.hpp cp -rfv $MODULES_DIR/ecs.hpp/headers/ecs.hpp/. $HEADERS_RDPARTY_DIR/ecs.hpp/ +mkdir -p $HEADERS_RDPARTY_DIR/flat.hpp +cp -rfv $MODULES_DIR/flat.hpp/headers/flat.hpp/. $HEADERS_RDPARTY_DIR/flat.hpp/ + mkdir -p $SOURCES_RDPARTY_DIR/imgui cp -fv $MODULES_DIR/imgui/imgui.cpp $SOURCES_RDPARTY_DIR/imgui/imgui.cpp cp -fv $MODULES_DIR/imgui/imgui.h $SOURCES_RDPARTY_DIR/imgui/imgui.h diff --git a/sources/3rdparty/imgui/imgui.cpp b/sources/3rdparty/imgui/imgui.cpp index a8153a23..4ff123e3 100644 --- a/sources/3rdparty/imgui/imgui.cpp +++ b/sources/3rdparty/imgui/imgui.cpp @@ -1133,6 +1133,7 @@ ImGuiStyle::ImGuiStyle() WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. WindowMinSize = ImVec2(32,32); // Minimum window size WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text + WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows @@ -3453,6 +3454,8 @@ void ImGui::NewFrame() IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!"); IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); + IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); + for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); @@ -5054,30 +5057,54 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } } +// Render title text, collapse button, close button void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; ImGuiWindowFlags flags = window->Flags; - // Close & collapse button are on layer 1 (same as menus) and don't default focus + const bool has_close_button = (p_open != NULL); + const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse); + + // Close & collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); - // Collapse button - if (!(flags & ImGuiWindowFlags_NoCollapse)) - if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos)) - window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function + // Layout buttons + // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. + float pad_l = style.FramePadding.x; + float pad_r = style.FramePadding.x; + float button_sz = g.FontSize; + ImVec2 close_button_pos; + ImVec2 collapse_button_pos; + if (has_close_button) + { + pad_r += button_sz; + close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); + } + if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) + { + pad_r += button_sz; + collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); + } + if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) + { + collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); + pad_l += button_sz; + } + + // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) + if (has_collapse_button) + if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) + window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function // Close button - if (p_open != NULL) - { - const float rad = g.FontSize * 0.5f; - if (CloseButton(window->GetID("#CLOSE"), ImVec2(window->Pos.x + window->Size.x - style.FramePadding.x - rad, window->Pos.y + style.FramePadding.y + rad), rad + 1)) + if (has_close_button) + if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) *p_open = false; - } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); @@ -5088,21 +5115,30 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const char* UNSAVED_DOCUMENT_MARKER = "*"; const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); - ImRect text_r = title_bar_rect; - float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); - float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); - if (style.WindowTitleAlign.x > 0.0f) - pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x); - text_r.Min.x += pad_left; - text_r.Max.x -= pad_right; - ImRect clip_rect = text_r; - clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton() - RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); + + // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, + // while uncentered title text will still reach edges correct. + if (pad_l > style.FramePadding.x) + pad_l += g.Style.ItemInnerSpacing.x; + if (pad_r > style.FramePadding.x) + pad_r += g.Style.ItemInnerSpacing.x; + if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) + { + float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center + float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); + pad_l = ImMax(pad_l, pad_extend * centerness); + pad_r = ImMax(pad_r, pad_extend * centerness); + } + + ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); + ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y); + //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); if (flags & ImGuiWindowFlags_UnsavedDocument) { - ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); + ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f)); - RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect); + RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); } } diff --git a/sources/3rdparty/imgui/imgui.h b/sources/3rdparty/imgui/imgui.h index 77fab495..188372f9 100644 --- a/sources/3rdparty/imgui/imgui.h +++ b/sources/3rdparty/imgui/imgui.h @@ -1283,6 +1283,7 @@ struct ImGuiStyle float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constraint individual windows, use SetNextWindowSizeConstraints(). ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. + ImGuiDir WindowMenuButtonPosition; // Side of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. float ChildRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows. float ChildBorderSize; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). float PopupRounding; // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding) diff --git a/sources/3rdparty/imgui/imgui_demo.cpp b/sources/3rdparty/imgui/imgui_demo.cpp index af0700c4..9b56e429 100644 --- a/sources/3rdparty/imgui/imgui_demo.cpp +++ b/sources/3rdparty/imgui/imgui_demo.cpp @@ -2999,6 +2999,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); ImGui::Text("Alignment"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); + ImGui::Combo("WindowMenuButtonPosition", (int*)&style.WindowMenuButtonPosition, "Left\0Right\0"); ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); ImGui::Text("Safe Area Padding"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); diff --git a/sources/3rdparty/imgui/imgui_internal.h b/sources/3rdparty/imgui/imgui_internal.h index b59844c0..8afa9206 100644 --- a/sources/3rdparty/imgui/imgui_internal.h +++ b/sources/3rdparty/imgui/imgui_internal.h @@ -811,7 +811,7 @@ struct ImGuiNextItemData // Tabs //----------------------------------------------------------------------------- -struct ImGuiTabBarSortItem +struct ImGuiShrinkWidthItem { int Index; float Width; @@ -976,7 +976,7 @@ struct ImGuiContext ImPool TabBars; ImGuiTabBar* CurrentTabBar; ImVector CurrentTabBarStack; - ImVector TabSortByWidthBuffer; + ImVector ShrinkWidthBuffer; // Widget state ImVec2 LastValidMousePos; @@ -1495,6 +1495,7 @@ namespace ImGui IMGUI_API void PopItemFlag(); IMGUI_API bool IsItemToggledSelection(); // was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API ImVec2 GetWorkRectMax(); + IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. @@ -1581,7 +1582,7 @@ namespace ImGui // Widgets IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); - IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); + IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags); IMGUI_API void Scrollbar(ImGuiAxis axis); diff --git a/sources/3rdparty/imgui/imgui_widgets.cpp b/sources/3rdparty/imgui/imgui_widgets.cpp index 30f721b6..cbf7ff72 100644 --- a/sources/3rdparty/imgui/imgui_widgets.cpp +++ b/sources/3rdparty/imgui/imgui_widgets.cpp @@ -717,14 +717,14 @@ bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) } // Button to close a window -bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)//, float size) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). - const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); + const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); bool is_clipped = !ItemAdd(bb, id); bool hovered, held; @@ -733,11 +733,12 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) return pressed; // Render + ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); ImVec2 center = bb.GetCenter(); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col, 12); - float cross_extent = (radius * 0.7071f) - 1.0f; + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; ImU32 cross_col = GetColorU32(ImGuiCol_Text); center -= ImVec2(0.5f, 0.5f); window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f); @@ -756,9 +757,11 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + // Render ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImVec2 center = bb.GetCenter(); if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9); + window->DrawList->AddCircleFilled(center/* + ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, col, 12); RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold @@ -1149,6 +1152,7 @@ void ImGui::Bullet() // - SeparatorEx() [Internal] // - Separator() // - SplitterBehavior() [Internal] +// - ShrinkWidths() [Internal] //------------------------------------------------------------------------- void ImGui::Spacing() @@ -1330,6 +1334,33 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float return held; } +static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) +{ + const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs; + const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; + if (int d = (int)(b->Width - a->Width)) + return d; + return (b->Index - a->Index); +} + +// Shrink excess width from a set of item, by removing width from the larger items first. +void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess) +{ + if (count > 1) + ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); + int count_same_width = 1; + while (width_excess > 0.0f && count_same_width < count) + { + while (count_same_width < count && items[0].Width == items[count_same_width].Width) + count_same_width++; + float width_to_remove_per_item_max = (count_same_width < count) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); + float width_to_remove_per_item = ImMin(width_excess / count_same_width, width_to_remove_per_item_max); + for (int item_n = 0; item_n < count_same_width; item_n++) + items[item_n].Width -= width_to_remove_per_item; + width_excess -= width_to_remove_per_item * count_same_width; + } +} + //------------------------------------------------------------------------- // [SECTION] Widgets: ComboBox //------------------------------------------------------------------------- @@ -2068,15 +2099,22 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int for (int i = 0; i < components; i++) { PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); value_changed |= DragScalar("", data_type, v, v_speed, v_min, v_max, format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); PopID(); PopItemWidth(); v = (void*)((char*)v + type_size); } PopID(); - TextEx(label, FindRenderedTextEnd(label)); + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + EndGroup(); return value_changed; } @@ -2516,15 +2554,22 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i for (int i = 0; i < components; i++) { PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); PopID(); PopItemWidth(); v = (void*)((char*)v + type_size); } PopID(); - TextEx(label, FindRenderedTextEnd(label)); + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + EndGroup(); return value_changed; } @@ -2827,8 +2872,13 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); value_changed = true; } - SameLine(0, style.ItemInnerSpacing.x); - TextEx(label, FindRenderedTextEnd(label)); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, style.ItemInnerSpacing.x); + TextEx(label, label_end); + } style.FramePadding = backup_frame_padding; PopID(); @@ -2860,15 +2910,22 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in for (int i = 0; i < components; i++) { PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); value_changed |= InputScalar("", data_type, v, step, step_fast, format, flags); - SameLine(0, g.Style.ItemInnerSpacing.x); PopID(); PopItemWidth(); v = (void*)((char*)v + type_size); } PopID(); - TextEx(label, FindRenderedTextEnd(label)); + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0.0f, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + EndGroup(); return value_changed; } @@ -5301,12 +5358,14 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label); if (p_open) { - // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + // Create a small overlapping close button + // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. ImGuiContext& g = *GImGui; ImGuiItemHoveredDataBackup last_item_backup; - float button_radius = g.FontSize * 0.5f; - ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y); - if (CloseButton(window->GetID((void*)((intptr_t)id+1)), button_center, button_radius)) + float button_size = g.FontSize; + ImVec2 button_pos = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x * 2.0f - button_size, window->DC.LastItemRect.Min.y); + if (CloseButton(window->GetID((void*)((intptr_t)id + 1)), button_pos)) *p_open = false; last_item_backup.Restore(); } @@ -6195,15 +6254,6 @@ static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const voi return (int)(a->Offset - b->Offset); } -static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs) -{ - const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs; - const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs; - if (int d = (int)(b->Width - a->Width)) - return d; - return (b->Index - a->Index); -} - static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiTabBarRef& ref) { ImGuiContext& g = *GImGui; @@ -6377,10 +6427,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Max.x! scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; - ImVector& width_sort_buffer = g.TabSortByWidthBuffer; - width_sort_buffer.resize(tab_bar->Tabs.Size); - // Compute ideal widths + g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); float width_total_contents = 0.0f; ImGuiTabItem* most_recently_selected_tab = NULL; bool found_selected_tab_id = false; @@ -6403,8 +6451,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents; // Store data so we can build an array sorted by width if we need to shrink tabs down - width_sort_buffer[tab_n].Index = tab_n; - width_sort_buffer[tab_n].Width = tab->WidthContents; + g.ShrinkWidthBuffer[tab_n].Index = tab_n; + g.ShrinkWidthBuffer[tab_n].Width = tab->WidthContents; } // Compute width @@ -6413,21 +6461,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown)) { // If we don't have enough room, resize down the largest tabs first - if (tab_bar->Tabs.Size > 1) - ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer); - int tab_count_same_width = 1; - while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size) - { - while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width) - tab_count_same_width++; - float width_to_remove_per_tab_max = (tab_count_same_width < tab_bar->Tabs.Size) ? (width_sort_buffer[0].Width - width_sort_buffer[tab_count_same_width].Width) : (width_sort_buffer[0].Width - 1.0f); - float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max); - for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++) - width_sort_buffer[tab_n].Width -= width_to_remove_per_tab; - width_excess -= width_to_remove_per_tab * tab_count_same_width; - } + ShrinkWidths(g.ShrinkWidthBuffer.Data, g.ShrinkWidthBuffer.Size, width_excess); for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width; + tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index].Width = (float)(int)g.ShrinkWidthBuffer[tab_n].Width; } else { @@ -7007,16 +7043,18 @@ bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, if (close_button_visible) { ImGuiItemHoveredDataBackup last_item_backup; - const float close_button_sz = g.FontSize * 0.5f; - if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x - close_button_sz, bb.Min.y + frame_padding.y + close_button_sz), close_button_sz)) + const float close_button_sz = g.FontSize; + PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); + if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y))) close_button_pressed = true; + PopStyleVar(); last_item_backup.Restore(); // Close with middle mouse button if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) close_button_pressed = true; - text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f; + text_pixel_clip_bb.Max.x -= close_button_sz; } // Label with ellipsis