2025-07-06 20:43:53 +03:00
# include "Toolbar.hpp"
2025-12-26 10:40:58 +03:00
# include "DrawingProgram/Tools/DrawingProgramToolBase.hpp"
# include "DrawingProgram/Tools/ScreenshotTool.hpp"
2025-07-06 20:43:53 +03:00
# include "Helpers/ConvertVec.hpp"
2025-10-01 20:08:01 +03:00
# include "Helpers/FileDownloader.hpp"
2026-01-08 16:37:18 +03:00
# include "Helpers/MathExtras.hpp"
2025-07-06 20:43:53 +03:00
# include "Helpers/Networking/NetLibrary.hpp"
2025-12-10 13:06:45 +03:00
# include "Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp"
2025-07-06 20:43:53 +03:00
# include "MainProgram.hpp"
# include "GUIStuff/Elements/Element.hpp"
# include "GUIStuff/GUIManager.hpp"
# include "InputManager.hpp"
2025-11-16 16:39:12 +03:00
# include "VersionConstants.hpp"
2025-07-06 20:43:53 +03:00
# include "World.hpp"
# include <SDL3/SDL_dialog.h>
2025-11-20 12:38:28 +03:00
# include <SDL3/SDL_render.h>
2025-07-06 20:43:53 +03:00
# include <algorithm>
# include <filesystem>
# include <optional>
# include <Helpers/Logger.hpp>
# include <fstream>
# include <Helpers/StringHelpers.hpp>
2025-10-01 20:08:01 +03:00
# include <Helpers/VersionNumber.hpp>
2025-10-15 16:33:24 +03:00
# include <modules/skparagraph/src/ParagraphBuilderImpl.h>
# include <modules/skparagraph/include/ParagraphStyle.h>
# include <modules/skparagraph/include/FontCollection.h>
# include <modules/skparagraph/include/TextStyle.h>
2025-10-16 09:38:58 +03:00
# include <include/core/SkFontStyle.h>
2025-10-15 16:33:24 +03:00
# include <modules/skunicode/include/SkUnicode_icu.h>
2025-11-21 10:36:46 +03:00
# include <modules/svg/include/SkSVGNode.h>
# include <include/core/SkStream.h>
2025-10-01 20:08:01 +03:00
# define UPDATE_DOWNLOAD_URL "https: //infinipaint.com/download.html"
# define UPDATE_NOTIFICATION_URL "https: //infinipaint.com/updateNotificationVersion.txt"
2025-07-06 20:43:53 +03:00
# ifdef __EMSCRIPTEN__
# include <EmscriptenHelpers/emscripten_browser_file.h>
# endif
Toolbar : : NativeFilePicker Toolbar : : nativeFilePicker ;
Toolbar : : Toolbar ( MainProgram & initMain ) :
io ( std : : make_shared < GUIStuff : : UpdateInputData > ( ) ) ,
main ( initMain )
{
2025-11-16 10:42:01 +03:00
io - > textTypeface = main . fonts - > map [ " Roboto " ] ;
io - > fonts = main . fonts ;
2025-11-21 10:36:46 +03:00
// NOTE: On windows, when the native file picker is open, any call to MakeFromFile fails
// So, it's better to load the icons at the beginning of the program so that the icon loading doesn't fail later
load_icons_at ( " data/icons " ) ;
2025-07-06 20:43:53 +03:00
load_default_palette ( ) ;
load_default_theme ( ) ;
}
2025-11-21 10:36:46 +03:00
void Toolbar : : load_icons_at ( const std : : filesystem : : path & pathToLoad ) {
for ( auto & iconDirEntry : std : : filesystem : : recursive_directory_iterator ( pathToLoad ) ) {
if ( iconDirEntry . is_regular_file ( ) ) {
std : : string iconRelativePath = iconDirEntry . path ( ) . relative_path ( ) . string ( ) ;
2025-11-21 10:44:40 +03:00
std : : replace ( iconRelativePath . begin ( ) , iconRelativePath . end ( ) , ' \\ ' , ' / ' ) ;
2025-11-21 10:36:46 +03:00
auto stream = SkStream : : MakeFromFile ( iconRelativePath . c_str ( ) ) ;
if ( ! stream )
throw std : : runtime_error ( " [Toolbar::Toolbar] Could not open file " + iconRelativePath ) ;
auto svgDom = SkSVGDOM : : Builder ( ) . make ( * stream ) ;
if ( ! svgDom )
throw std : : runtime_error ( " [Toolbar::Toolbar] Could not parse SVG " + iconRelativePath ) ;
else {
if ( svgDom - > containerSize ( ) . width ( ) = = 0 | | svgDom - > containerSize ( ) . height ( ) = = 0 )
svgDom - > setContainerSize ( { 1000 , 1000 } ) ;
io - > svgData [ iconRelativePath ] = svgDom ;
}
}
}
}
2025-07-06 20:43:53 +03:00
void Toolbar : : load_default_theme ( ) {
io - > theme = GUIStuff : : get_default_dark_mode ( ) ;
themeData . themeCurrentlyLoaded = " Default " ;
}
void Toolbar : : save_theme ( ) {
std : : filesystem : : create_directory ( main . configPath / " themes " ) ;
std : : ofstream f ( main . configPath / " themes " / ( themeData . themeCurrentlyLoaded + " .json " ) ) ;
if ( f . is_open ( ) ) {
using json = nlohmann : : json ;
json j ;
j = * io - > theme ;
f < < j ;
f . close ( ) ;
}
}
bool Toolbar : : load_theme ( ) {
std : : filesystem : : path themeDir = main . configPath / " themes " ;
bool successfullyLoaded = false ;
if ( std : : filesystem : : exists ( themeDir ) & & std : : filesystem : : is_directory ( themeDir ) ) {
std : : ifstream f ( themeDir / ( themeData . themeCurrentlyLoaded + " .json " ) ) ;
if ( f . is_open ( ) ) {
using json = nlohmann : : json ;
try {
json j ;
f > > j ;
auto theme ( std : : make_shared < GUIStuff : : Theme > ( ) ) ;
j . get_to ( * theme ) ;
io - > theme = theme ;
successfullyLoaded = true ;
} catch ( . . . ) { }
f . close ( ) ;
}
}
if ( ! successfullyLoaded )
load_default_theme ( ) ;
return successfullyLoaded ;
}
void Toolbar : : load_default_palette ( ) {
paletteData . palettes . clear ( ) ;
paletteData . palettes . emplace_back ( ) ;
auto & palette = paletteData . palettes . back ( ) . colors ;
paletteData . palettes . back ( ) . name = " Default " ;
palette = { { 1.0 , 1.0 , 1.0 } , { 0.0 , 0.0 , 0.0 } , { 1.0 , 0.0 , 0.0 } , { 1.0 , 0.529411792755127 , 0.0 } , { 1.0 , 0.8274509906768799 , 0.0 } , { 0.8705882430076599 , 1.0 , 0.03921568766236305 } , { 0.6313725709915161 , 1.0 , 0.03921568766236305 } , { 0.03921568766236305 , 1.0 , 0.6000000238418579 } , { 0.03921568766236305 , 0.9372549057006836 , 1.0 } , { 0.0784313753247261 , 0.4901960790157318 , 0.9607843160629272 } , { 0.3450980484485626 , 0.03921568766236305 , 1.0 } , { 0.7450980544090271 , 0.03921568766236305 , 1.0 } } ;
paletteData . selectedPalette = 0 ;
}
void Toolbar : : save_palettes ( ) {
std : : ofstream f ( main . configPath / " palettes.json " ) ;
if ( f . is_open ( ) ) {
using json = nlohmann : : json ;
json j ;
auto palettesToSave = paletteData . palettes ;
palettesToSave . erase ( palettesToSave . begin ( ) ) ;
j = palettesToSave ;
f < < j ;
f . close ( ) ;
}
}
void Toolbar : : load_palettes ( ) {
std : : ifstream f ( main . configPath / " palettes.json " ) ;
load_default_palette ( ) ;
if ( f . is_open ( ) ) {
using json = nlohmann : : json ;
try {
json j ;
f > > j ;
std : : vector < PaletteData : : Palette > palettes ;
j . get_to ( palettes ) ;
paletteData . palettes . insert ( paletteData . palettes . end ( ) , palettes . begin ( ) , palettes . end ( ) ) ;
} catch ( . . . ) { }
f . close ( ) ;
}
}
// You can't trust that filter wont be -1 on the platform youre on, so dont use the extension from the callback
void Toolbar : : sdl_open_file_dialog_callback ( void * userData , const char * const * fileList , int filter ) {
if ( ! fileList ) {
nativeFilePicker . isOpen = false ;
return ;
}
if ( ! ( * fileList ) ) {
nativeFilePicker . isOpen = false ;
return ;
}
ExtensionFilter e ;
if ( filter > = 0 )
e = nativeFilePicker . extensionFiltersComplete [ filter ] ;
nativeFilePicker . postSelectionFunc ( fileList [ 0 ] , e ) ;
nativeFilePicker . isOpen = false ;
}
void Toolbar : : open_file_selector ( const std : : string & filePickerName , const std : : vector < ExtensionFilter > & extensionFilters , OpenFileSelectorCallback postSelectionFunc , const std : : string & fileName , bool isSaving ) {
if ( useNativeFilePicker ) {
if ( ! nativeFilePicker . isOpen ) {
nativeFilePicker . postSelectionFunc = postSelectionFunc ;
nativeFilePicker . extensionFiltersComplete = extensionFilters ;
nativeFilePicker . sdlFileFilters . clear ( ) ;
for ( auto & e : nativeFilePicker . extensionFiltersComplete )
nativeFilePicker . sdlFileFilters . emplace_back ( e . name . c_str ( ) , e . extensions . c_str ( ) ) ;
if ( isSaving )
SDL_ShowSaveFileDialog ( sdl_open_file_dialog_callback , nullptr , main . window . sdlWindow , nativeFilePicker . sdlFileFilters . data ( ) , nativeFilePicker . sdlFileFilters . size ( ) , nullptr ) ;
else
SDL_ShowOpenFileDialog ( sdl_open_file_dialog_callback , nullptr , main . window . sdlWindow , nativeFilePicker . sdlFileFilters . data ( ) , nativeFilePicker . sdlFileFilters . size ( ) , nullptr , false ) ;
}
}
else {
filePicker . isOpen = true ;
filePicker . refreshEntries = true ;
filePicker . extensionFiltersComplete = extensionFilters ;
filePicker . extensionFilters . clear ( ) ;
for ( auto & [ name , exList ] : extensionFilters )
filePicker . extensionFilters . emplace_back ( exList ) ;
filePicker . extensionSelected = extensionFilters . size ( ) - 1 ;
filePicker . filePickerWindowName = filePickerName ;
filePicker . postSelectionFunc = postSelectionFunc ;
filePicker . fileName = " " ;
}
}
std : : filesystem : : path & Toolbar : : file_selector_path ( ) {
return filePicker . currentSearchPath ;
}
void Toolbar : : color_selector_left ( Vector4f * color ) {
colorLeft = color ;
justAssignedColorLeft = true ;
}
void Toolbar : : color_selector_right ( Vector4f * color ) {
colorRight = color ;
justAssignedColorRight = true ;
}
void Toolbar : : load_licenses ( ) {
2025-11-21 08:35:47 +03:00
std : : filesystem : : path third_party_license_path ( " data/third_party_licenses " ) ;
2025-07-06 20:43:53 +03:00
for ( const auto & entry : std : : filesystem : : directory_iterator ( third_party_license_path ) ) {
if ( entry . is_regular_file ( ) )
thirdPartyLicenses . emplace_back ( entry . path ( ) . filename ( ) . string ( ) , read_file_to_string ( entry . path ( ) ) ) ;
}
std : : sort ( thirdPartyLicenses . begin ( ) , thirdPartyLicenses . end ( ) , [ ] ( const auto & a1 , const auto & a2 ) {
return std : : lexicographical_compare ( a1 . first . begin ( ) , a1 . first . end ( ) , a2 . first . begin ( ) , a2 . first . end ( ) ) ;
} ) ;
2025-11-16 16:39:12 +03:00
ownLicenseText = " InfiniPaint v " + VersionConstants : : CURRENT_VERSION_STRING ;
2025-10-30 11:26:00 +03:00
ownLicenseText + =
2025-07-06 20:43:53 +03:00
R " (
Copyright © 2025 Yousef Khadadeh
Permission is hereby granted , free of charge , to any person obtaining a copy of this software and associated documentation files ( the “ Software ” ) , to deal in the Software without restriction , including without limitation the rights to use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of the Software , and to permit persons to whom the Software is furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED “ AS IS ” , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
) " ;
}
nlohmann : : json Toolbar : : get_config_json ( ) {
using json = nlohmann : : json ;
json toRet ;
json jKeybinds ;
for ( unsigned i = 0 ; i < InputManager : : KEY_ASSIGNABLE_COUNT ; i + + ) {
auto f = std : : find_if ( main . input . keyAssignments . begin ( ) , main . input . keyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = i ;
} ) ;
if ( f ! = main . input . keyAssignments . end ( ) )
jKeybinds [ json ( static_cast < InputManager : : KeyCodeEnum > ( i ) ) ] = main . input . key_assignment_to_str ( f - > first ) ;
}
toRet [ " keybinds " ] = jKeybinds ;
toRet [ " guiScale " ] = guiScale ;
toRet [ " jumpTransitionTime " ] = jumpTransitionTime ;
toRet [ " dragZoomSpeed " ] = dragZoomSpeed ;
toRet [ " scrollZoomSpeed " ] = scrollZoomSpeed ;
2025-11-20 12:38:28 +03:00
toRet [ " vsync " ] = main . window . vsyncValue ;
2025-11-23 20:51:14 +03:00
# ifndef __EMSCRIPTEN__
2025-11-23 12:53:50 +03:00
toRet [ " applyDisplayScale " ] = main . window . applyDisplayScale ;
2025-11-23 20:51:14 +03:00
# endif
2025-07-06 20:43:53 +03:00
toRet [ " displayName " ] = main . displayName ;
toRet [ " useNativeFilePicker " ] = useNativeFilePicker ;
toRet [ " themeInUse " ] = themeData . themeCurrentlyLoaded ;
2025-08-01 20:07:47 +03:00
toRet [ " defaultCanvasBackgroundColor " ] = main . defaultCanvasBackgroundColor ;
2025-10-03 09:11:22 +03:00
toRet [ " flipZoomToolDirection " ] = flipZoomToolDirection ;
2025-10-01 20:08:01 +03:00
# ifndef __EMSCRIPTEN__
toRet [ " checkForUpdates " ] = updateCheckerData . checkForUpdates ;
# endif
2025-07-08 15:12:35 +03:00
json tablet ;
tablet [ " pressureAffectsBrushWidth " ] = tabletOptions . pressureAffectsBrushWidth ;
tablet [ " smoothingSamplingTime " ] = tabletOptions . smoothingSamplingTime ;
tablet [ " middleClickButton " ] = tabletOptions . middleClickButton ;
tablet [ " rightClickButton " ] = tabletOptions . rightClickButton ;
tablet [ " ignoreMouseMovementWhenPenInProximity " ] = tabletOptions . ignoreMouseMovementWhenPenInProximity ;
2025-11-30 16:19:36 +03:00
tablet [ " brushMinimumSize " ] = tabletOptions . brushMinimumSize ;
2026-01-16 17:42:00 +03:00
tablet [ " zoomWhilePenDownAndButtonHeld " ] = tabletOptions . zoomWhilePenDownAndButtonHeld ;
2025-07-08 15:12:35 +03:00
toRet [ " tablet " ] = tablet ;
2026-01-24 12:31:55 +03:00
json debugJson ;
debugJson [ " showPerformance " ] = showPerformance ;
debugJson [ " fpsLimit " ] = main . fpsLimit ;
debugJson [ " jumpTransitionEasing " ] = jumpTransitionEasing ;
debugJson [ " cacheNodeResolution " ] = DrawingProgramCache : : CACHE_NODE_RESOLUTION ;
debugJson [ " maxCacheNodes " ] = DrawingProgramCache : : MAXIMUM_DRAW_CACHE_SURFACES ;
debugJson [ " maxComponentsInNode " ] = DrawingProgramCache : : MAXIMUM_COMPONENTS_IN_SINGLE_NODE ;
debugJson [ " componentCountToForceCacheRebuild " ] = DrawingProgramCache : : MINIMUM_COMPONENTS_TO_START_REBUILD ;
2026-01-27 12:40:39 +03:00
debugJson [ " maximumFrameTimeToForceCacheRebuild " ] = DrawingProgramCache : : MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH ;
debugJson [ " millisecondMinimumTimeToCheckForCacheRebuild " ] = DrawingProgramCache : : MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH ;
2026-01-24 12:31:55 +03:00
toRet [ " debug " ] = debugJson ;
2025-07-06 20:43:53 +03:00
return toRet ;
}
2025-11-17 16:08:42 +03:00
void Toolbar : : set_config_json ( const nlohmann : : json & j , VersionNumber version ) {
2025-07-06 20:43:53 +03:00
using json = nlohmann : : json ;
main . input . keyAssignments . clear ( ) ;
try {
const json & jKeybinds = j . at ( " keybinds " ) ;
for ( unsigned i = 0 ; i < InputManager : : KEY_ASSIGNABLE_COUNT ; i + + ) {
try {
Vector2ui32 a = main . input . key_assignment_from_str ( jKeybinds . at ( json ( static_cast < InputManager : : KeyCodeEnum > ( i ) ) ) ) ;
if ( a ! = Vector2ui32 { 0 , 0 } )
main . input . keyAssignments . emplace ( a , i ) ;
else
throw ;
}
catch ( . . . ) {
auto f = std : : find_if ( main . input . defaultKeyAssignments . begin ( ) , main . input . defaultKeyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = i ;
} ) ;
main . input . keyAssignments . emplace ( f - > first , i ) ;
}
}
}
catch ( . . . ) {
main . input . keyAssignments = main . input . defaultKeyAssignments ;
}
2025-11-17 16:08:42 +03:00
try { j . at ( " displayName " ) . get_to ( main . displayName ) ; } catch ( . . . ) { }
try { j . at ( " dragZoomSpeed " ) . get_to ( dragZoomSpeed ) ; } catch ( . . . ) { }
try { j . at ( " scrollZoomSpeed " ) . get_to ( scrollZoomSpeed ) ; } catch ( . . . ) { }
2025-11-20 12:38:28 +03:00
try {
j . at ( " vsync " ) . get_to ( main . window . vsyncValue ) ;
main . set_vsync_value ( main . window . vsyncValue ) ;
}
catch ( . . . ) {
main . set_vsync_value ( 1 ) ;
}
2025-11-23 20:51:14 +03:00
# ifndef __EMSCRIPTEN__
2025-11-23 12:53:50 +03:00
try { j . at ( " applyDisplayScale " ) . get_to ( main . window . applyDisplayScale ) ; } catch ( . . . ) { }
2025-11-23 20:51:14 +03:00
# endif
2025-11-17 16:08:42 +03:00
try { j . at ( " guiScale " ) . get_to ( guiScale ) ; } catch ( . . . ) { }
try { j . at ( " useNativeFilePicker " ) . get_to ( useNativeFilePicker ) ; } catch ( . . . ) { }
try { j . at ( " themeInUse " ) . get_to ( themeData . themeCurrentlyLoaded ) ; } catch ( . . . ) { }
if ( version > = VersionNumber ( 0 , 3 , 0 ) )
try { j . at ( " defaultCanvasBackgroundColor " ) . get_to ( main . defaultCanvasBackgroundColor ) ; } catch ( . . . ) { }
try { j . at ( " flipZoomToolDirection " ) . get_to ( flipZoomToolDirection ) ; } catch ( . . . ) { }
2025-10-01 20:08:01 +03:00
# ifndef __EMSCRIPTEN__
2025-11-17 16:08:42 +03:00
try { j . at ( " checkForUpdates " ) . get_to ( updateCheckerData . checkForUpdates ) ; } catch ( . . . ) { }
2025-10-01 20:08:01 +03:00
# endif
2025-07-08 12:18:55 +03:00
2025-11-17 16:08:42 +03:00
try { j . at ( " tablet " ) . at ( " pressureAffectsBrushWidth " ) . get_to ( tabletOptions . pressureAffectsBrushWidth ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " smoothingSamplingTime " ) . get_to ( tabletOptions . smoothingSamplingTime ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " middleClickButton " ) . get_to ( tabletOptions . middleClickButton ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " rightClickButton " ) . get_to ( tabletOptions . rightClickButton ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " ignoreMouseMovementWhenPenInProximity " ) . get_to ( tabletOptions . ignoreMouseMovementWhenPenInProximity ) ; } catch ( . . . ) { }
2025-11-30 16:19:36 +03:00
try { j . at ( " tablet " ) . at ( " brushMinimumSize " ) . get_to ( tabletOptions . brushMinimumSize ) ; } catch ( . . . ) { }
2026-01-16 17:42:00 +03:00
try { j . at ( " tablet " ) . at ( " zoomWhilePenDownAndButtonHeld " ) . get_to ( tabletOptions . zoomWhilePenDownAndButtonHeld ) ; } catch ( . . . ) { }
2025-07-08 15:12:35 +03:00
2026-01-24 12:31:55 +03:00
try { j . at ( " debug " ) . at ( " showPerformance " ) . get_to ( showPerformance ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " fpsLimit " ) . get_to ( main . fpsLimit ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " jumpTransitionEasing " ) . get_to ( jumpTransitionEasing ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " cacheNodeResolution " ) . get_to ( DrawingProgramCache : : CACHE_NODE_RESOLUTION ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " maxCacheNodes " ) . get_to ( DrawingProgramCache : : MAXIMUM_DRAW_CACHE_SURFACES ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " maxComponentsInNode " ) . get_to ( DrawingProgramCache : : MAXIMUM_COMPONENTS_IN_SINGLE_NODE ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " componentCountToForceCacheRebuild " ) . get_to ( DrawingProgramCache : : MINIMUM_COMPONENTS_TO_START_REBUILD ) ; } catch ( . . . ) { }
2026-01-27 12:40:39 +03:00
try { j . at ( " debug " ) . at ( " maximumFrameTimeToForceCacheRebuild " ) . get_to ( DrawingProgramCache : : MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " millisecondMinimumTimeToCheckForCacheRebuild " ) . get_to ( DrawingProgramCache : : MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH ) ; } catch ( . . . ) { }
2026-01-24 12:31:55 +03:00
2025-07-08 12:18:55 +03:00
main . update_display_names ( ) ;
2025-07-06 20:43:53 +03:00
}
void Toolbar : : update ( ) {
2025-10-01 20:08:01 +03:00
# ifndef __EMSCRIPTEN__
update_notification_check ( ) ;
# endif
2025-07-06 20:43:53 +03:00
if ( main . input . key ( InputManager : : KEY_SAVE ) . pressed & & ! optionsMenuOpen & & ! filePicker . isOpen )
save_func ( ) ;
if ( main . input . key ( InputManager : : KEY_SAVE_AS ) . pressed & & ! optionsMenuOpen & & ! filePicker . isOpen )
save_as_func ( ) ;
if ( main . input . key ( InputManager : : KEY_SHOW_METRICS ) . pressed )
showPerformance = ! showPerformance ;
if ( main . input . key ( InputManager : : KEY_OPEN_CHAT ) . pressed & & chatBoxState = = CHATBOXSTATE_CLOSE )
chatBoxState = CHATBOXSTATE_JUSTOPEN ;
if ( main . input . key ( InputManager : : KEY_SHOW_PLAYER_LIST ) . pressed )
playerMenuOpen = ! playerMenuOpen ;
start_gui ( ) ;
if ( main . drawGui ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-09-27 17:59:56 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( gui . windowSize . x ( ) ) , . height = CLAY_SIZING_FIT ( gui . windowSize . y ( ) ) } ,
2025-07-06 20:43:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
} ) {
top_toolbar ( ) ;
2026-01-09 21:56:58 +03:00
if ( ! main . world - > clientStillConnecting )
drawing_program_gui ( ) ;
2026-01-18 13:39:49 +03:00
if ( ! closePopupData . worldsToClose . empty ( ) )
close_popup_gui ( ) ;
2025-07-06 20:43:53 +03:00
if ( viewWebVersionWelcome )
web_version_welcome ( ) ;
2025-10-01 20:08:01 +03:00
# ifndef __EMSCRIPTEN__
else if ( updateCheckerData . showGui )
update_notification_gui ( ) ;
# endif
2025-07-06 20:43:53 +03:00
else if ( filePicker . isOpen )
file_picker_gui ( ) ;
else if ( optionsMenuOpen )
options_menu ( ) ;
2026-01-09 21:56:58 +03:00
else if ( main . world - > clientStillConnecting )
still_connecting_center_message ( ) ;
2025-07-06 20:43:53 +03:00
else if ( playerMenuOpen )
player_list ( ) ;
2026-01-09 21:56:58 +03:00
else if ( ! main . world - > drawProg . layerMan . is_a_layer_being_edited ( ) )
no_layers_being_edited_message ( ) ;
2025-07-06 20:43:53 +03:00
if ( showPerformance )
performance_metrics ( ) ;
}
2026-01-09 21:56:58 +03:00
if ( ! main . world - > clientStillConnecting )
chat_box ( ) ;
2025-07-06 20:43:53 +03:00
}
2026-01-23 09:40:50 +03:00
else {
if ( ! closePopupData . worldsToClose . empty ( ) ) // Should still show close popup if gui is disabled
close_popup_gui ( ) ;
}
2025-07-06 20:43:53 +03:00
2026-01-09 21:56:58 +03:00
if ( ! main . world - > clientStillConnecting ) {
if ( io - > hoverObstructed & & ( io - > mouse . leftClick | | io - > mouse . rightClick ) )
rightClickPopupLocation = std : : nullopt ;
if ( rightClickPopupLocation . has_value ( ) ) {
if ( ! main . world - > drawProg . right_click_popup_gui ( rightClickPopupLocation . value ( ) ) )
rightClickPopupLocation = std : : nullopt ;
}
2025-07-06 20:43:53 +03:00
2026-01-09 21:56:58 +03:00
if ( io - > hoverObstructed & & io - > mouse . rightClick ) // This runs specifically if the paint popup has the cursor
2025-12-01 17:33:14 +03:00
rightClickPopupLocation = std : : nullopt ;
}
2025-07-06 20:43:53 +03:00
end_gui ( ) ;
2025-09-27 17:59:56 +03:00
2025-07-06 20:43:53 +03:00
justAssignedColorLeft = false ;
justAssignedColorRight = false ;
if ( ! optionsMenuOpen | | generalSettingsOptions ! = GSETTINGS_KEYBINDS )
keybindWaiting = std : : nullopt ;
2025-09-27 17:59:56 +03:00
calculate_final_gui_scale ( ) ;
2025-07-06 20:43:53 +03:00
}
2026-01-18 13:39:49 +03:00
bool Toolbar : : app_close_requested ( ) {
for ( auto & w : main . worlds ) {
if ( w - > should_ask_before_closing ( ) )
add_world_to_close_popup_data ( w ) ;
}
closePopupData . closeAppWhenDone = true ;
return closePopupData . worldsToClose . empty ( ) ;
}
void Toolbar : : add_world_to_close_popup_data ( const std : : shared_ptr < World > & w ) {
auto it = std : : find_if ( closePopupData . worldsToClose . begin ( ) , closePopupData . worldsToClose . end ( ) , [ & ] ( auto & wStruct ) {
return wStruct . w . lock ( ) = = w ;
} ) ;
if ( it = = closePopupData . worldsToClose . end ( ) )
closePopupData . worldsToClose . emplace_back ( w , true ) ;
}
void Toolbar : : close_popup_gui ( ) {
std : : erase_if ( closePopupData . worldsToClose , [ ] ( auto & wPair ) {
return wPair . w . expired ( ) ;
} ) ;
CLAY_AUTO_ID ( {
. layout = {
2026-01-20 18:40:14 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 , 600 ) } ,
2026-01-18 13:39:49 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2026-01-26 19:08:36 +03:00
. floating = { . zIndex = 1 , . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2026-01-18 13:39:49 +03:00
} ) {
gui . obstructing_window ( ) ;
gui . push_id ( " Close single file popup gui " ) ;
gui . text_label ( " Files may contain unsaved changes " ) ;
2026-01-20 18:40:14 +03:00
gui . scroll_bar_area ( " close file popup gui scroll area " , false , [ & ] ( float , float , float & ) {
2026-01-18 13:39:49 +03:00
CLAY_AUTO_ID ( {
. layout = {
. childGap = io - > theme - > childGap1 ,
2026-01-20 18:40:14 +03:00
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
2026-01-18 13:39:49 +03:00
} ) {
2026-01-20 18:40:14 +03:00
size_t i = 0 ;
for ( auto & [ w , setToSave ] : closePopupData . worldsToClose ) {
auto wLock = w . lock ( ) ;
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor2 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
} ) {
gui . push_id ( i ) ;
gui . checkbox ( " set to save checkbox " , & setToSave ) ;
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. childGap = 0 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
} ) {
gui . text_label ( wLock - > name ) ;
gui . text_label_light ( wLock - > filePath . empty ( ) ? " Autosave in " + main . documentsPath . string ( ) : wLock - > filePath . string ( ) ) ;
}
gui . pop_id ( ) ;
2026-01-18 13:39:49 +03:00
}
2026-01-20 18:40:14 +03:00
+ + i ;
2026-01-18 13:39:49 +03:00
}
}
2026-01-20 18:40:14 +03:00
} ) ;
2026-01-18 13:39:49 +03:00
if ( gui . text_button_wide ( " Save " , " Save " ) ) {
for ( auto & [ w , setToSave ] : closePopupData . worldsToClose ) {
auto wLock = w . lock ( ) ;
if ( setToSave ) {
if ( wLock - > filePath . empty ( ) )
wLock - > autosave_to_directory ( main . documentsPath ) ;
else
wLock - > save_to_file ( wLock - > filePath ) ;
}
main . set_tab_to_close ( w ) ;
}
closePopupData . worldsToClose . clear ( ) ;
if ( closePopupData . closeAppWhenDone )
main . setToQuit = true ;
}
if ( gui . text_button_wide ( " Discard All " , " Discard All " ) ) {
for ( auto & [ w , setToSave ] : closePopupData . worldsToClose )
main . set_tab_to_close ( w ) ;
closePopupData . worldsToClose . clear ( ) ;
if ( closePopupData . closeAppWhenDone )
main . setToQuit = true ;
}
if ( gui . text_button_wide ( " Cancel " , " Cancel " ) ) {
closePopupData . worldsToClose . clear ( ) ;
closePopupData . closeAppWhenDone = false ;
}
gui . pop_id ( ) ;
}
}
2025-07-06 20:43:53 +03:00
void Toolbar : : save_func ( ) {
if ( main . world - > filePath = = std : : filesystem : : path ( ) )
save_as_func ( ) ;
2025-07-28 18:21:09 +03:00
else
2026-01-18 13:39:49 +03:00
main . world - > save_to_file ( main . world - > filePath ) ;
2025-07-06 20:43:53 +03:00
}
void Toolbar : : save_as_func ( ) {
# ifdef __EMSCRIPTEN__
optionsMenuOpen = true ;
optionsMenuType = SET_DOWNLOAD_NAME ;
# else
2026-01-14 07:33:54 +03:00
open_file_selector ( " Save " , { { " InfiniPaint Canvas " , World : : FILE_EXTENSION } } , [ w = make_weak_ptr ( main . world ) ] ( const std : : filesystem : : path & p , const auto & e ) {
2025-07-06 20:43:53 +03:00
auto world = w . lock ( ) ;
if ( world )
world - > save_to_file ( p ) ;
} , " " , true ) ;
# endif
}
2025-12-01 17:33:14 +03:00
void Toolbar : : paint_popup ( Vector2f popupPos ) {
double newRotationAngle = 0.0 ;
gui . paint_circle_popup_menu ( " paint circle popup " , popupPos , {
. currentRotationAngle = main . world - > drawData . cam . c . rotation ,
. newRotationAngle = & newRotationAngle ,
. selectedColor = main . world - > drawProg . get_foreground_color_ptr ( ) ,
. palette = paletteData . palettes [ paletteData . selectedPalette ] . colors
} ) ;
if ( newRotationAngle ! = main . world - > drawData . cam . c . rotation ) {
main . world - > drawData . cam . c . rotate_about ( main . world - > drawData . cam . c . from_space ( main . window . size . cast < float > ( ) * 0.5f ) , newRotationAngle - main . world - > drawData . cam . c . rotation ) ;
2025-07-06 20:43:53 +03:00
}
}
void Toolbar : : top_toolbar ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-11-01 20:47:34 +03:00
. padding = CLAY_PADDING_ALL ( static_cast < uint16_t > ( io - > theme - > padding1 / 2 ) ) ,
2025-11-01 18:07:39 +03:00
. childGap = static_cast < uint16_t > ( io - > theme - > childGap1 / 2 ) ,
2025-07-06 20:43:53 +03:00
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
2025-11-01 20:47:34 +03:00
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 )
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
gui . push_id ( " menu top toolbar " ) ;
global_log ( ) ;
bool menuPopUpJustOpen = false ;
bool bookmarkMenuPopUpJustOpen = false ;
2025-09-06 19:06:30 +03:00
bool gridMenuPopUpJustOpen = false ;
2025-12-25 17:18:28 +03:00
bool layerMenuPopUpJustOpen = false ;
2025-11-01 16:07:19 +03:00
if ( gui . svg_icon_button_transparent ( " Main Menu Button " , " data/icons/menu.svg " , menuPopUpOpen ) ) {
2026-01-09 10:14:25 +03:00
if ( menuPopUpOpen )
menuPopUpOpen = false ;
else {
menuPopUpOpen = true ;
menuPopUpJustOpen = true ;
}
2025-07-06 20:43:53 +03:00
}
std : : vector < std : : pair < std : : string , std : : string > > tabNames ;
2026-01-18 13:39:49 +03:00
for ( size_t i = 0 ; i < main . worlds . size ( ) ; i + + ) {
auto & w = main . worlds [ i ] ;
bool shouldAddStarNextToName = w - > should_ask_before_closing ( ) & & ! w - > netServer & & ! w - > netClient ;
tabNames . emplace_back ( w - > netObjMan . is_connected ( ) ? " data/icons/network.svg " : " " , w - > name + ( shouldAddStarNextToName ? " * " : " " ) ) ;
}
2025-07-06 20:43:53 +03:00
std : : optional < size_t > closedTab ;
gui . tab_list ( " file tab list " , tabNames , main . worldIndex , closedTab ) ;
2026-01-18 13:39:49 +03:00
if ( closedTab ) {
auto & closedWorldPtr = main . worlds [ closedTab . value ( ) ] ;
if ( closedWorldPtr - > should_ask_before_closing ( ) )
add_world_to_close_popup_data ( closedWorldPtr ) ;
else
main . set_tab_to_close ( closedWorldPtr ) ;
}
2026-01-09 21:56:58 +03:00
if ( ! main . world - > clientStillConnecting ) {
if ( main . world - > netObjMan . is_connected ( ) & & gui . svg_icon_button_transparent ( " Player List Toggle Button " , " data/icons/list.svg " , playerMenuOpen ) )
playerMenuOpen = ! playerMenuOpen ;
if ( gui . svg_icon_button_transparent ( " Menu Undo Button " , " data/icons/undo.svg " ) )
main . world - > undo_with_checks ( ) ;
if ( gui . svg_icon_button_transparent ( " Menu Redo Button " , " data/icons/redo.svg " ) )
main . world - > redo_with_checks ( ) ;
if ( gui . svg_icon_button_transparent ( " Grids Button " , " data/icons/grid.svg " , gridMenu . popupOpen ) ) {
if ( gridMenu . popupOpen )
stop_displaying_grid_menu ( ) ;
else {
gridMenu . popupOpen = true ;
gridMenuPopUpJustOpen = true ;
}
2025-12-25 17:18:28 +03:00
}
2026-01-09 21:56:58 +03:00
if ( gui . svg_icon_button_transparent ( " Layer Menu Button " , " data/icons/layer.svg " , layerMenuPopupOpen ) ) {
if ( layerMenuPopupOpen ) {
main . world - > drawProg . layerMan . listGUI . refresh_gui_data ( ) ;
layerMenuPopupOpen = false ;
}
else {
layerMenuPopupOpen = true ;
layerMenuPopUpJustOpen = true ;
}
2026-01-09 15:04:51 +03:00
}
2026-01-09 21:56:58 +03:00
if ( gui . svg_icon_button_transparent ( " Bookmark Menu Button " , " data/icons/bookmark.svg " , bookmarkMenuPopupOpen ) ) {
if ( bookmarkMenuPopupOpen ) {
main . world - > bMan . refresh_gui_data ( ) ;
bookmarkMenuPopupOpen = false ;
}
else {
bookmarkMenuPopupOpen = true ;
bookmarkMenuPopUpJustOpen = true ;
}
2025-07-06 20:43:53 +03:00
}
2026-01-09 21:56:58 +03:00
if ( gridMenu . popupOpen )
grid_menu ( gridMenuPopUpJustOpen ) ;
if ( bookmarkMenuPopupOpen )
bookmark_menu ( bookmarkMenuPopUpJustOpen ) ;
if ( layerMenuPopupOpen )
layer_menu ( layerMenuPopUpJustOpen ) ;
}
2025-07-06 20:43:53 +03:00
if ( menuPopUpOpen ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-11-01 16:07:19 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 100 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-07-06 20:43:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
2025-11-01 16:07:19 +03:00
. childGap = 1 ,
2025-07-06 20:43:53 +03:00
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . offset = { . x = 0 , . y = static_cast < float > ( io - > theme - > padding1 ) } , . attachPoints = { . element = CLAY_ATTACH_POINT_LEFT_TOP , . parent = CLAY_ATTACH_POINT_LEFT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " new file local " , " New File " ) ) {
2025-07-06 20:43:53 +03:00
main . new_tab ( {
2026-01-01 22:46:02 +03:00
. isClient = false
2025-07-06 20:43:53 +03:00
} , true ) ;
}
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " open file " , " Open " ) )
2026-01-01 22:46:02 +03:00
open_world_file ( false , " " , " " ) ;
2026-01-09 21:56:58 +03:00
if ( ! main . world - > clientStillConnecting ) {
if ( gui . text_button_left_transparent ( " save file " , " Save " ) )
save_func ( ) ;
if ( gui . text_button_left_transparent ( " save as file " , " Save As " ) )
save_as_func ( ) ;
if ( gui . text_button_left_transparent ( " screenshot " , " Take Screenshot " ) )
main . world - > drawProg . switch_to_tool ( DrawingProgramToolType : : SCREENSHOT ) ;
if ( gui . text_button_left_transparent ( " add image or file to canvas " , " Add Image/File to Canvas " ) ) {
# ifdef __EMSCRIPTEN__
static std : : weak_ptr < World > worldWeakPtr ;
worldWeakPtr = make_weak_ptr ( main . world ) ;
emscripten_browser_file : : upload ( " * " , [ ] ( std : : string const & fileName , std : : string const & mimeType , std : : string_view buffer , void * callbackData ) {
if ( ! buffer . empty ( ) ) {
auto world = ( ( std : : weak_ptr < World > * ) callbackData ) - > lock ( ) ;
if ( world )
world - > drawProg . add_file_to_canvas_by_data ( fileName , buffer , world - > main . window . size . cast < float > ( ) / 2.0f ) ;
else
Logger : : get ( ) . log ( " INFO " , " Loading image to canvas that has been destroyed " ) ;
}
} , & worldWeakPtr ) ;
# else
open_file_selector ( " Open File " , { { " Any File " , " * " } } , [ w = make_weak_ptr ( main . world ) ] ( const std : : filesystem : : path & p , const auto & e ) {
auto wLock = w . lock ( ) ;
if ( wLock )
wLock - > drawProg . add_file_to_canvas_by_path ( p . string ( ) , wLock - > main . window . size . cast < float > ( ) / 2.0f , false ) ;
2026-01-05 19:15:42 +03:00
else
Logger : : get ( ) . log ( " INFO " , " Loading image to canvas that has been destroyed " ) ;
2026-01-09 21:56:58 +03:00
} ) ;
# endif
}
if ( main . world - > netObjMan . is_connected ( ) ) {
if ( gui . text_button_left_transparent ( " lobby info " , " Lobby Info " ) ) {
optionsMenuOpen = true ;
optionsMenuType = LOBBY_INFO_MENU ;
}
}
else if ( gui . text_button_left_transparent ( " start hosting " , " Host " ) ) {
serverLocalID = NetLibrary : : get_random_server_local_id ( ) ;
serverToConnectTo = NetLibrary : : get_global_id ( ) + serverLocalID ;
2025-07-06 20:43:53 +03:00
optionsMenuOpen = true ;
2026-01-09 21:56:58 +03:00
optionsMenuType = HOST_MENU ;
}
if ( gui . text_button_left_transparent ( " canvas specific settings " , " Canvas Settings " ) ) {
optionsMenuOpen = true ;
optionsMenuType = CANVAS_SETTINGS_MENU ;
2025-07-06 20:43:53 +03:00
}
}
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " start connecting " , " Connect " ) ) {
2025-07-06 20:43:53 +03:00
serverToConnectTo . clear ( ) ;
optionsMenuOpen = true ;
optionsMenuType = CONNECT_MENU ;
}
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " open options " , " Settings " ) ) {
2025-07-06 20:43:53 +03:00
optionsMenuOpen = true ;
optionsMenuType = GENERAL_SETTINGS_MENU ;
}
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " about menu button " , " About " ) ) {
2025-07-06 20:43:53 +03:00
optionsMenuOpen = true ;
optionsMenuType = ABOUT_MENU ;
}
# ifndef __EMSCRIPTEN__
2026-01-20 18:40:14 +03:00
if ( gui . text_button_left_transparent ( " quit button " , " Quit " ) ) {
if ( main . app_close_requested ( ) )
main . setToQuit = true ;
}
2025-07-06 20:43:53 +03:00
# endif
if ( io - > mouse . leftClick & & ! menuPopUpJustOpen )
menuPopUpOpen = false ;
}
}
gui . pop_id ( ) ;
}
}
void Toolbar : : web_version_welcome ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 700 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
2025-10-01 20:08:01 +03:00
gui . push_id ( " web version welcome gui " ) ;
2025-07-06 20:43:53 +03:00
gui . text_label_centered ( " Welcome to the web version of InfiniPaint! " ) ;
gui . text_label (
R " (This version contains more known issues than the native version of the app. This includes:
- Rare crashes
- If this browser tab is unfocused , or the window is minimized , any InfiniPaint tabs connected online ( whether host or client ) will be disconnected
- 4 GB memory limit . Might be a problem if you ' re uploading many files / images
2025-11-17 16:08:42 +03:00
- Not multithreaded
- Can ' t access local fonts
2025-07-06 20:43:53 +03:00
If you like this app , consider downloading the native version for your system ) " );
if ( gui . text_button_wide ( " got it " , " Got It " ) )
viewWebVersionWelcome = false ;
gui . pop_id ( ) ;
}
}
2026-01-09 21:56:58 +03:00
void Toolbar : : still_connecting_center_message ( ) {
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
gui . text_label ( " Connecting to server... " ) ;
}
}
void Toolbar : : no_layers_being_edited_message ( ) {
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
gui . text_label ( " Select a layer to edit " ) ;
}
}
2025-10-01 20:08:01 +03:00
# ifndef __EMSCRIPTEN__
void Toolbar : : update_notification_check ( ) {
if ( ! updateCheckerData . updateCheckDone ) {
2025-10-01 20:31:03 +03:00
if ( updateCheckerData . checkForUpdates ) {
if ( ! updateCheckerData . versionFile )
updateCheckerData . versionFile = FileDownloader : : download_data_from_url ( UPDATE_NOTIFICATION_URL ) ;
else {
switch ( updateCheckerData . versionFile - > status ) {
case FileDownloader : : DownloadData : : Status : : IN_PROGRESS :
break ;
case FileDownloader : : DownloadData : : Status : : SUCCESS : {
updateCheckerData . updateCheckDone = true ;
std : : optional < VersionNumber > newVersion = version_str_to_version_numbers ( updateCheckerData . versionFile - > str ) ;
2025-11-16 16:39:12 +03:00
std : : optional < VersionNumber > currentVersion = VersionConstants : : CURRENT_VERSION_NUMBER ;
2025-10-01 20:31:03 +03:00
if ( newVersion . has_value ( ) & & currentVersion . has_value ( ) ) {
VersionNumber & newV = newVersion . value ( ) ;
VersionNumber & currentV = currentVersion . value ( ) ;
updateCheckerData . newVersionStr = version_numbers_to_version_str ( newV ) ;
Logger : : get ( ) . log ( " INFO " , " Latest online version is v " + updateCheckerData . newVersionStr ) ;
if ( newV > currentV )
updateCheckerData . showGui = true ;
else if ( newV = = currentV )
Logger : : get ( ) . log ( " INFO " , " Current version is up to date " ) ;
else
Logger : : get ( ) . log ( " INFO " , " Local version has larger version number than the latest online one " ) ;
}
2025-10-01 20:08:01 +03:00
else
2025-10-01 20:31:03 +03:00
Logger : : get ( ) . log ( " INFO " , " Update notification file couldn't be converted to version numbers " ) ;
updateCheckerData . versionFile = nullptr ;
break ;
2025-10-01 20:08:01 +03:00
}
2025-10-01 20:31:03 +03:00
case FileDownloader : : DownloadData : : Status : : FAILURE :
Logger : : get ( ) . log ( " INFO " , " Failed to check for updates " ) ;
updateCheckerData . updateCheckDone = true ;
updateCheckerData . versionFile = nullptr ;
break ;
2025-10-01 20:08:01 +03:00
}
}
}
2025-10-01 20:31:03 +03:00
else
updateCheckerData . updateCheckDone = true ;
2025-10-01 20:08:01 +03:00
}
}
void Toolbar : : update_notification_gui ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-10-01 20:08:01 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 700 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-10-01 20:08:01 +03:00
} ) {
gui . obstructing_window ( ) ;
gui . push_id ( " update notification gui " ) ;
gui . text_label_centered ( " Update v " + updateCheckerData . newVersionStr + " available! " ) ;
if ( gui . text_button_wide ( " download " , " Open download page in web browser " ) ) {
SDL_OpenURL ( UPDATE_DOWNLOAD_URL ) ;
updateCheckerData . showGui = false ;
}
if ( gui . text_button_wide ( " ignore forever " , " Ignore and don't notify again (can be changed in settings) " ) ) {
updateCheckerData . checkForUpdates = false ;
updateCheckerData . showGui = false ;
}
if ( gui . text_button_wide ( " ignore for now " , " Ignore for now " ) )
updateCheckerData . showGui = false ;
gui . pop_id ( ) ;
}
}
# endif
2025-09-06 19:06:30 +03:00
void Toolbar : : grid_menu ( bool justOpened ) {
2025-12-31 11:59:53 +03:00
if ( main . world - > gridMan . grids ) {
gui . push_id ( " grid menu " ) ;
CLAY_AUTO_ID ( {
. layout = {
2026-01-10 13:46:49 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 300 ) , . height = CLAY_SIZING_FIT ( 0 , 600 ) } ,
2025-12-31 11:59:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
. floating = { . offset = { . x = 0 , . y = static_cast < float > ( io - > theme - > padding1 ) } , . attachPoints = { . element = CLAY_ATTACH_POINT_RIGHT_TOP , . parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
gui . obstructing_window ( ) ;
gui . text_label_centered ( " Grids " ) ;
float entryHeight = 25.0f ;
if ( main . world - > gridMan . grids - > empty ( ) )
gui . text_label_centered ( " No grids yet... " ) ;
uint32_t toDelete = std : : numeric_limits < uint32_t > : : max ( ) ;
gui . scroll_bar_many_entries_area ( " grid menu entries " , entryHeight , main . world - > gridMan . grids - > size ( ) , true , [ & ] ( size_t i , bool isListHovered ) {
auto & grid = main . world - > gridMan . grids - > at ( i ) - > obj ;
bool selectedEntry = gridMenu . selectedGrid = = i ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-09-10 15:03:06 +03:00
. layout = {
2025-12-31 11:59:53 +03:00
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIXED ( entryHeight ) } ,
. childGap = 2 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
} ,
. backgroundColor = selectedEntry ? convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) : convert_vec4 < Clay_Color > ( io - > theme - > backColor2 )
2025-09-10 15:03:06 +03:00
} ) {
2025-12-31 11:59:53 +03:00
gui . text_label ( grid - > get_display_name ( ) ) ;
bool miniButtonClicked = false ;
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. childGap = 1 ,
. childAlignment = { . x = CLAY_ALIGN_X_RIGHT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
}
} ) {
if ( gui . svg_icon_button_transparent ( " visibility eye " , grid - > visible ? " data/icons/eyeopen.svg " : " data/icons/eyeclose.svg " , false , entryHeight , false ) ) {
miniButtonClicked = true ;
grid - > visible = ! grid - > visible ;
NetworkingObjects : : generic_serialized_class_send_update_to_all < WorldGrid > ( grid ) ;
}
if ( gui . svg_icon_button_transparent ( " edit pencil " , " data/icons/pencil.svg " , false , entryHeight , false ) ) {
miniButtonClicked = true ;
main . world - > drawProg . modify_grid ( grid ) ;
stop_displaying_grid_menu ( ) ;
}
if ( gui . svg_icon_button_transparent ( " delete trash " , " data/icons/trash.svg " , false , entryHeight , false ) ) {
miniButtonClicked = true ;
toDelete = i ;
}
2025-09-16 16:44:33 +03:00
}
2025-12-31 11:59:53 +03:00
if ( Clay_Hovered ( ) & & io - > mouse . leftClick & & isListHovered & & ! miniButtonClicked ) {
gridMenu . selectedGrid = i ;
if ( io - > mouse . leftClick > = 2 ) {
main . world - > drawProg . modify_grid ( grid ) ;
stop_displaying_grid_menu ( ) ;
}
2025-09-16 16:44:33 +03:00
}
2025-09-06 19:06:30 +03:00
}
2025-12-31 11:59:53 +03:00
} ) ;
if ( toDelete ! = std : : numeric_limits < uint32_t > : : max ( ) )
main . world - > gridMan . remove_grid ( toDelete ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
bool addByEnter = false ;
gui . input_text ( " grid text input " , & gridMenu . newName , true , [ & ] ( GUIStuff : : SelectionHelper & s ) {
addByEnter = s . selected & & io - > key . enter ;
} ) ;
if ( gui . svg_icon_button ( " grid add button " , " data/icons/plusbold.svg " , false , GUIStuff : : GUIManager : : SMALL_BUTTON_SIZE ) | | ( addByEnter & & ! gridMenu . newName . empty ( ) ) ) {
main . world - > gridMan . add_default_grid ( gridMenu . newName ) ;
main . world - > drawProg . modify_grid ( main . world - > gridMan . grids - > at ( main . world - > gridMan . grids - > size ( ) - 1 ) - > obj ) ;
stop_displaying_grid_menu ( ) ;
2025-09-16 16:44:33 +03:00
}
2025-10-06 09:07:11 +03:00
} ) ;
2025-09-16 16:44:33 +03:00
2025-12-31 11:59:53 +03:00
bool dropdownHover = false ;
if ( io - > mouse . leftClick & & ! Clay_Hovered ( ) & & ! justOpened & & ! dropdownHover )
stop_displaying_grid_menu ( ) ;
}
gui . pop_id ( ) ;
2025-09-06 19:06:30 +03:00
}
}
2025-09-12 09:54:49 +03:00
void Toolbar : : stop_displaying_grid_menu ( ) {
gridMenu . newName . clear ( ) ;
gridMenu . popupOpen = false ;
2025-12-10 13:06:45 +03:00
gridMenu . selectedGrid = std : : numeric_limits < uint32_t > : : max ( ) ;
2025-09-12 09:54:49 +03:00
}
2025-07-06 20:43:53 +03:00
void Toolbar : : bookmark_menu ( bool justOpened ) {
gui . push_id ( " bookmark menu " ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2026-01-10 13:46:49 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 300 ) , . height = CLAY_SIZING_FIT ( 0 , 600 ) } ,
2025-07-06 20:43:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . offset = { . x = 0 , . y = static_cast < float > ( io - > theme - > padding1 ) } , . attachPoints = { . element = CLAY_ATTACH_POINT_RIGHT_TOP , . parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
gui . text_label_centered ( " Bookmarks " ) ;
2025-12-21 00:33:04 +03:00
main . world - > bMan . setup_list_gui ( " bookmark menu list " ) ;
2025-12-23 17:17:33 +03:00
if ( io - > mouse . leftClick & & ! Clay_Hovered ( ) & & ! justOpened ) {
bookmarkMenuPopupOpen = false ;
main . world - > bMan . refresh_gui_data ( ) ;
}
2025-07-06 20:43:53 +03:00
}
gui . pop_id ( ) ;
}
2025-12-25 17:18:28 +03:00
void Toolbar : : layer_menu ( bool justOpened ) {
gui . push_id ( " layer menu " ) ;
CLAY_AUTO_ID ( {
. layout = {
2026-01-10 13:46:49 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 300 ) , . height = CLAY_SIZING_FIT ( 0 , 600 ) } ,
2025-12-25 17:18:28 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
. floating = { . offset = { . x = 0 , . y = static_cast < float > ( io - > theme - > padding1 ) } , . attachPoints = { . element = CLAY_ATTACH_POINT_RIGHT_TOP , . parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
gui . obstructing_window ( ) ;
gui . text_label_centered ( " Layers " ) ;
2025-12-25 19:37:41 +03:00
bool hoveringOverDropdown = false ;
main . world - > drawProg . layerMan . listGUI . setup_list_gui ( " layer menu list " , hoveringOverDropdown ) ;
if ( io - > mouse . leftClick & & ! Clay_Hovered ( ) & & ! justOpened & & ! hoveringOverDropdown ) {
2025-12-25 17:18:28 +03:00
layerMenuPopupOpen = false ;
main . world - > drawProg . layerMan . listGUI . refresh_gui_data ( ) ;
}
}
gui . pop_id ( ) ;
}
2025-10-16 09:38:58 +03:00
std : : unique_ptr < skia : : textlayout : : Paragraph > Toolbar : : build_paragraph_from_chat_message ( const ChatMessage & message , float alpha ) {
skia : : textlayout : : ParagraphStyle pStyle ;
pStyle . setTextAlign ( skia : : textlayout : : TextAlign : : kLeft ) ;
skia : : textlayout : : TextStyle tStyle ;
tStyle . setFontSize ( io - > fontSize ) ;
2025-11-16 10:42:01 +03:00
tStyle . setFontFamilies ( main . fonts - > get_default_font_families ( ) ) ;
2025-10-16 09:38:58 +03:00
tStyle . setFontStyle ( SkFontStyle : : Bold ( ) ) ;
2025-11-01 16:07:19 +03:00
tStyle . setForegroundColor ( SkPaint { color_mul_alpha ( message . type = = ChatMessage : : JOIN ? io - > theme - > warningColor : io - > theme - > frontColor1 , alpha ) } ) ;
2025-10-16 09:38:58 +03:00
pStyle . setTextStyle ( tStyle ) ;
2025-11-16 10:42:01 +03:00
skia : : textlayout : : ParagraphBuilderImpl a ( pStyle , io - > fonts - > collection , SkUnicodes : : ICU : : Make ( ) ) ;
2025-10-16 09:38:58 +03:00
if ( message . type = = ChatMessage : : JOIN ) {
std : : string messageName = message . name + " " ;
a . addText ( messageName . c_str ( ) , messageName . length ( ) ) ;
}
else {
std : : string messageName = " [ " + message . name + " ] " ;
a . addText ( messageName . c_str ( ) , messageName . length ( ) ) ;
}
tStyle . setFontStyle ( SkFontStyle : : Normal ( ) ) ;
a . pushStyle ( tStyle ) ;
a . addText ( message . message . c_str ( ) , message . message . length ( ) ) ;
return a . Build ( ) ;
}
2025-07-06 20:43:53 +03:00
void Toolbar : : chat_box ( ) {
2025-10-15 16:33:24 +03:00
constexpr float CHATBOX_WIDTH = 700 ;
2026-01-01 22:46:02 +03:00
if ( main . world - > netObjMan . is_connected ( ) ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. layoutDirection = CLAY_LEFT_TO_RIGHT
} ,
. floating = { . offset = { static_cast < float > ( io - > theme - > padding1 ) , - static_cast < float > ( io - > theme - > padding1 ) } , . attachPoints = { . element = CLAY_ATTACH_POINT_LEFT_BOTTOM , . parent = CLAY_ATTACH_POINT_LEFT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
gui . obstructing_window ( ) ;
gui . push_id ( " chat box open button " ) ;
if ( gui . svg_icon_button ( " Chat Open Button " , " data/icons/chat.svg " , chatBoxState ! = CHATBOXSTATE_CLOSE ) ) {
if ( chatBoxState = = CHATBOXSTATE_CLOSE )
chatBoxState = CHATBOXSTATE_JUSTOPEN ;
else
chatBoxState = CHATBOXSTATE_CLOSE ;
}
gui . pop_id ( ) ;
}
}
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-10-15 16:33:24 +03:00
. sizing = { . width = CLAY_SIZING_FIXED ( CHATBOX_WIDTH ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-07-06 20:43:53 +03:00
. childGap = 0 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_BOTTOM } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. floating = { . offset = { 60 + static_cast < float > ( io - > theme - > padding1 ) , - static_cast < float > ( io - > theme - > padding1 ) } , . attachPoints = { . element = CLAY_ATTACH_POINT_LEFT_BOTTOM , . parent = CLAY_ATTACH_POINT_LEFT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
gui . obstructing_window ( ) ;
gui . push_id ( " chat box " ) ;
if ( chatBoxState = = CHATBOXSTATE_JUSTOPEN | | chatBoxState = = CHATBOXSTATE_OPEN ) {
2025-10-15 16:33:24 +03:00
gui . push_id ( " messages " ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( 0 ) ,
. childGap = 0 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_BOTTOM } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 )
} ) {
2025-10-15 16:33:24 +03:00
int id = 0 ;
2025-07-06 20:43:53 +03:00
for ( auto & chatMessage : main . world - > chatMessages | std : : views : : reverse ) {
2025-10-15 16:33:24 +03:00
gui . push_id ( id ) ;
2025-10-16 09:38:58 +03:00
gui . text_paragraph ( " text " , build_paragraph_from_chat_message ( chatMessage , 1.0f ) , CHATBOX_WIDTH ) ;
2025-10-15 16:33:24 +03:00
gui . pop_id ( ) ;
2025-10-16 09:38:58 +03:00
id + + ;
2025-07-06 20:43:53 +03:00
}
}
2025-10-15 16:33:24 +03:00
gui . pop_id ( ) ;
2025-07-06 20:43:53 +03:00
gui . left_to_right_line_layout ( [ & ] ( ) {
gui . input_text ( " message input " , & chatMessageInput ) ;
if ( gui . text_button ( " send button " , " Send " ) ) {
if ( ! chatMessageInput . empty ( ) )
main . world - > send_chat_message ( chatMessageInput ) ;
}
} ) ;
gui . push_id ( " message input " ) ;
GUIStuff : : GUIManager : : ElementContainer & a = gui . elements [ gui . idStack ] ;
GUIStuff : : TextBox < std : : string > & t = * ( GUIStuff : : TextBox < std : : string > * ) a . elem . get ( ) ;
gui . pop_id ( ) ;
if ( chatBoxState = = CHATBOXSTATE_JUSTOPEN ) {
t . selection . selected = true ;
chatBoxState = CHATBOXSTATE_OPEN ;
}
2025-10-15 16:33:24 +03:00
if ( io - > key . escape )
2025-07-06 20:43:53 +03:00
t . selection . selected = false ;
2025-10-15 16:33:24 +03:00
if ( io - > key . enter ) {
2025-07-06 20:43:53 +03:00
main . world - > send_chat_message ( chatMessageInput ) ;
t . selection . selected = false ;
}
if ( ! t . selection . selected ) {
chatMessageInput . clear ( ) ;
chatBoxState = CHATBOXSTATE_CLOSE ;
}
}
else {
constexpr float DISPLAY_TIME = 8.0f ;
constexpr float FADE_START_TIME = 7.0f ;
2025-10-15 16:33:24 +03:00
gui . push_id ( " messages popup " ) ;
int id = 0 ;
2025-07-06 20:43:53 +03:00
for ( auto & chatMessage : main . world - > chatMessages | std : : views : : reverse ) {
chatMessage . time . update_time_since ( ) ;
if ( chatMessage . time < DISPLAY_TIME ) {
float a = 1.0f - lerp_time < float > ( chatMessage . time , DISPLAY_TIME , FADE_START_TIME ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( 0 ) ,
. childGap = 0 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( color_mul_alpha ( io - > theme - > backColor1 , a ) ) ,
} ) {
2025-10-15 16:33:24 +03:00
gui . push_id ( id ) ;
2025-07-06 20:43:53 +03:00
gui . obstructing_window ( ) ;
2025-10-16 09:38:58 +03:00
gui . text_paragraph ( " text " , build_paragraph_from_chat_message ( chatMessage , a ) , CHATBOX_WIDTH ) ;
2025-10-15 16:33:24 +03:00
gui . pop_id ( ) ;
2025-07-06 20:43:53 +03:00
}
}
2025-10-16 09:38:58 +03:00
id + + ;
2025-07-06 20:43:53 +03:00
}
2025-10-15 16:33:24 +03:00
gui . pop_id ( ) ;
2025-07-06 20:43:53 +03:00
}
gui . pop_id ( ) ;
}
}
void Toolbar : : global_log ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 300 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_RIGHT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. floating = { . offset = { 0 , 10 } , . attachPoints = { . element = CLAY_ATTACH_POINT_RIGHT_TOP , . parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM } , . attachTo = CLAY_ATTACH_TO_PARENT }
} ) {
constexpr float DISPLAY_TIME = 8.0f ;
constexpr float FADE_START_TIME = 7.0f ;
for ( auto & logM : main . logMessages ) {
logM . time . update_time_since ( ) ;
if ( logM . time < DISPLAY_TIME ) {
float a = 1.0f - lerp_time < float > ( logM . time , DISPLAY_TIME , FADE_START_TIME ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 300 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = 0 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( color_mul_alpha ( io - > theme - > backColor1 , a ) ) ,
2025-11-01 20:47:34 +03:00
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 )
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
SkColor4f c { 0 , 0 , 0 , 0 } ;
switch ( logM . color ) {
case LogMessage : : COLOR_NORMAL :
c = io - > theme - > frontColor1 ;
break ;
case LogMessage : : COLOR_ERROR :
2025-11-01 16:07:19 +03:00
c = io - > theme - > errorColor ;
2025-07-06 20:43:53 +03:00
break ;
}
gui . text_label_color ( logM . text , color_mul_alpha ( c , a ) ) ;
}
}
else
break ;
}
}
}
void Toolbar : : drawing_program_gui ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
} ,
} ) {
main . world - > drawProg . toolbar_gui ( ) ;
2025-11-05 18:21:49 +03:00
isUpdatingColorLeft = isUpdatingColorRight = false ;
2025-07-06 20:43:53 +03:00
if ( colorLeft ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-12-01 13:05:59 +03:00
. padding = { . top = 40 , . bottom = 40 }
}
2025-07-06 20:43:53 +03:00
} ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-12-01 13:05:59 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 300 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 )
} ) {
gui . obstructing_window ( ) ;
isUpdatingColorLeft | = gui . color_picker_items ( " colorpickerleft " , colorLeft , true , 300.0f - io - > theme - > padding1 * 2.0f ) ;
bool hoveringOnDropdown = false ;
isUpdatingColorLeft | = color_palette ( " colorpickerleftpalette " , colorLeft , hoveringOnDropdown ) ;
if ( ! Clay_Hovered ( ) & & ! justAssignedColorLeft & & ! hoveringOnDropdown & & io - > mouse . leftClick )
colorLeft = nullptr ;
}
2025-07-06 20:43:53 +03:00
}
}
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) }
}
} ) { }
if ( colorRight ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-12-01 13:05:59 +03:00
. padding = { . top = 40 , . bottom = 40 }
}
2025-07-06 20:43:53 +03:00
} ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-12-01 13:05:59 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 300 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 )
} ) {
gui . obstructing_window ( ) ;
isUpdatingColorRight | = gui . color_picker_items ( " colorpickerright " , colorRight , true , 300.0f - io - > theme - > padding1 * 2.0f ) ;
bool hoveringOnDropdown = false ;
isUpdatingColorRight | = color_palette ( " colorpickerrightpalette " , colorRight , hoveringOnDropdown ) ;
if ( ! Clay_Hovered ( ) & & ! justAssignedColorRight & & ! hoveringOnDropdown & & io - > mouse . leftClick )
colorRight = nullptr ;
}
2025-07-06 20:43:53 +03:00
}
}
main . world - > drawProg . tool_options_gui ( ) ;
}
}
2026-01-07 12:03:59 +03:00
bool Toolbar : : color_palette ( const char * id , Vector4f * color , bool & hoveringOnDropdown ) {
2025-11-05 18:21:49 +03:00
bool isUpdating = false ;
2025-07-06 20:43:53 +03:00
gui . push_id ( id ) ;
auto & palette = paletteData . palettes [ paletteData . selectedPalette ] . colors ;
2025-11-01 16:07:19 +03:00
constexpr float COLOR_BUTTON_SIZE = GUIStuff : : GUIManager : : BIG_BUTTON_SIZE ;
2025-12-01 13:05:59 +03:00
2026-01-07 12:03:59 +03:00
size_t nextID = 0 ;
2025-12-01 13:05:59 +03:00
gui . scroll_bar_area ( " color palette scroll area " , false , [ & ] ( float , float , float & ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-12-01 13:05:59 +03:00
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
2025-07-06 20:43:53 +03:00
. childGap = io - > theme - > childGap1 ,
2025-12-01 13:05:59 +03:00
. layoutDirection = CLAY_TOP_TO_BOTTOM
2025-07-06 20:43:53 +03:00
}
} ) {
2025-12-01 13:05:59 +03:00
size_t i = 0 ;
2025-07-06 20:43:53 +03:00
while ( i < palette . size ( ) ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-12-01 13:05:59 +03:00
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIXED ( COLOR_BUTTON_SIZE ) } ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
2025-07-06 20:43:53 +03:00
}
} ) {
2025-12-01 13:05:59 +03:00
while ( i < palette . size ( ) ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-12-01 13:05:59 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( COLOR_BUTTON_SIZE ) , . height = CLAY_SIZING_FIXED ( COLOR_BUTTON_SIZE ) }
}
} ) {
Vector4f newC = { palette [ i ] . x ( ) , palette [ i ] . y ( ) , palette [ i ] . z ( ) , 1.0f } ;
2026-01-07 12:03:59 +03:00
gui . push_id ( nextID + + ) ;
if ( gui . color_button ( " c " , & newC , ( paletteData . selectedColor = = ( int ) i ) ) ) {
2025-12-01 13:05:59 +03:00
paletteData . selectedColor = ( int ) i ;
// We want to keep the old color's alpha
color - > x ( ) = newC . x ( ) ;
color - > y ( ) = newC . y ( ) ;
color - > z ( ) = newC . z ( ) ;
isUpdating = true ;
}
2026-01-07 12:03:59 +03:00
gui . pop_id ( ) ;
2025-12-01 13:05:59 +03:00
if ( paletteData . selectedColor = = ( int ) i & & ( newC . x ( ) ! = color - > x ( ) | | newC . y ( ) ! = color - > y ( ) | | newC . z ( ) ! = color - > z ( ) ) )
paletteData . selectedColor = - 1 ;
}
i + + ;
if ( i % 6 = = 0 )
break ;
2025-07-06 20:43:53 +03:00
}
}
}
}
2025-12-01 13:05:59 +03:00
} ) ;
2025-07-06 20:43:53 +03:00
if ( paletteData . selectedPalette ! = 0 ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = { . top = 3 , . bottom = 3 } ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
}
} ) {
if ( gui . svg_icon_button ( " addcolor " , " data/icons/plus.svg " , false , COLOR_BUTTON_SIZE ) ) {
palette . emplace_back ( color - > x ( ) , color - > y ( ) , color - > z ( ) ) ;
paletteData . selectedColor = palette . size ( ) - 1 ;
}
if ( gui . svg_icon_button ( " deletecolor " , " data/icons/close.svg " , false , COLOR_BUTTON_SIZE ) ) {
if ( paletteData . selectedColor > = 0 & & paletteData . selectedColor < ( int ) palette . size ( ) ) {
palette . erase ( palette . begin ( ) + paletteData . selectedColor ) ;
paletteData . selectedColor = - 1 ;
}
}
}
}
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = { . top = 3 , . bottom = 3 } ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
}
} ) {
std : : vector < std : : string > paletteNames ;
for ( auto & p : paletteData . palettes )
paletteNames . emplace_back ( p . name ) ;
2025-12-01 13:05:59 +03:00
gui . dropdown_select ( " paletteselector " , & paletteData . selectedPalette , paletteNames , 200.0f , [ & ] ( ) {
2025-07-06 20:43:53 +03:00
hoveringOnDropdown = Clay_Hovered ( ) ;
} ) ;
if ( gui . svg_icon_button ( " paletteadd " , " data/icons/plus.svg " , paletteData . addingPalette , 25.0f ) ) paletteData . addingPalette = ! paletteData . addingPalette ;
if ( paletteData . selectedPalette ! = 0 ) {
if ( gui . svg_icon_button ( " paletteremove " , " data/icons/close.svg " , false , 25.0f ) ) {
paletteData . palettes . erase ( paletteData . palettes . begin ( ) + paletteData . selectedPalette ) ;
paletteData . selectedPalette = 0 ;
}
}
}
if ( paletteData . addingPalette ) {
gui . input_text_field ( " paletteinputname " , " Name " , & paletteData . newPaletteStr ) ;
if ( gui . text_button_wide ( " addpalettebutton " , " Create " ) & & ! paletteData . newPaletteStr . empty ( ) ) {
paletteData . palettes . emplace_back ( ) ;
paletteData . palettes . back ( ) . name = paletteData . newPaletteStr ;
paletteData . selectedPalette = paletteData . palettes . size ( ) - 1 ;
paletteData . addingPalette = false ;
}
}
gui . pop_id ( ) ;
2025-11-05 18:21:49 +03:00
return isUpdating ;
2025-07-06 20:43:53 +03:00
}
void Toolbar : : performance_metrics ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
2026-01-02 20:11:33 +03:00
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_BOTTOM } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
2025-07-06 20:43:53 +03:00
} ,
2026-01-08 16:37:18 +03:00
. floating = { . offset = { - 10 , - 10 } , . attachPoints = { . element = CLAY_ATTACH_POINT_RIGHT_BOTTOM , . parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM } , . pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
2026-01-02 20:11:33 +03:00
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_RIGHT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
2026-01-08 16:37:18 +03:00
. backgroundColor = convert_vec4 < Clay_Color > ( color_mul_alpha ( io - > theme - > backColor1 , 0.7f ) ) ,
2026-01-02 20:11:33 +03:00
} ) {
gui . text_label ( " Undo queue " ) ;
2026-01-02 21:06:13 +03:00
std : : vector < std : : string > undoList = main . world - > undo . get_front_undo_queue_names ( 10 ) ;
for ( const std : : string & u : undoList )
2026-01-02 20:11:33 +03:00
gui . text_label ( u ) ;
}
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_RIGHT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
2026-01-08 16:37:18 +03:00
. backgroundColor = convert_vec4 < Clay_Color > ( color_mul_alpha ( io - > theme - > backColor1 , 0.7f ) ) ,
2026-01-02 20:11:33 +03:00
} ) {
std : : stringstream a ;
a < < " FPS: " < < std : : fixed < < std : : setprecision ( 0 ) < < ( 1.0 / main . deltaTime ) ;
gui . text_label ( a . str ( ) ) ;
gui . text_label ( " Item Count: " + std : : to_string ( main . world - > drawProg . layerMan . total_component_count ( ) ) ) ;
std : : stringstream b ;
b < < " Coord: " < < main . world - > drawData . cam . c . pos . x ( ) . display_int_str ( 5 ) < < " , " < < main . world - > drawData . cam . c . pos . y ( ) . display_int_str ( 5 ) ;
gui . text_label ( b . str ( ) ) ;
std : : stringstream c ;
c < < " Zoom: " < < main . world - > drawData . cam . c . inverseScale . display_int_str ( 5 ) ;
gui . text_label ( c . str ( ) ) ;
std : : stringstream d ;
d < < " Rotation: " < < main . world - > drawData . cam . c . rotation ;
gui . text_label ( d . str ( ) ) ;
}
2025-07-06 20:43:53 +03:00
}
}
void Toolbar : : player_list ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 500 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
gui . push_id ( " client list " ) ;
gui . text_label_centered ( " Player List " ) ;
2025-12-17 14:46:27 +03:00
if ( ! main . world - > clientStillConnecting ) {
2025-07-06 20:43:53 +03:00
gui . left_to_right_line_layout ( [ & ] ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 20 ) , . height = CLAY_SIZING_FIXED ( 20 ) }
} ,
2025-12-17 14:46:27 +03:00
. backgroundColor = convert_vec4 < Clay_Color > ( SkColor4f { main . world - > ownClientData - > get_cursor_color ( ) . x ( ) , main . world - > ownClientData - > get_cursor_color ( ) . y ( ) , main . world - > ownClientData - > get_cursor_color ( ) . z ( ) , 1.0f } ) ,
2025-07-06 20:43:53 +03:00
. cornerRadius = CLAY_CORNER_RADIUS ( 3 )
} ) { }
2025-12-17 14:46:27 +03:00
gui . text_label ( main . world - > ownClientData - > get_display_name ( ) ) ;
2025-07-06 20:43:53 +03:00
} ) ;
2025-12-17 14:46:27 +03:00
size_t num = 0 ;
for ( auto & client : main . world - > clients - > get_data ( ) ) {
if ( client ! = main . world - > ownClientData ) {
gui . push_id ( num + + ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
CLAY_AUTO_ID ( {
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 20 ) , . height = CLAY_SIZING_FIXED ( 20 ) }
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( SkColor4f { client - > get_cursor_color ( ) . x ( ) , client - > get_cursor_color ( ) . y ( ) , client - > get_cursor_color ( ) . z ( ) , 1.0f } ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( 3 )
} ) { }
gui . text_label ( client - > get_display_name ( ) ) ;
CLAY_AUTO_ID ( { . layout = { . sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } } } ) { }
if ( gui . text_button ( " teleport button " , " Jump To " ) )
main . world - > drawData . cam . smooth_move_to ( * main . world , client - > get_cam_coords ( ) , client - > get_window_size ( ) ) ;
} ) ;
gui . pop_id ( ) ;
}
}
2025-07-06 20:43:53 +03:00
}
if ( gui . text_button_wide ( " close list " , " Done " ) )
playerMenuOpen = false ;
gui . pop_id ( ) ;
}
}
2026-01-01 22:46:02 +03:00
void Toolbar : : open_world_file ( bool isClient , const std : : string & netSource , const std : : string & serverLocalID2 ) {
2025-07-06 20:43:53 +03:00
# ifdef __EMSCRIPTEN__
static struct UploadData {
2026-01-01 22:46:02 +03:00
bool iC ;
2025-07-06 20:43:53 +03:00
std : : string nS ;
std : : string sLID ;
MainProgram * main ;
} uploadData ;
2026-01-01 22:46:02 +03:00
uploadData . iC = isClient ;
2025-07-06 20:43:53 +03:00
uploadData . nS = netSource ;
uploadData . sLID = serverLocalID2 ;
uploadData . main = & main ;
emscripten_browser_file : : upload ( " . " + World : : FILE_EXTENSION , [ ] ( std : : string const & fileName , std : : string const & mimeType , std : : string_view buffer , void * callbackData ) {
if ( ! buffer . empty ( ) ) {
UploadData * uD = ( UploadData * ) callbackData ;
uD - > main - > new_tab ( {
2026-01-01 22:46:02 +03:00
. isClient = uD - > iC ,
2025-11-21 08:35:47 +03:00
. filePathSource = std : : filesystem : : path ( fileName ) ,
2025-07-06 20:43:53 +03:00
. netSource = uD - > nS ,
. serverLocalID = uD - > sLID ,
. fileDataBuffer = buffer
} , true ) ;
}
} , & uploadData ) ;
# else
2026-01-14 07:33:54 +03:00
open_file_selector ( " Open " , { { " InfiniPaint Canvas " , World : : FILE_EXTENSION } , { " Any File " , " * " } } , [ & , isClient = isClient , netSource = netSource , serverLocalID2 = serverLocalID2 ] ( const std : : filesystem : : path & p , const auto & e ) {
2025-07-06 20:43:53 +03:00
main . new_tab ( {
2026-01-01 22:46:02 +03:00
. isClient = isClient ,
2025-11-21 08:35:47 +03:00
. filePathSource = p ,
2025-07-06 20:43:53 +03:00
. netSource = netSource ,
. serverLocalID = serverLocalID2
} ) ;
} ) ;
# endif
}
void Toolbar : : options_menu ( ) {
switch ( optionsMenuType ) {
case HOST_MENU :
case CONNECT_MENU : {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-11-29 06:56:06 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 650 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-07-06 20:43:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
if ( optionsMenuType = = HOST_MENU ) {
gui . push_id ( " host menu " ) ;
std : : string oldS = serverToConnectTo ;
gui . input_text_field ( " lobby " , " Lobby " , & serverToConnectTo ) ;
serverToConnectTo = oldS ;
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( gui . text_button_wide ( " copy lobby address " , " Copy Lobby Address " ) )
main . input . set_clipboard_str ( serverToConnectTo ) ;
if ( gui . text_button_wide ( " host file " , " Host " ) ) {
main . world - > start_hosting ( serverToConnectTo , serverLocalID ) ;
optionsMenuOpen = false ;
}
if ( gui . text_button_wide ( " cancel " , " Cancel " ) )
optionsMenuOpen = false ;
} ) ;
gui . pop_id ( ) ;
}
else if ( optionsMenuType = = CONNECT_MENU ) {
gui . push_id ( " connect menu " ) ;
gui . input_text_field ( " lobby " , " Lobby " , & serverToConnectTo ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( gui . text_button_wide ( " connect " , " Connect " ) ) {
if ( serverToConnectTo . length ( ) ! = ( NetLibrary : : LOCALID_LEN + NetLibrary : : GLOBALID_LEN ) )
Logger : : get ( ) . log ( " USERINFO " , " Connect issue: Incorrect address length " ) ;
else if ( serverToConnectTo . substr ( 0 , NetLibrary : : GLOBALID_LEN ) = = NetLibrary : : get_global_id ( ) )
Logger : : get ( ) . log ( " USERINFO " , " Connect issue: Can't connect to your own address " ) ;
else {
main . new_tab ( {
2026-01-01 22:46:02 +03:00
. isClient = true ,
2025-07-06 20:43:53 +03:00
. netSource = serverToConnectTo
} , true ) ;
optionsMenuOpen = false ;
}
}
if ( gui . text_button_wide ( " cancel " , " Cancel " ) )
optionsMenuOpen = false ;
} ) ;
gui . pop_id ( ) ;
}
}
break ;
}
case GENERAL_SETTINGS_MENU : {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 600 ) , . height = CLAY_SIZING_FIXED ( 500 ) } ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . push_id ( " gsettings " ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
}
} ) {
gui . obstructing_window ( ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 150 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
2025-11-01 16:07:19 +03:00
. childGap = 2 ,
2025-07-06 20:43:53 +03:00
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
} ) {
auto oldOption = generalSettingsOptions ;
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " Generalbutton " , " General " , generalSettingsOptions = = GSETTINGS_GENERAL ) ) generalSettingsOptions = GSETTINGS_GENERAL ;
if ( gui . text_button_left_transparent ( " Tabletbutton " , " Tablet " , generalSettingsOptions = = GSETTINGS_TABLET ) ) generalSettingsOptions = GSETTINGS_TABLET ;
if ( gui . text_button_left_transparent ( " Themebutton " , " Theme " , generalSettingsOptions = = GSETTINGS_THEME ) ) generalSettingsOptions = GSETTINGS_THEME ;
if ( gui . text_button_left_transparent ( " Keybindsbutton " , " Keybinds " , generalSettingsOptions = = GSETTINGS_KEYBINDS ) ) generalSettingsOptions = GSETTINGS_KEYBINDS ;
if ( gui . text_button_left_transparent ( " Debugbutton " , " Debug " , generalSettingsOptions = = GSETTINGS_DEBUG ) ) generalSettingsOptions = GSETTINGS_DEBUG ;
2025-07-06 20:43:53 +03:00
if ( oldOption ! = generalSettingsOptions ) {
load_theme ( ) ;
themeData . selectedThemeIndex = std : : nullopt ;
}
}
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
} ) {
2025-11-22 13:00:52 +03:00
gui . scroll_bar_area ( " general settings scroll area " , false , [ & ] ( float , float , float ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
} ) {
switch ( generalSettingsOptions ) {
case GSETTINGS_GENERAL : {
2025-07-20 15:24:44 +03:00
gui . push_id ( " general settings " ) ;
2025-12-01 13:05:59 +03:00
gui . input_text_field ( " display name input " , " Display name " , & main . displayName ) ;
2025-07-08 12:18:55 +03:00
main . update_display_names ( ) ;
2025-08-01 20:07:47 +03:00
SkColor4f newBackColor { main . defaultCanvasBackgroundColor . x ( ) , main . defaultCanvasBackgroundColor . y ( ) , main . defaultCanvasBackgroundColor . z ( ) , 1.0f } ;
gui . color_picker_button_field ( " defaultCanvasBackgroundColor " , " Default canvas background color " , & newBackColor , false ) ;
main . defaultCanvasBackgroundColor = convert_vec3 < Vector3f > ( newBackColor ) ;
2025-07-06 20:43:53 +03:00
# ifndef __EMSCRIPTEN__
2025-12-01 13:05:59 +03:00
gui . checkbox_field ( " native file pick " , " Use native file picker " , & useNativeFilePicker ) ;
gui . checkbox_field ( " update notifications enable " , " Check for updates on startup " , & updateCheckerData . checkForUpdates ) ;
2025-07-06 20:43:53 +03:00
# endif
gui . slider_scalar_field ( " drag zoom slider " , " Drag zoom speed " , & dragZoomSpeed , 0.0 , 1.0 , 3 ) ;
gui . slider_scalar_field ( " scroll zoom slider " , " Scroll zoom speed " , & scrollZoomSpeed , 0.0 , 1.0 , 3 ) ;
2025-10-03 09:11:22 +03:00
gui . checkbox_field ( " flip zoom tool direction " , " Flip zoom tool direction " , & flipZoomToolDirection ) ;
2026-01-26 19:08:36 +03:00
gui . checkbox_field ( " make all tools share same size " , " Make all tools share size " , & main . toolConfig . globalConf . useGlobalRelativeWidth ) ;
2025-07-06 20:43:53 +03:00
gui . input_scalar_field ( " jump transition time " , " Jump transition time " , & jumpTransitionTime , 0.01f , 1000.0f , 2 ) ;
2025-11-23 12:53:50 +03:00
gui . input_scalar_field ( " Max GUI Scale " , " Max GUI Scale " , & guiScale , 0.5f , 5.0f , 1 ) ;
2025-11-20 12:38:28 +03:00
gui . text_label ( " VSync: " ) ;
if ( gui . radio_button_field ( " Vsync On " , " On " , main . window . vsyncValue = = 1 ) )
main . set_vsync_value ( 1 ) ;
if ( gui . radio_button_field ( " Vsync Off " , " Off " , main . window . vsyncValue = = 0 ) )
main . set_vsync_value ( 0 ) ;
if ( gui . radio_button_field ( " Vsync Adaptive " , " Adaptive " , main . window . vsyncValue = = - 1 ) )
main . set_vsync_value ( - 1 ) ;
2025-11-23 20:51:14 +03:00
# ifndef __EMSCRIPTEN__
2025-11-23 12:53:50 +03:00
gui . checkbox_field ( " apply display scale " , " Apply display scale " , & main . window . applyDisplayScale ) ;
2025-11-23 20:51:14 +03:00
# endif
2025-11-23 12:53:50 +03:00
2025-07-20 15:24:44 +03:00
gui . pop_id ( ) ;
2025-07-06 20:43:53 +03:00
break ;
}
case GSETTINGS_TABLET : {
gui . push_id ( " tablet settings " ) ;
gui . checkbox_field ( " pen pressure width " , " Pen pressure affects brush size " , & tabletOptions . pressureAffectsBrushWidth ) ;
gui . slider_scalar_field ( " smoothing time " , " Smoothing sampling time " , & tabletOptions . smoothingSamplingTime , 0.001f , 1.0f , 3 ) ;
gui . input_scalar_field < uint8_t > ( " middle click " , " Middle click pen button " , & tabletOptions . middleClickButton , 1 , 255 ) ;
gui . input_scalar_field < uint8_t > ( " right click " , " Right click pen button " , & tabletOptions . rightClickButton , 1 , 255 ) ;
2025-11-30 16:19:36 +03:00
gui . slider_scalar_field ( " tablet brush minimum size " , " Brush relative minimum size " , & tabletOptions . brushMinimumSize , 0.0f , 1.0f , 3 ) ;
2026-01-16 17:42:00 +03:00
gui . checkbox_field ( " tablet zoom with button method " , " Zoom when pen touching tablet and pen button assigned to middle click is held " , & tabletOptions . zoomWhilePenDownAndButtonHeld ) ;
2025-07-08 15:12:35 +03:00
# ifdef _WIN32
2025-11-20 12:38:28 +03:00
gui . checkbox_field ( " mouse ignore when pen proximity " , " Ignore mouse movement when pen in proximity " , & tabletOptions . ignoreMouseMovementWhenPenInProximity ) ;
2025-07-08 15:12:35 +03:00
# endif
2025-07-06 20:43:53 +03:00
gui . pop_id ( ) ;
break ;
}
case GSETTINGS_THEME : {
gui . push_id ( " theme " ) ;
if ( ! themeData . selectedThemeIndex )
reload_theme_list ( ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( { . layout = {
2025-07-06 20:43:53 +03:00
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT ,
}
} ) {
gui . text_label ( " Theme: " ) ;
size_t changedVal = themeData . selectedThemeIndex . value ( ) ;
gui . dropdown_select ( " dropdownSelectThemes " , & themeData . selectedThemeIndex . value ( ) , themeData . themeDirList ) ;
if ( changedVal ! = themeData . selectedThemeIndex . value ( ) ) {
themeData . themeCurrentlyLoaded = themeData . themeDirList [ themeData . selectedThemeIndex . value ( ) ] ;
reload_theme_list ( ) ;
}
}
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( themeData . selectedThemeIndex ! = 0 & & gui . text_button_wide ( " savethemebutton " , " Save " ) ) {
themeData . themeCurrentlyLoaded = themeData . themeDirList [ themeData . selectedThemeIndex . value ( ) ] ;
save_theme ( ) ;
reload_theme_list ( ) ;
}
if ( gui . text_button_wide ( " saveasthemebutton " , " Save As " , themeData . openedSaveAsMenu ) ) themeData . openedSaveAsMenu = ! themeData . openedSaveAsMenu ;
if ( gui . text_button_wide ( " reloadthemebutton " , " Reload " ) ) {
themeData . themeCurrentlyLoaded = themeData . themeDirList [ themeData . selectedThemeIndex . value ( ) ] ;
reload_theme_list ( ) ;
}
if ( themeData . selectedThemeIndex ! = 0 & & gui . text_button_wide ( " deletethemebutton " , " Delete " ) ) {
try { std : : filesystem : : remove ( main . configPath / " themes " / ( themeData . themeDirList [ themeData . selectedThemeIndex . value ( ) ] + " .json " ) ) ; } catch ( . . . ) { }
themeData . themeCurrentlyLoaded = " Default " ;
reload_theme_list ( ) ;
}
} ) ;
if ( themeData . openedSaveAsMenu ) {
gui . left_to_right_line_layout ( [ & ] ( ) {
gui . text_label ( " Theme name: " ) ;
gui . input_text ( " saveasnewthemename " , & themeData . themeCurrentlyLoaded ) ;
} ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( gui . text_button_wide ( " saveasdone " , " Done " ) ) {
save_theme ( ) ;
reload_theme_list ( ) ;
}
if ( gui . text_button_wide ( " saveascancel " , " Cancel " ) )
themeData . openedSaveAsMenu = false ;
} ) ;
}
gui . text_label ( " Edit theme: " ) ;
2025-11-01 18:07:39 +03:00
gui . text_label_light ( " Note: Changes only remain if theme is saved " ) ;
2025-07-06 20:43:53 +03:00
gui . color_picker_button_field ( " fillColor1 " , " Fill Color 1 " , & io - > theme - > fillColor1 , true ) ;
gui . color_picker_button_field ( " fillColor2 " , " Fill Color 2 " , & io - > theme - > fillColor2 , true ) ;
gui . color_picker_button_field ( " backColor1 " , " Back Color 1 " , & io - > theme - > backColor1 , true ) ;
gui . color_picker_button_field ( " backColor2 " , " Back Color 2 " , & io - > theme - > backColor2 , true ) ;
gui . color_picker_button_field ( " frontColor1 " , " Front Color 1 " , & io - > theme - > frontColor1 , true ) ;
gui . color_picker_button_field ( " frontColor2 " , " Front Color 2 " , & io - > theme - > frontColor2 , true ) ;
2025-11-01 16:07:19 +03:00
gui . color_picker_button_field ( " warningColor " , " Warning Color " , & io - > theme - > warningColor , true ) ;
gui . color_picker_button_field ( " errorColor " , " Error Color " , & io - > theme - > errorColor , true ) ;
2025-07-06 20:43:53 +03:00
//gui.slider_scalar_field("hoverExpandTime", "Hover Expand Time", &io->theme->hoverExpandTime, 0.001f, 1.0f);
gui . input_scalar_field < uint16_t > ( " childGap1 " , " Gap between child elements " , & io - > theme - > childGap1 , 0 , 30 ) ;
gui . input_scalar_field < uint16_t > ( " padding1 " , " Window padding " , & io - > theme - > padding1 , 0 , 30 ) ;
gui . slider_scalar_field < float > ( " windowCorners1 " , " Window corner radius " , & io - > theme - > windowCorners1 , 0 , 30 ) ;
gui . pop_id ( ) ;
break ;
}
case GSETTINGS_KEYBINDS : {
if ( keybindWaiting . has_value ( ) ) {
main . input . stop_key_input ( ) ;
if ( main . input . lastPressedKeybind ) {
2025-07-08 01:41:05 -05:00
unsigned v = keybindWaiting . value ( ) ;
2025-07-06 20:43:53 +03:00
Vector2ui32 newKey = main . input . lastPressedKeybind . value ( ) ;
main . input . keyAssignments . erase ( newKey ) ;
auto f = std : : find_if ( main . input . keyAssignments . begin ( ) , main . input . keyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = v ;
} ) ;
if ( f ! = main . input . keyAssignments . end ( ) )
main . input . keyAssignments . erase ( f ) ;
main . input . keyAssignments . emplace ( newKey , v ) ;
keybindWaiting = std : : nullopt ;
}
}
gui . push_id ( " keybind entries " ) ;
2025-07-08 01:41:05 -05:00
for ( unsigned i = 0 ; i < InputManager : : KEY_ASSIGNABLE_COUNT ; i + + ) {
2025-07-06 20:43:53 +03:00
gui . push_id ( i ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-11-01 18:07:39 +03:00
. padding = CLAY_PADDING_ALL ( 0 ) ,
2025-07-06 20:43:53 +03:00
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
}
} ) {
2026-01-07 12:03:59 +03:00
gui . text_label ( std : : string ( nlohmann : : json ( static_cast < InputManager : : KeyCodeEnum > ( i ) ) ) ) ;
2025-07-06 20:43:53 +03:00
auto f = std : : find_if ( main . input . keyAssignments . begin ( ) , main . input . keyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = i ;
} ) ;
std : : string assignedKeystrokeStr = f ! = main . input . keyAssignments . end ( ) ? main . input . key_assignment_to_str ( f - > first ) : " " ;
if ( gui . text_button_wide ( " keybind button " , assignedKeystrokeStr , keybindWaiting . has_value ( ) & & keybindWaiting . value ( ) = = i ) )
keybindWaiting = i ;
}
gui . pop_id ( ) ;
}
gui . pop_id ( ) ;
break ;
}
case GSETTINGS_DEBUG : {
2025-07-20 15:24:44 +03:00
gui . push_id ( " debug settings menu " ) ;
2025-12-01 13:05:59 +03:00
gui . checkbox_field ( " show performance metrics " , " Show metrics " , & showPerformance ) ;
gui . input_scalar_fields ( " jump transition easing " , " Jump easing " , & jumpTransitionEasing , 4 , - 10.0f , 10.0f , 2 ) ;
2025-07-06 20:43:53 +03:00
# ifndef __EMSCRIPTEN__
gui . input_scalar_field ( " fps cap slider " , " FPS cap " , & main . fpsLimit , 3.0f , 10000.0f ) ;
# endif
2026-01-24 12:31:55 +03:00
gui . text_label_light ( " Cache related settings " ) ;
gui . input_scalar_field < size_t > ( " cache node resolution " , " Cache node resolution " , & DrawingProgramCache : : CACHE_NODE_RESOLUTION , 256 , 8192 ) ;
gui . input_scalar_field < size_t > ( " max cache nodes " , " Maximum cached nodes " , & DrawingProgramCache : : MAXIMUM_DRAW_CACHE_SURFACES , 2 , 10000 ) ;
size_t cacheVRAMConsumptionInMB = ( DrawingProgramCache : : MAXIMUM_DRAW_CACHE_SURFACES // Number of surfaces
* DrawingProgramCache : : CACHE_NODE_RESOLUTION * DrawingProgramCache : : CACHE_NODE_RESOLUTION // Number of pixels per cache surface
* 4 ) // 4 Channels per pixel (RGBA)
/ ( 1024 * 1024 ) ; // Bytes -> Megabytes conversion
gui . text_label_light ( " Cache max VRAM consumption (MB): " + std : : to_string ( cacheVRAMConsumptionInMB ) ) ;
gui . input_scalar_field < size_t > ( " max components in node " , " Maximum components in single node " , & DrawingProgramCache : : MAXIMUM_COMPONENTS_IN_SINGLE_NODE , 2 , 10000 ) ;
gui . input_scalar_field < size_t > ( " components to force cache rebuild " , " Number of components to force cache rebuild " , & DrawingProgramCache : : MINIMUM_COMPONENTS_TO_START_REBUILD , 1 , 1000000 ) ;
2026-01-27 12:40:39 +03:00
gui . input_scalar_field < size_t > ( " maximum frame time to force cache rebuild " , " Maximum frame time to force cache rebuild (ms) " , & DrawingProgramCache : : MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH , 1 , 1000000 ) ;
gui . input_scalar_field < size_t > ( " minimum time to force cache rebuild " , " Minimum time to check cache rebuild (ms) " , & DrawingProgramCache : : MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH , 1 , 1000000 ) ;
2025-07-20 15:24:44 +03:00
gui . pop_id ( ) ;
2025-07-06 20:43:53 +03:00
break ;
}
}
}
} ) ;
if ( gui . text_button_wide ( " done menu " , " Done " ) ) {
main . save_config ( ) ;
load_theme ( ) ;
themeData . selectedThemeIndex = std : : nullopt ;
optionsMenuOpen = false ;
}
}
}
gui . pop_id ( ) ;
}
break ;
}
case LOBBY_INFO_MENU : {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-11-29 06:56:06 +03:00
. sizing = { . width = CLAY_SIZING_FIT ( 650 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-07-06 20:43:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . push_id ( " lobby info menu " ) ;
gui . obstructing_window ( ) ;
2026-01-01 22:46:02 +03:00
if ( ! main . world | | ! main . world - > netObjMan . is_connected ( ) )
2025-07-06 20:43:53 +03:00
optionsMenuOpen = false ;
else {
std : : string oldS = main . world - > netSource ;
gui . input_text_field ( " lobby " , " Lobby " , & main . world - > netSource ) ;
main . world - > netSource = oldS ;
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( gui . text_button_wide ( " copy lobby address " , " Copy Lobby Address " ) )
main . input . set_clipboard_str ( main . world - > netSource ) ;
if ( gui . text_button_wide ( " done " , " Done " ) )
optionsMenuOpen = false ;
} ) ;
}
gui . pop_id ( ) ;
}
break ;
}
2025-08-01 10:58:45 +03:00
case CANVAS_SETTINGS_MENU : {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-08-01 10:58:45 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 500 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-08-01 10:58:45 +03:00
} ) {
gui . push_id ( " canvas settings menu " ) ;
gui . obstructing_window ( ) ;
2025-12-16 23:06:46 +03:00
SkColor4f newColorToSet = main . world - > canvasTheme . get_back_color ( ) ;
2025-08-01 10:58:45 +03:00
gui . color_picker_button_field ( " canvasColor " , " Canvas Color " , & newColorToSet , false ) ;
2025-12-16 23:06:46 +03:00
main . world - > canvasTheme . set_back_color ( convert_vec3 < Vector3f > ( newColorToSet ) ) ;
2025-08-01 10:58:45 +03:00
if ( gui . text_button_wide ( " done " , " Done " ) )
optionsMenuOpen = false ;
gui . pop_id ( ) ;
}
break ;
}
2025-07-06 20:43:53 +03:00
case SET_DOWNLOAD_NAME : {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 500 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . push_id ( " set download name menu " ) ;
gui . obstructing_window ( ) ;
if ( ! main . world )
optionsMenuOpen = false ;
gui . input_text_field ( " file name " , " File Name " , & downloadNameSet ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( gui . text_button_wide ( " download save button " , " Save " ) ) {
2025-07-28 18:21:09 +03:00
main . world - > save_to_file ( downloadNameSet ) ;
2025-07-06 20:43:53 +03:00
optionsMenuOpen = false ;
}
if ( gui . text_button_wide ( " cancel " , " Cancel " ) )
optionsMenuOpen = false ;
} ) ;
gui . pop_id ( ) ;
}
break ;
}
case ABOUT_MENU : {
about_menu_gui ( ) ;
break ;
}
}
}
void Toolbar : : about_menu_gui ( ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-11-01 20:47:34 +03:00
. sizing = { . width = CLAY_SIZING_FIXED ( 700 ) , . height = CLAY_SIZING_FIXED ( 600 ) } ,
2025-07-06 20:43:53 +03:00
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . push_id ( " about menu popup " ) ;
gui . obstructing_window ( ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
gui . push_id ( " Menu Selector " ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIT ( 200 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
}
} ) {
2025-11-22 13:00:52 +03:00
gui . scroll_bar_area ( " About Menu Selector Scroll Area " , true , [ & ] ( float , float , float ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
2025-11-22 13:00:52 +03:00
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) }
2025-07-06 20:43:53 +03:00
}
} ) {
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " infinipaintnoticebutton " , " InfiniPaint " , selectedLicense = = - 1 ) ) selectedLicense = - 1 ;
2025-07-06 20:43:53 +03:00
}
2025-11-08 20:50:23 +03:00
gui . text_label_light_centered ( " Third Party Components " ) ;
2025-07-06 20:43:53 +03:00
for ( int i = 0 ; i < static_cast < int > ( thirdPartyLicenses . size ( ) ) ; i + + ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIT ( 0 ) } ,
2025-11-22 13:00:52 +03:00
. padding = CLAY_PADDING_ALL ( 1 )
2025-07-06 20:43:53 +03:00
}
} ) {
gui . push_id ( i ) ;
2025-11-01 16:07:19 +03:00
if ( gui . text_button_left_transparent ( " noticebutton " , thirdPartyLicenses [ i ] . first , selectedLicense = = i ) ) selectedLicense = i ;
2025-07-06 20:43:53 +03:00
gui . pop_id ( ) ;
}
}
} ) ;
}
gui . pop_id ( ) ;
2025-11-22 13:00:52 +03:00
gui . scroll_bar_area ( " About Menu Text Scroll Area " , false , [ & ] ( float , float , float ) {
2025-07-06 20:43:53 +03:00
gui . text_label_size ( ( selectedLicense = = - 1 ) ? ownLicenseText : thirdPartyLicenses [ selectedLicense ] . second , 0.8f ) ;
} ) ;
} ) ;
if ( gui . text_button_wide ( " done " , " Done " ) )
optionsMenuOpen = false ;
gui . pop_id ( ) ;
}
}
void Toolbar : : reload_theme_list ( ) {
themeData . selectedThemeIndex = 0 ;
themeData . themeDirList . clear ( ) ;
themeData . themeDirList . emplace_back ( " Default " ) ;
std : : filesystem : : path themeDir = main . configPath / " themes " ;
if ( std : : filesystem : : exists ( themeDir ) & & std : : filesystem : : is_directory ( themeDir ) ) {
for ( auto & theme : std : : filesystem : : recursive_directory_iterator ( themeDir ) ) {
std : : string name = theme . path ( ) . stem ( ) . string ( ) ;
if ( name ! = " Default " ) {
themeData . themeDirList . emplace_back ( name ) ;
if ( name = = themeData . themeCurrentlyLoaded )
themeData . selectedThemeIndex = themeData . themeDirList . size ( ) - 1 ;
}
}
}
if ( ! load_theme ( ) )
themeData . selectedThemeIndex = 0 ;
themeData . openedSaveAsMenu = false ;
}
void Toolbar : : file_picker_gui ( ) {
gui . push_id ( " filepicker " ) ;
bool isDoneByDoubleClick = false ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 700 ) , . height = CLAY_SIZING_FIXED ( 500 ) } ,
. padding = CLAY_PADDING_ALL ( io - > theme - > padding1 ) ,
. childGap = io - > theme - > childGap1 ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) ,
. cornerRadius = CLAY_CORNER_RADIUS ( io - > theme - > windowCorners1 ) ,
2025-11-01 20:47:34 +03:00
. floating = { . attachPoints = { . element = CLAY_ATTACH_POINT_CENTER_CENTER , . parent = CLAY_ATTACH_POINT_CENTER_CENTER } , . attachTo = CLAY_ATTACH_TO_PARENT }
2025-07-06 20:43:53 +03:00
} ) {
gui . obstructing_window ( ) ;
gui . text_label_centered ( filePicker . filePickerWindowName ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
if ( gui . svg_icon_button ( " file picker back button " , " data/icons/backarrow.svg " , false , 30.0f ) ) {
filePicker . currentSearchPath = filePicker . currentSearchPath . parent_path ( ) ;
filePicker . refreshEntries = true ;
}
std : : filesystem : : path pathDiff = filePicker . currentSearchPath ;
gui . input_path_field ( " file picker path " , " Path " , & filePicker . currentSearchPath , std : : filesystem : : file_type : : directory ) ;
if ( pathDiff ! = filePicker . currentSearchPath )
filePicker . refreshEntries = true ;
} ) ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_GROW ( 0 ) } ,
. childAlignment = { . x = CLAY_ALIGN_X_CENTER , . y = CLAY_ALIGN_Y_TOP } ,
. layoutDirection = CLAY_TOP_TO_BOTTOM
} ,
. backgroundColor = convert_vec4 < Clay_Color > ( io - > theme - > backColor2 )
} ) {
if ( filePicker . refreshEntries ) {
filePicker . entries . clear ( ) ;
for ( ; ; ) {
try {
for ( const std : : filesystem : : path & entry : std : : filesystem : : directory_iterator ( filePicker . currentSearchPath ) )
filePicker . entries . emplace_back ( entry ) ;
break ;
}
catch ( const std : : exception & e ) {
Logger : : get ( ) . log ( " INFO " , e . what ( ) ) ;
if ( filePicker . currentSearchPath = = main . homePath ) // The home path must exist. If we get errors on the home path, we have a real problem
throw e ;
filePicker . currentSearchPath = main . homePath ;
}
}
std : : vector < std : : string > extensionList = split_string_by_token ( filePicker . extensionFilters [ filePicker . extensionSelected ] , " ; " ) ;
for ( std : : string & s : extensionList ) {
std : : transform ( s . begin ( ) , s . end ( ) , s . begin ( ) , : : tolower ) ;
s . insert ( 0 , " . " ) ;
}
std : : erase_if ( filePicker . entries , [ & ] ( const std : : filesystem : : path & a ) {
if ( extensionList . empty ( ) )
return false ;
if ( extensionList [ 0 ] = = " .* " )
return false ;
std : : string fExtension ;
if ( a . has_extension ( ) ) {
fExtension = a . extension ( ) . string ( ) ;
std : : transform ( fExtension . begin ( ) , fExtension . end ( ) , fExtension . begin ( ) , : : tolower ) ;
}
return std : : filesystem : : is_regular_file ( a ) & & ( fExtension . empty ( ) | | ! std : : ranges : : contains ( extensionList , fExtension ) ) ;
} ) ;
std : : sort ( filePicker . entries . begin ( ) , filePicker . entries . end ( ) , [ & ] ( const std : : filesystem : : path & a , const std : : filesystem : : path & b ) {
bool aDir = std : : filesystem : : is_directory ( a ) ;
bool bDir = std : : filesystem : : is_directory ( b ) ;
const std : : string & aStr = a . string ( ) ;
const std : : string & bStr = b . string ( ) ;
if ( aDir & & ! bDir )
return true ;
if ( ! aDir & & bDir )
return false ;
return std : : lexicographical_compare ( aStr . begin ( ) , aStr . end ( ) , bStr . begin ( ) , bStr . end ( ) ) ;
} ) ;
filePicker . refreshEntries = false ;
}
float entryHeight = 25.0f ;
2025-11-22 13:00:52 +03:00
gui . scroll_bar_many_entries_area ( " file picker entries " , entryHeight , filePicker . entries . size ( ) , true , [ & ] ( size_t i , bool isListHovered ) {
2025-07-06 20:43:53 +03:00
const std : : filesystem : : path & entry = filePicker . entries [ i ] ;
bool selectedEntry = filePicker . currentSelectedPath = = entry ;
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_GROW ( 0 ) , . height = CLAY_SIZING_FIXED ( entryHeight ) } ,
2025-11-01 16:07:19 +03:00
. childGap = 1 ,
2025-07-06 20:43:53 +03:00
. childAlignment = { . x = CLAY_ALIGN_X_LEFT , . y = CLAY_ALIGN_Y_CENTER } ,
. layoutDirection = CLAY_LEFT_TO_RIGHT
} ,
. backgroundColor = selectedEntry ? convert_vec4 < Clay_Color > ( io - > theme - > backColor1 ) : convert_vec4 < Clay_Color > ( io - > theme - > backColor2 )
} ) {
2025-12-02 16:09:30 +03:00
CLAY_AUTO_ID ( {
2025-07-06 20:43:53 +03:00
. layout = {
. sizing = { . width = CLAY_SIZING_FIXED ( 20 ) , . height = CLAY_SIZING_FIXED ( 20 ) }
} ,
} ) {
if ( std : : filesystem : : is_directory ( entry ) )
gui . svg_icon ( " folder icon " , " data/icons/folder.svg " , selectedEntry ) ;
else
gui . svg_icon ( " file icon " , " data/icons/file.svg " , selectedEntry ) ;
}
gui . text_label ( entry . filename ( ) . string ( ) ) ;
if ( Clay_Hovered ( ) & & io - > mouse . leftClick & & isListHovered ) {
if ( selectedEntry & & io - > mouse . leftClick > = 2 ) {
if ( std : : filesystem : : is_directory ( entry ) ) {
filePicker . currentSearchPath = entry ;
filePicker . refreshEntries = true ;
}
else if ( std : : filesystem : : is_regular_file ( entry ) )
isDoneByDoubleClick = true ;
}
else {
filePicker . currentSelectedPath = entry ;
if ( std : : filesystem : : is_regular_file ( entry ) )
filePicker . fileName = entry . filename ( ) . string ( ) ;
}
}
}
} ) ;
}
gui . left_to_right_line_layout ( [ & ] ( ) {
gui . input_text ( " filepicker filename " , & filePicker . fileName ) ;
size_t oldExtensionSelected = filePicker . extensionSelected ;
gui . dropdown_select ( " filepicker select type " , & filePicker . extensionSelected , filePicker . extensionFilters ) ;
if ( oldExtensionSelected ! = filePicker . extensionSelected )
filePicker . refreshEntries = true ;
} ) ;
gui . left_to_right_line_layout ( [ & ] ( ) {
std : : filesystem : : path pathToRet = std : : filesystem : : path ( ) ;
if ( gui . text_button_wide ( " filepicker done " , " Done " ) | | isDoneByDoubleClick ) {
if ( ! filePicker . fileName . empty ( ) ) {
pathToRet = filePicker . currentSearchPath / filePicker . fileName ;
filePicker . postSelectionFunc ( pathToRet , filePicker . extensionFiltersComplete [ filePicker . extensionSelected ] ) ;
}
filePicker . isOpen = false ;
}
if ( gui . text_button_wide ( " filepicker cancel " , " Cancel " ) ) {
filePicker . isOpen = false ;
}
} ) ;
}
gui . pop_id ( ) ;
}
2025-10-14 11:27:55 +03:00
void Toolbar : : initialize_io_before_update ( ) {
2025-07-06 20:43:53 +03:00
io - > mouse . leftClick = main . input . mouse . leftClicks ;
io - > mouse . rightClick = main . input . mouse . rightClicks ;
io - > mouse . leftHeld = main . input . mouse . leftDown ;
io - > mouse . rightHeld = main . input . mouse . rightDown ;
2025-08-17 16:49:30 +03:00
io - > mouse . globalPos = main . input . mouse . pos / final_gui_scale ( ) ;
2025-07-06 20:43:53 +03:00
io - > mouse . scroll = main . input . mouse . scrollAmount ;
io - > deltaTime = main . deltaTime ;
io - > key . left = main . input . key ( InputManager : : KEY_TEXT_LEFT ) . repeat ;
io - > key . right = main . input . key ( InputManager : : KEY_TEXT_RIGHT ) . repeat ;
io - > key . up = main . input . key ( InputManager : : KEY_TEXT_UP ) . repeat ;
io - > key . down = main . input . key ( InputManager : : KEY_TEXT_DOWN ) . repeat ;
io - > key . leftShift = main . input . key ( InputManager : : KEY_TEXT_SHIFT ) . held ;
io - > key . leftCtrl = main . input . key ( InputManager : : KEY_TEXT_CTRL ) . held ;
io - > key . enter = main . input . key ( InputManager : : KEY_TEXT_ENTER ) . repeat ;
2025-10-15 16:33:24 +03:00
io - > key . escape = main . input . key ( InputManager : : KEY_GENERIC_ESCAPE ) . repeat ;
2025-11-10 14:45:51 +03:00
io - > richTextBoxEdit = [ & ] ( std : : shared_ptr < RichText : : TextBox > t , std : : shared_ptr < RichText : : TextBox : : Cursor > c ) {
2025-12-02 09:53:03 +03:00
main . input . text . set_rich_text_box_input ( t , c , false ) ;
2025-07-06 20:43:53 +03:00
} ;
2025-10-14 11:27:55 +03:00
if ( io - > acceptingTextInput )
main . input . text_input_silence_everything ( ) ;
io - > acceptingTextInput = false ;
}
void Toolbar : : start_gui ( ) {
2025-07-06 20:43:53 +03:00
gui . windowPos = Vector2f { 0.0f , 0.0f } ;
2025-09-27 17:59:56 +03:00
gui . windowSize = main . window . size . cast < float > ( ) / final_gui_scale ( ) ;
2025-07-06 20:43:53 +03:00
gui . io = io ;
2025-10-14 11:27:55 +03:00
io - > hoverObstructed = false ;
2025-07-06 20:43:53 +03:00
gui . begin ( ) ;
}
2025-08-17 16:49:30 +03:00
float Toolbar : : final_gui_scale ( ) {
2025-09-27 17:59:56 +03:00
return finalCalculatedGuiScale ;
}
void Toolbar : : calculate_final_gui_scale ( ) {
2025-11-01 18:07:39 +03:00
Vector2f maxWindowSizeBeforeForcedFit = final_gui_scale_not_fit ( ) * Vector2f { 700.0f , 700.0f } ;
2025-09-27 17:59:56 +03:00
Vector2f fitRatio = { main . window . size . x ( ) / maxWindowSizeBeforeForcedFit . x ( ) , main . window . size . y ( ) / maxWindowSizeBeforeForcedFit . y ( ) } ;
finalCalculatedGuiScale = final_gui_scale_not_fit ( ) * std : : min ( std : : min ( fitRatio . x ( ) , fitRatio . y ( ) ) , 1.0f ) ;
}
float Toolbar : : final_gui_scale_not_fit ( ) {
2025-11-23 12:53:50 +03:00
return guiScale * main . get_scale_and_density_factor_gui ( ) ;
2025-08-17 16:49:30 +03:00
}
2025-07-06 20:43:53 +03:00
void Toolbar : : end_gui ( ) {
gui . end ( ) ;
if ( io - > clipboard . textOut )
main . input . set_clipboard_str ( * io - > clipboard . textOut ) ;
}
void Toolbar : : draw ( SkCanvas * canvas ) {
if ( ! main . takingScreenshot ) {
canvas - > save ( ) ;
2025-08-17 16:49:30 +03:00
canvas - > scale ( final_gui_scale ( ) , final_gui_scale ( ) ) ;
2025-07-06 20:43:53 +03:00
gui . draw ( canvas ) ;
canvas - > restore ( ) ;
}
}