2026-05-11 23:06:39 +03:00
/*
* InfiniPaint
* Copyright ( C ) 2025 - 2026 Yousef Khadadeh
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
*/
2025-12-25 17:18:28 +03:00
# include "DrawingProgramLayerManager.hpp"
2026-10-25 18:07:47 +01:00
# include "FogStripper.hpp"
2025-12-25 17:18:28 +03:00
# include "../../World.hpp"
2026-01-04 16:50:47 +03:00
# include "Helpers/NetworkingObjects/NetObjID.hpp"
2025-12-30 19:46:08 +03:00
# include "Helpers/NetworkingObjects/NetObjOrderedList.hpp"
2026-01-02 10:29:50 +03:00
# include "Helpers/NetworkingObjects/NetObjOwnerPtr.decl.hpp"
2026-01-04 16:50:47 +03:00
# include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
2026-01-01 22:46:02 +03:00
# include "Helpers/Parallel.hpp"
2026-01-02 09:22:52 +03:00
# include "../../CanvasComponents/CanvasComponentContainer.hpp"
# include "../../CanvasComponents/CanvasComponentAllocator.hpp"
# include "../../CanvasComponents/CanvasComponent.hpp"
2026-01-02 10:29:50 +03:00
# include <chrono>
2026-10-25 18:07:47 +01:00
# include <functional>
2025-12-25 17:18:28 +03:00
DrawingProgramLayerManager : : DrawingProgramLayerManager ( DrawingProgram & drawProg ) :
drawP ( drawProg ) , // initialize drawP first for next objects to be initialized properly
listGUI ( * this )
{ }
2026-01-01 22:46:02 +03:00
void DrawingProgramLayerManager : : server_init_no_file ( ) {
layerTreeRoot = drawP . world . netObjMan . make_obj_from_ptr < DrawingProgramLayerListItem > ( new DrawingProgramLayerListItem ( drawP . world . netObjMan , " ROOT " , true ) ) ;
layerTreeRoot - > get_folder ( ) . set_component_list_callbacks ( * this ) ; // Only need to call set_component_list_callbacks on root, and the rest will get the callbacks set as well
editingLayer = layerTreeRoot - > get_folder ( ) . folderList - > emplace_back_direct ( layerTreeRoot - > get_folder ( ) . folderList , drawP . world . netObjMan , " First Layer " , false ) - > obj ;
2025-12-25 17:18:28 +03:00
}
2025-12-31 16:30:56 +03:00
void DrawingProgramLayerManager : : scale_up ( const WorldScalar & scaleUpAmount ) {
layerTreeRoot - > scale_up ( scaleUpAmount ) ;
}
2025-12-25 17:18:28 +03:00
void DrawingProgramLayerManager : : write_components_server ( cereal : : PortableBinaryOutputArchive & a ) {
2026-10-25 18:07:47 +01:00
auto hidden = FogStripper : : compute_hidden_component_ids ( * this ) ;
clientHiddenComponentIds = hidden ;
a ( layerTreeRoot . get_net_id ( ) ) ;
layerTreeRoot - > write_stripped_constructor ( a , hidden ) ;
2025-12-25 17:18:28 +03:00
}
void DrawingProgramLayerManager : : read_components_client ( cereal : : PortableBinaryInputArchive & a ) {
layerTreeRoot = drawP . world . netObjMan . read_create_message < DrawingProgramLayerListItem > ( a , nullptr ) ;
2026-01-08 14:02:37 +03:00
disable_add_to_cache_and_commit_update_block ( [ & ] ( ) {
layerTreeRoot - > set_component_list_callbacks ( * this ) ;
} ) ;
2026-01-01 22:46:02 +03:00
auto flattenedCompList = get_flattened_component_list ( ) ;
parallel_loop_container ( flattenedCompList , [ & drawP = drawP ] ( CanvasComponentContainer : : ObjInfo * comp ) {
comp - > obj - > commit_update_dont_invalidate_cache ( drawP ) ;
} ) ;
drawP . rebuild_cache ( ) ;
2025-12-31 11:59:53 +03:00
editingLayer = layerTreeRoot - > get_folder ( ) . get_initial_editing_layer ( ) ;
2026-10-25 18:07:47 +01:00
ensure_editing_layer_is_editable ( ) ;
2025-12-26 10:10:19 +03:00
}
bool DrawingProgramLayerManager : : is_a_layer_being_edited ( ) {
return ! editingLayer . expired ( ) ;
}
2025-12-29 19:57:36 +03:00
uint32_t DrawingProgramLayerManager : : edited_layer_component_count ( ) {
return editingLayer . lock ( ) - > get_layer ( ) . components - > size ( ) ;
}
2025-12-31 11:42:16 +03:00
uint32_t DrawingProgramLayerManager : : total_component_count ( ) {
if ( layerTreeRoot )
return layerTreeRoot - > get_component_count ( ) ;
return 0 ;
}
2025-12-30 19:46:08 +03:00
CanvasComponentContainer : : ObjInfoIterator DrawingProgramLayerManager : : get_edited_layer_end_iterator ( ) {
return editingLayer . lock ( ) - > get_layer ( ) . components - > end ( ) ;
}
2025-12-26 10:10:19 +03:00
void DrawingProgramLayerManager : : draw ( SkCanvas * canvas , const DrawData & drawData ) {
2026-01-10 22:47:21 +03:00
if ( layerTreeRoot )
layerTreeRoot - > draw ( canvas , drawData ) ;
2025-12-25 17:18:28 +03:00
}
2025-12-26 19:52:36 +03:00
2025-12-30 19:46:08 +03:00
std : : vector < CanvasComponentContainer : : ObjInfo * > DrawingProgramLayerManager : : get_flattened_component_list ( ) const {
std : : vector < CanvasComponentContainer : : ObjInfo * > toRet ;
2025-12-26 19:52:36 +03:00
layerTreeRoot - > get_flattened_component_list ( toRet ) ;
return toRet ;
}
2025-12-28 17:34:33 +03:00
2025-12-29 19:57:36 +03:00
std : : vector < DrawingProgramLayerListItem * > DrawingProgramLayerManager : : get_flattened_layer_list ( ) {
std : : vector < DrawingProgramLayerListItem * > toRet ;
layerTreeRoot - > get_flattened_layer_list ( toRet ) ;
return toRet ;
}
2025-12-28 17:34:33 +03:00
bool DrawingProgramLayerManager : : layer_tree_root_exists ( ) {
return layerTreeRoot ;
}
const DrawingProgramLayerListItem & DrawingProgramLayerManager : : get_layer_root ( ) {
return * layerTreeRoot ;
}
2025-12-30 19:46:08 +03:00
bool DrawingProgramLayerManager : : component_passes_layer_selector ( CanvasComponentContainer : : ObjInfo * c , LayerSelector layerSelector ) {
2026-10-25 18:07:47 +01:00
if ( ! c | | ! c - > obj | | ! c - > obj - > parentLayer )
return false ;
if ( ! can_current_peer_edit_layer ( c - > obj - > parentLayer ) )
return false ;
2025-12-28 17:34:33 +03:00
switch ( layerSelector ) {
case LayerSelector : : ALL_VISIBLE_LAYERS :
return c - > obj - > parentLayer - > get_visible ( ) ;
case LayerSelector : : LAYER_BEING_EDITED :
return is_a_layer_being_edited ( ) & & c - > obj - > parentLayer = = editingLayer . lock ( ) . get ( ) ;
}
return false ;
}
2025-12-29 19:57:36 +03:00
2026-01-20 11:59:03 +03:00
void DrawingProgramLayerManager : : push_components_to ( const std : : vector < CanvasComponentContainer : : ObjInfo * > & objs , bool beginIfTrueEndIfFalse ) {
using namespace NetworkingObjects ;
if ( objs . empty ( ) )
return ;
struct MoveUndoLayerData {
std : : vector < uint32_t > oldPos ;
std : : vector < WorldUndoManager : : UndoObjectID > objs ;
} ;
std : : unordered_map < WorldUndoManager : : UndoObjectID , MoveUndoLayerData > toMoveMapUndo ;
auto & undoMan = drawP . world . undo ;
2026-06-02 21:57:53 +03:00
drawP . world . send_reliable_multi_command_to_all ( [ & ] ( ) {
2026-01-20 11:59:03 +03:00
std : : unordered_map < DrawingProgramLayerListItem * , std : : vector < CanvasComponentContainer : : ObjInfo * > > toEraseMap ;
std : : vector < NetObjOwnerPtr < DrawingProgramLayerListItem > > toInsertObjPtrs ;
DrawingProgramLayerListItem * parentLayer = nullptr ;
std : : vector < CanvasComponentContainer : : ObjInfoIterator > toEraseList ;
auto commitMoveOnLayer = [ & ] ( ) {
if ( toEraseList . empty ( ) )
return ;
{
MoveUndoLayerData moveUndoData ;
uint32_t i = 0 ;
for ( auto & e : toEraseList ) {
moveUndoData . objs . emplace_back ( drawP . world . undo . get_undoid_from_netid ( e - > obj . get_net_id ( ) ) ) ;
moveUndoData . oldPos . emplace_back ( e - > pos - i ) ;
+ + i ;
}
toMoveMapUndo [ undoMan . get_undoid_from_netid ( parentLayer - > get_layer ( ) . components . get_net_id ( ) ) ] = moveUndoData ;
}
std : : vector < NetObjOwnerPtr < CanvasComponentContainer > > erasedObjects ;
auto & components = parentLayer - > get_layer ( ) . components ;
components - > erase_list ( components , toEraseList , & erasedObjects ) ;
std : : vector < std : : pair < CanvasComponentContainer : : ObjInfoIterator , NetObjOwnerPtr < CanvasComponentContainer > > > toInsert ;
auto insertIt = beginIfTrueEndIfFalse ? components - > begin ( ) : components - > end ( ) ;
for ( auto & e : erasedObjects ) {
toInsert . emplace_back ( insertIt , std : : move ( e ) ) ;
2026-01-20 16:16:33 +03:00
toInsert . back ( ) . second . reassign_ids ( ) ;
2026-01-20 11:59:03 +03:00
}
2026-02-10 13:12:53 +03:00
components - > insert_sorted_list_and_send_create ( components , toInsert ) ;
2026-02-02 13:36:52 +03:00
toEraseList . clear ( ) ;
2026-01-20 11:59:03 +03:00
} ;
for ( auto & obj : objs ) {
if ( parentLayer ! = obj - > obj - > parentLayer ) {
commitMoveOnLayer ( ) ;
parentLayer = obj - > obj - > parentLayer ;
}
toEraseList . emplace_back ( obj - > obj - > objInfo ) ;
}
commitMoveOnLayer ( ) ;
2026-06-02 21:57:53 +03:00
} ) ;
2026-01-20 11:59:03 +03:00
class MoveComponentsUndoAction : public WorldUndoAction {
public :
MoveComponentsUndoAction ( std : : unordered_map < WorldUndoManager : : UndoObjectID , MoveUndoLayerData > initToMoveMapUndo ) :
toMoveMapUndo ( std : : move ( initToMoveMapUndo ) )
{ }
std : : string get_name ( ) const override {
return " Move Components " ;
}
bool undo ( WorldUndoManager & undoMan ) override {
return undo_redo ( undoMan ) ;
}
bool redo ( WorldUndoManager & undoMan ) override {
return undo_redo ( undoMan ) ;
}
bool undo_redo ( WorldUndoManager & undoMan ) {
struct MoveLayerData {
std : : vector < uint32_t > * oldPos ;
std : : vector < NetObjID > objs ;
} ;
std : : unordered_map < NetObjID , MoveLayerData > toMoveObjs ;
{
for ( auto & [ undoID , moveUndoData ] : toMoveMapUndo ) {
std : : optional < NetObjID > netID = undoMan . get_netid_from_undoid ( undoID ) ;
if ( ! netID . has_value ( ) )
return false ;
auto & moveMapEntry = toMoveObjs [ netID . value ( ) ] ;
moveMapEntry . oldPos = & moveUndoData . oldPos ;
if ( ! undoMan . fill_netid_list_from_undoid_list ( moveMapEntry . objs , moveUndoData . objs ) )
return false ;
}
}
2026-05-28 21:55:30 +03:00
{
2026-01-20 11:59:03 +03:00
for ( auto & [ compNetID , moveData ] : toMoveObjs ) {
auto components = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < CanvasComponentContainer : : NetList > ( compNetID ) ;
std : : vector < std : : pair < CanvasComponentContainer : : ObjInfoIterator , NetObjOwnerPtr < CanvasComponentContainer > > > toInsert ;
std : : vector < NetObjOrderedListIterator < CanvasComponentContainer > > toErase = components - > get_list ( moveData . objs ) ;
2026-05-28 21:55:30 +03:00
components - > sort_netobj_ordered_list_iterator_list ( components , toErase ) ; // There's a chance that objects could have changed order on the client side, so sort them
2026-02-10 13:12:53 +03:00
2026-01-20 11:59:03 +03:00
std : : vector < NetObjOwnerPtr < CanvasComponentContainer > > erasedObjs ;
std : : vector < uint32_t > newPos ;
{
uint32_t i = 0 ;
for ( auto & it : toErase ) {
newPos . emplace_back ( it - > pos - i ) ;
+ + i ;
}
}
components - > erase_list ( components , toErase , & erasedObjs ) ;
std : : vector < NetObjOrderedListIterator < CanvasComponentContainer > > insertIteratorList = components - > at_ordered_indices ( * moveData . oldPos ) ;
2026-01-20 16:16:33 +03:00
for ( uint32_t i = 0 ; i < toErase . size ( ) ; i + + ) {
2026-01-20 11:59:03 +03:00
toInsert . emplace_back ( insertIteratorList [ i ] , std : : move ( erasedObjs [ i ] ) ) ;
2026-01-20 16:16:33 +03:00
toInsert . back ( ) . second . reassign_ids ( ) ;
}
2026-02-10 13:12:53 +03:00
components - > insert_sorted_list_and_send_create ( components , toInsert ) ;
2026-01-20 11:59:03 +03:00
* moveData . oldPos = newPos ;
}
2026-05-28 21:55:30 +03:00
}
2026-01-20 11:59:03 +03:00
return true ;
}
~ MoveComponentsUndoAction ( ) { }
std : : unordered_map < WorldUndoManager : : UndoObjectID , MoveUndoLayerData > toMoveMapUndo ;
} ;
undoMan . push ( std : : make_unique < MoveComponentsUndoAction > ( std : : move ( toMoveMapUndo ) ) ) ;
}
2025-12-30 19:46:08 +03:00
std : : vector < CanvasComponentContainer : : ObjInfoIterator > DrawingProgramLayerManager : : add_many_components_to_layer_being_edited ( const std : : vector < std : : pair < CanvasComponentContainer : : ObjInfoIterator , CanvasComponentContainer * > > & newObjs ) {
2026-01-04 16:50:47 +03:00
if ( ! newObjs . empty ( ) ) {
auto editLayerPtr = editingLayer . lock ( ) ;
if ( ! editLayerPtr )
2026-01-05 19:15:42 +03:00
throw std : : runtime_error ( " [DrawingProgramLayerManager::add_many_components_to_layer_being_edited] No layer selected! " ) ;
2026-10-25 18:07:47 +01:00
if ( ! can_current_peer_edit_layer ( editLayerPtr . get ( ) ) ) {
for ( auto & [ it , obj ] : newObjs )
delete obj ;
return { } ;
}
2026-02-10 13:12:53 +03:00
auto toRet = editLayerPtr - > get_layer ( ) . components - > insert_sorted_list_and_send_create ( editLayerPtr - > get_layer ( ) . components , newObjs ) ;
2026-01-04 16:50:47 +03:00
add_undo_place_components ( editingLayer . lock ( ) . get ( ) , toRet ) ;
2026-01-07 12:28:04 +03:00
return toRet ;
2026-01-04 16:50:47 +03:00
}
return { } ;
2025-12-29 19:57:36 +03:00
}
2026-05-26 17:08:43 +03:00
std : : vector < CanvasComponentContainer : : ObjInfoIterator > DrawingProgramLayerManager : : add_many_components_to_layer ( DrawingProgramLayerListItem * layer , const std : : vector < std : : pair < CanvasComponentContainer : : ObjInfoIterator , CanvasComponentContainer * > > & newObjs , bool newUndo ) {
auto toRet = layer - > get_layer ( ) . components - > insert_sorted_list_and_send_create ( layer - > get_layer ( ) . components , newObjs ) ;
add_undo_place_components ( layer , toRet , newUndo ) ;
return toRet ;
}
2026-01-08 14:02:37 +03:00
void DrawingProgramLayerManager : : disable_add_to_cache_block ( const std : : function < void ( ) > & toRun ) {
addToCacheOnComponentInsert = false ;
toRun ( ) ;
addToCacheOnComponentInsert = true ;
}
void DrawingProgramLayerManager : : disable_commit_update_block ( const std : : function < void ( ) > & toRun ) {
commitUpdateOnComponentInsert = false ;
toRun ( ) ;
commitUpdateOnComponentInsert = true ;
}
void DrawingProgramLayerManager : : disable_add_to_cache_and_commit_update_block ( const std : : function < void ( ) > & toRun ) {
addToCacheOnComponentInsert = false ;
commitUpdateOnComponentInsert = false ;
toRun ( ) ;
commitUpdateOnComponentInsert = true ;
addToCacheOnComponentInsert = true ;
}
2026-10-25 18:07:47 +01:00
bool DrawingProgramLayerManager : : can_current_peer_edit_layer ( const DrawingProgramLayerListItem * layer ) const {
if ( ! layer )
return false ;
if ( FogStripper : : world_is_host ( drawP . world ) )
return true ;
return ! layer - > is_player_edit_blocked ( ) ;
}
bool DrawingProgramLayerManager : : client_may_modify_component ( const CanvasComponentContainer * comp ) const {
// Host-side authority check for CLIENT messages — must NOT use can_current_peer_edit_layer
// (that always returns true on the host).
if ( ! comp | | ! comp - > parentLayer )
return false ;
return ! comp - > parentLayer - > is_player_edit_blocked ( ) ;
}
void DrawingProgramLayerManager : : ensure_editing_layer_is_editable ( ) {
auto cur = editingLayer . lock ( ) ;
if ( cur & & can_current_peer_edit_layer ( cur . get ( ) ) )
return ;
if ( ! layerTreeRoot )
return ;
std : : function < NetworkingObjects : : NetObjWeakPtr < DrawingProgramLayerListItem > ( const DrawingProgramLayerFolder & ) > findEditable ;
findEditable = [ & ] ( const DrawingProgramLayerFolder & folder ) - > NetworkingObjects : : NetObjWeakPtr < DrawingProgramLayerListItem > {
for ( auto & c : * folder . folderList ) {
if ( ! c . obj - > is_folder ( ) & & can_current_peer_edit_layer ( c . obj . get ( ) ) )
return c . obj ;
}
for ( auto & c : * folder . folderList ) {
if ( c . obj - > is_folder ( ) ) {
auto found = findEditable ( c . obj - > get_folder ( ) ) ;
if ( ! found . expired ( ) )
return found ;
}
}
return { } ;
} ;
editingLayer = findEditable ( layerTreeRoot - > get_folder ( ) ) ;
drawP . selection . remove_uneditable_from_selection ( ) ;
}
void DrawingProgramLayerManager : : refresh_fog_client_projection ( ) {
fogClientProjectionDirty = false ;
FogStripper : : sync_hidden_set_to_clients ( drawP . world , * this ) ;
}
void DrawingProgramLayerManager : : mark_fog_client_projection_dirty ( ) {
if ( drawP . world . netServer )
fogClientProjectionDirty = true ;
}
void DrawingProgramLayerManager : : flush_fog_client_projection_if_dirty ( ) {
if ( ! fogClientProjectionDirty )
return ;
refresh_fog_client_projection ( ) ;
}
void DrawingProgramLayerManager : : defer_reject_component ( DrawingProgramLayerListItem * layer , NetworkingObjects : : NetObjID compId ) {
deferredRejectInserts . emplace_back ( layer , compId ) ;
}
void DrawingProgramLayerManager : : flush_deferred_rejects ( ) {
if ( deferredRejectInserts . empty ( ) )
return ;
auto pending = std : : move ( deferredRejectInserts ) ;
deferredRejectInserts . clear ( ) ;
for ( auto & [ layer , compId ] : pending ) {
if ( ! layer | | layer - > is_folder ( ) )
continue ;
auto & comps = layer - > get_layer ( ) . components ;
if ( ! comps - > contains ( compId ) )
continue ;
comps - > erase ( comps , comps - > get ( compId ) ) ;
}
}
2025-12-30 19:46:08 +03:00
CanvasComponentContainer : : ObjInfo * DrawingProgramLayerManager : : add_component_to_layer_being_edited ( CanvasComponentContainer * newObj ) {
2025-12-29 19:57:36 +03:00
auto editLayerPtr = editingLayer . lock ( ) ;
if ( ! editLayerPtr )
2026-01-05 19:15:42 +03:00
throw std : : runtime_error ( " [DrawingProgramLayerManager::add_component_to_layer_being_edited] No layer selected! " ) ;
2026-10-25 18:07:47 +01:00
if ( ! can_current_peer_edit_layer ( editLayerPtr . get ( ) ) ) {
delete newObj ;
return nullptr ;
}
2025-12-30 19:46:08 +03:00
return & ( * editLayerPtr - > get_layer ( ) . components - > push_back_and_send_create ( editLayerPtr - > get_layer ( ) . components , newObj ) ) ;
2025-12-29 19:57:36 +03:00
}
2025-12-31 16:30:56 +03:00
2026-01-01 22:46:02 +03:00
void DrawingProgramLayerManager : : load_file ( cereal : : PortableBinaryInputArchive & a , VersionNumber version ) {
2026-01-02 09:22:52 +03:00
if ( version > = VersionNumber ( 0 , 4 , 0 ) ) {
layerTreeRoot = drawP . world . netObjMan . make_obj_from_ptr < DrawingProgramLayerListItem > ( new DrawingProgramLayerListItem ( ) ) ;
layerTreeRoot - > load_file ( a , version , * this ) ;
2026-01-08 14:02:37 +03:00
disable_add_to_cache_and_commit_update_block ( [ & ] ( ) {
layerTreeRoot - > get_folder ( ) . set_component_list_callbacks ( * this ) ; // Only need to call set_component_list_callbacks on root, and the rest will get the callbacks set as well
} ) ;
2026-01-02 09:22:52 +03:00
}
else {
server_init_no_file ( ) ;
2026-01-08 14:02:37 +03:00
disable_add_to_cache_and_commit_update_block ( [ & ] ( ) {
CanvasComponentContainer : : NetListTemporaryPtr editingLayerTmpPtrComponents = editingLayer . lock ( ) - > get_layer ( ) . components ;
uint64_t compCount ;
a ( compCount ) ;
for ( uint64_t i = 0 ; i < compCount ; i + + ) {
CanvasComponentContainer * newContainer = new CanvasComponentContainer ( ) ;
newContainer - > load_file ( a , version , drawP . world . netObjMan ) ;
editingLayerTmpPtrComponents - > push_back_and_send_create ( editingLayerTmpPtrComponents , newContainer ) ;
}
} ) ;
2026-01-02 09:22:52 +03:00
}
2026-01-01 22:46:02 +03:00
auto flattenedCompList = get_flattened_component_list ( ) ;
parallel_loop_container ( flattenedCompList , [ & drawP = drawP ] ( CanvasComponentContainer : : ObjInfo * comp ) {
comp - > obj - > commit_update_dont_invalidate_cache ( drawP ) ;
} ) ;
2026-04-03 14:18:04 +03:00
layerTreeRoot - > erase_invalid_components ( ) ;
2026-01-01 22:46:02 +03:00
drawP . rebuild_cache ( ) ;
editingLayer = layerTreeRoot - > get_folder ( ) . get_initial_editing_layer ( ) ;
2026-10-25 18:07:47 +01:00
ensure_editing_layer_is_editable ( ) ;
2026-01-01 22:46:02 +03:00
}
void DrawingProgramLayerManager : : save_file ( cereal : : PortableBinaryOutputArchive & a ) const {
layerTreeRoot - > save_file ( a ) ;
}
2026-01-01 23:28:26 +03:00
void DrawingProgramLayerManager : : get_used_resources ( std : : unordered_set < NetworkingObjects : : NetObjID > & resourceSet ) {
layerTreeRoot - > get_used_resources ( resourceSet ) ;
}
2026-01-04 16:50:47 +03:00
void DrawingProgramLayerManager : : add_undo_place_component ( CanvasComponentContainer : : ObjInfo * objInfo ) {
using namespace NetworkingObjects ;
class AddCanvasComponentWorldUndoAction : public WorldUndoAction {
public :
2026-01-06 12:55:44 +03:00
AddCanvasComponentWorldUndoAction ( uint32_t newPos , WorldUndoManager : : UndoObjectID initUndoID , WorldUndoManager : : UndoObjectID initComponentListUndoID ) :
2026-01-04 16:50:47 +03:00
pos ( newPos ) ,
undoID ( initUndoID ) ,
componentListUndoID ( initComponentListUndoID )
{ }
std : : string get_name ( ) const override {
return " Add Canvas Object " ;
}
bool undo ( WorldUndoManager & undoMan ) override {
std : : optional < NetObjID > toEraseID = undoMan . get_netid_from_undoid ( undoID ) ;
if ( ! toEraseID . has_value ( ) )
return false ;
std : : optional < NetObjID > toEraseCompListID = undoMan . get_netid_from_undoid ( componentListUndoID ) ;
if ( ! toEraseCompListID . has_value ( ) )
return false ;
auto compList = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( toEraseCompListID . value ( ) ) ;
2026-01-06 12:55:44 +03:00
auto toEraseIt = compList - > get ( toEraseID . value ( ) ) ;
copyData = toEraseIt - > obj - > get_data_copy ( ) ;
compList - > erase ( compList , toEraseIt ) ;
2026-01-04 16:50:47 +03:00
return true ;
}
bool redo ( WorldUndoManager & undoMan ) override {
std : : optional < NetworkingObjects : : NetObjID > toInsertID = undoMan . get_netid_from_undoid ( undoID ) ;
if ( toInsertID . has_value ( ) )
return false ;
std : : optional < NetObjID > toEraseCompListID = undoMan . get_netid_from_undoid ( componentListUndoID ) ;
if ( ! toEraseCompListID . has_value ( ) )
return false ;
auto compList = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( toEraseCompListID . value ( ) ) ;
auto insertedIt = compList - > emplace_direct ( compList , compList - > at ( pos ) , undoMan . world . netObjMan , * copyData ) ;
undoMan . register_new_netid_to_existing_undoid ( undoID , insertedIt - > obj . get_net_id ( ) ) ;
2026-01-06 15:15:36 +03:00
copyData = nullptr ;
2026-01-04 16:50:47 +03:00
return true ;
}
void scale_up ( const WorldScalar & scaleAmount ) override {
2026-01-06 15:15:36 +03:00
if ( copyData )
copyData - > scale_up ( scaleAmount ) ;
2026-01-04 16:50:47 +03:00
}
~ AddCanvasComponentWorldUndoAction ( ) { }
std : : unique_ptr < CanvasComponentContainer : : CopyData > copyData ;
uint32_t pos ;
WorldUndoManager : : UndoObjectID undoID ;
WorldUndoManager : : UndoObjectID componentListUndoID ;
} ;
2026-01-06 12:55:44 +03:00
drawP . world . undo . push ( std : : make_unique < AddCanvasComponentWorldUndoAction > ( objInfo - > pos , drawP . world . undo . get_undoid_from_netid ( objInfo - > obj . get_net_id ( ) ) , drawP . world . undo . get_undoid_from_netid ( objInfo - > obj - > parentLayer - > get_layer ( ) . components . get_net_id ( ) ) ) ) ;
2026-01-04 16:50:47 +03:00
}
2026-05-26 17:08:43 +03:00
void DrawingProgramLayerManager : : erase_component_map ( const std : : unordered_map < DrawingProgramLayerListItem * , std : : vector < CanvasComponentContainer : : ObjInfoIterator > > & eraseMap , bool newUndo ) {
add_undo_erase_components ( eraseMap , newUndo ) ;
2026-06-02 21:57:53 +03:00
drawP . world . send_reliable_multi_command_to_all ( [ & ] ( ) {
2026-01-04 16:50:47 +03:00
for ( auto & [ layerListItem , netObjSetToErase ] : eraseMap ) {
auto & layerComponentList = layerListItem - > get_layer ( ) . components ;
layerComponentList - > erase_list ( layerComponentList , netObjSetToErase ) ;
}
2026-06-02 21:57:53 +03:00
} ) ;
2026-01-04 16:50:47 +03:00
}
2026-05-26 17:08:43 +03:00
void DrawingProgramLayerManager : : add_undo_erase_components ( const std : : unordered_map < DrawingProgramLayerListItem * , std : : vector < CanvasComponentContainer : : ObjInfoIterator > > & eraseMap , bool newUndo ) {
2026-01-04 16:50:47 +03:00
using namespace NetworkingObjects ;
struct EraseListData {
std : : vector < WorldUndoManager : : UndoObjectID > undoID ;
std : : vector < uint32_t > pos ;
std : : vector < std : : unique_ptr < CanvasComponentContainer : : CopyData > > copyData ;
} ;
class EraseCanvasComponentsWorldUndoAction : public WorldUndoAction {
public :
EraseCanvasComponentsWorldUndoAction ( std : : unordered_map < WorldUndoManager : : UndoObjectID , EraseListData > initEraseMap ) :
undoEraseMap ( std : : move ( initEraseMap ) )
{ }
std : : string get_name ( ) const override {
return " Erase Canvas Objects " ;
}
bool undo ( WorldUndoManager & undoMan ) override {
struct InsertData {
std : : vector < NetObjOrderedListIterator < CanvasComponentContainer > > it ;
std : : vector < std : : unique_ptr < CanvasComponentContainer : : CopyData > > * copyData ;
std : : vector < WorldUndoManager : : UndoObjectID > * undoID ;
} ;
std : : unordered_map < NetObjID , InsertData > insertMap ;
{
for ( auto & [ parentUndoID , eData ] : undoEraseMap ) {
std : : optional < NetObjID > parentNetID = undoMan . get_netid_from_undoid ( parentUndoID ) ;
if ( ! parentNetID )
return false ;
auto & insertData = insertMap [ parentNetID . value ( ) ] ;
auto parentListPtr = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( parentNetID . value ( ) ) ;
insertData . it = parentListPtr - > at_ordered_indices ( eData . pos ) ;
insertData . copyData = & eData . copyData ;
insertData . undoID = & eData . undoID ;
}
}
2026-05-28 21:55:30 +03:00
{
2026-01-04 16:50:47 +03:00
for ( auto & [ parentNetID , insertData ] : insertMap ) {
auto parentListPtr = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( parentNetID ) ;
std : : vector < std : : pair < NetObjOrderedListIterator < CanvasComponentContainer > , NetObjOwnerPtr < CanvasComponentContainer > > > toInsert ( insertData . it . size ( ) ) ;
for ( size_t i = 0 ; i < insertData . it . size ( ) ; i + + ) {
toInsert [ i ] . first = insertData . it [ i ] ;
toInsert [ i ] . second = undoMan . world . netObjMan . make_obj_direct < CanvasComponentContainer > ( undoMan . world . netObjMan , * insertData . copyData - > at ( i ) ) ;
undoMan . register_new_netid_to_existing_undoid ( insertData . undoID - > at ( i ) , toInsert [ i ] . second . get_net_id ( ) ) ;
}
2026-02-10 13:12:53 +03:00
parentListPtr - > insert_sorted_list_and_send_create ( parentListPtr , toInsert ) ;
2026-01-06 15:15:36 +03:00
insertData . copyData - > clear ( ) ;
2026-01-04 16:50:47 +03:00
}
2026-05-28 21:55:30 +03:00
}
2026-01-04 16:50:47 +03:00
return true ;
}
bool redo ( WorldUndoManager & undoMan ) override {
std : : unordered_map < NetObjID , std : : vector < NetObjOrderedListIterator < CanvasComponentContainer > > > eraseMap ;
{
for ( auto & [ parentUndoID , eData ] : undoEraseMap ) {
std : : optional < NetObjID > parentNetID = undoMan . get_netid_from_undoid ( parentUndoID ) ;
if ( ! parentNetID )
return false ;
auto & eraseData = eraseMap [ parentNetID . value ( ) ] ;
auto parentListPtr = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( parentNetID . value ( ) ) ;
std : : vector < NetObjID > netIDList ;
if ( ! undoMan . fill_netid_list_from_undoid_list ( netIDList , eData . undoID ) )
return false ;
eraseData = parentListPtr - > get_list ( netIDList ) ;
2026-01-06 15:15:36 +03:00
for ( auto & it : eraseData )
eData . copyData . emplace_back ( it - > obj - > get_data_copy ( ) ) ;
2026-01-04 16:50:47 +03:00
}
}
2026-05-28 21:55:30 +03:00
{
2026-01-04 16:50:47 +03:00
for ( auto & [ parentNetID , eData ] : eraseMap ) {
auto parentListPtr = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( parentNetID ) ;
parentListPtr - > erase_list ( parentListPtr , eData ) ;
}
2026-05-28 21:55:30 +03:00
}
2026-01-04 16:50:47 +03:00
return true ;
}
void scale_up ( const WorldScalar & scaleAmount ) override {
for ( auto & [ undoID , eData ] : undoEraseMap ) {
for ( auto & c : eData . copyData )
c - > scale_up ( scaleAmount ) ;
}
}
~ EraseCanvasComponentsWorldUndoAction ( ) { }
std : : unordered_map < WorldUndoManager : : UndoObjectID , EraseListData > undoEraseMap ;
} ;
auto & undoMan = drawP . world . undo ;
std : : unordered_map < WorldUndoManager : : UndoObjectID , EraseListData > undoMap ;
for ( auto & [ item , compContainer ] : eraseMap ) {
auto & undoData = undoMap [ undoMan . get_undoid_from_netid ( item - > get_layer ( ) . components . get_net_id ( ) ) ] ;
// Vectors must be sorted
uint32_t i = 0 ;
for ( auto & comp : compContainer ) {
undoData . undoID . emplace_back ( undoMan . get_undoid_from_netid ( comp - > obj . get_net_id ( ) ) ) ;
undoData . copyData . emplace_back ( comp - > obj - > get_data_copy ( ) ) ;
undoData . pos . emplace_back ( comp - > pos - i ) ;
+ + i ;
}
}
2026-05-26 17:08:43 +03:00
if ( newUndo )
undoMan . push ( std : : make_unique < EraseCanvasComponentsWorldUndoAction > ( std : : move ( undoMap ) ) ) ;
else
undoMan . push_on_last ( std : : make_unique < EraseCanvasComponentsWorldUndoAction > ( std : : move ( undoMap ) ) ) ;
2026-01-04 16:50:47 +03:00
}
2026-05-26 17:08:43 +03:00
void DrawingProgramLayerManager : : add_undo_place_components ( DrawingProgramLayerListItem * parent , const std : : vector < CanvasComponentContainer : : ObjInfoIterator > & placeList , bool newUndo ) {
2026-01-04 16:50:47 +03:00
using namespace NetworkingObjects ;
struct PlaceListData {
std : : vector < WorldUndoManager : : UndoObjectID > undoID ;
std : : vector < uint32_t > pos ;
std : : vector < std : : unique_ptr < CanvasComponentContainer : : CopyData > > copyData ;
} ;
class PlaceCanvasComponentsWorldUndoAction : public WorldUndoAction {
public :
PlaceCanvasComponentsWorldUndoAction ( WorldUndoManager : : UndoObjectID parentList , PlaceListData initPlaceData ) :
parentUndoID ( parentList ) ,
undoPlaceData ( std : : move ( initPlaceData ) )
{ }
std : : string get_name ( ) const override {
return " Place Canvas Objects " ;
}
bool undo ( WorldUndoManager & undoMan ) override {
NetObjTemporaryPtr < NetObjOrderedList < CanvasComponentContainer > > parentListPtr ;
std : : vector < NetObjOrderedListIterator < CanvasComponentContainer > > eraseData ;
{
std : : optional < NetObjID > parentNetID = undoMan . get_netid_from_undoid ( parentUndoID ) ;
if ( ! parentNetID )
return false ;
parentListPtr = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( parentNetID . value ( ) ) ;
}
{
std : : vector < NetObjID > netIDList ;
if ( ! undoMan . fill_netid_list_from_undoid_list ( netIDList , undoPlaceData . undoID ) )
return false ;
eraseData = parentListPtr - > get_list ( netIDList ) ;
2026-01-06 15:15:36 +03:00
for ( auto & it : eraseData )
undoPlaceData . copyData . emplace_back ( it - > obj - > get_data_copy ( ) ) ;
2026-01-04 16:50:47 +03:00
}
parentListPtr - > erase_list ( parentListPtr , eraseData ) ;
return true ;
}
bool redo ( WorldUndoManager & undoMan ) override {
std : : vector < NetObjOrderedListIterator < CanvasComponentContainer > > it ;
NetObjTemporaryPtr < NetObjOrderedList < CanvasComponentContainer > > parentListPtr ;
2026-05-28 21:55:30 +03:00
auto p = std : : chrono : : steady_clock : : now ( ) ;
2026-01-04 16:50:47 +03:00
{
std : : optional < NetObjID > parentNetID = undoMan . get_netid_from_undoid ( parentUndoID ) ;
if ( ! parentNetID )
return false ;
parentListPtr = undoMan . world . netObjMan . get_obj_temporary_ref_from_id < NetObjOrderedList < CanvasComponentContainer > > ( parentNetID . value ( ) ) ;
}
2026-05-28 21:55:30 +03:00
p = std : : chrono : : steady_clock : : now ( ) ;
2026-01-04 16:50:47 +03:00
{
it = parentListPtr - > at_ordered_indices ( undoPlaceData . pos ) ;
}
2026-05-28 21:55:30 +03:00
p = std : : chrono : : steady_clock : : now ( ) ;
2026-01-04 16:50:47 +03:00
std : : vector < std : : pair < NetObjOrderedListIterator < CanvasComponentContainer > , NetObjOwnerPtr < CanvasComponentContainer > > > toInsert ( it . size ( ) ) ;
for ( size_t i = 0 ; i < it . size ( ) ; i + + ) {
toInsert [ i ] . first = it [ i ] ;
toInsert [ i ] . second = undoMan . world . netObjMan . make_obj_direct < CanvasComponentContainer > ( undoMan . world . netObjMan , * undoPlaceData . copyData . at ( i ) ) ;
undoMan . register_new_netid_to_existing_undoid ( undoPlaceData . undoID . at ( i ) , toInsert [ i ] . second . get_net_id ( ) ) ;
}
2026-05-28 21:55:30 +03:00
p = std : : chrono : : steady_clock : : now ( ) ;
2026-02-10 13:12:53 +03:00
parentListPtr - > insert_sorted_list_and_send_create ( parentListPtr , toInsert ) ;
2026-01-04 16:50:47 +03:00
2026-05-28 21:55:30 +03:00
p = std : : chrono : : steady_clock : : now ( ) ;
2026-01-06 15:15:36 +03:00
undoPlaceData . copyData . clear ( ) ;
2026-01-04 16:50:47 +03:00
return true ;
}
void scale_up ( const WorldScalar & scaleAmount ) override {
for ( auto & c : undoPlaceData . copyData )
c - > scale_up ( scaleAmount ) ;
}
~ PlaceCanvasComponentsWorldUndoAction ( ) { }
WorldUndoManager : : UndoObjectID parentUndoID ;
PlaceListData undoPlaceData ;
} ;
auto & undoMan = drawP . world . undo ;
PlaceListData undoData ;
// Vectors must be sorted
uint32_t i = 0 ;
for ( auto & comp : placeList ) {
undoData . undoID . emplace_back ( undoMan . get_undoid_from_netid ( comp - > obj . get_net_id ( ) ) ) ;
undoData . pos . emplace_back ( comp - > pos - i ) ;
+ + i ;
}
2026-05-26 17:08:43 +03:00
if ( newUndo )
undoMan . push ( std : : make_unique < PlaceCanvasComponentsWorldUndoAction > ( undoMan . get_undoid_from_netid ( parent - > get_layer ( ) . components . get_net_id ( ) ) , std : : move ( undoData ) ) ) ;
else
undoMan . push_on_last ( std : : make_unique < PlaceCanvasComponentsWorldUndoAction > ( undoMan . get_undoid_from_netid ( parent - > get_layer ( ) . components . get_net_id ( ) ) , std : : move ( undoData ) ) ) ;
2026-01-04 16:50:47 +03:00
}