You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ===================================== MAJOR FEATURES + CHANGES ===================================== Change 3433074 by Matt.Kuhlenschmidt Fix crash when clicking on certian tutorial blueprints. #jira UE-44593 Change 3433075 by Matt.Kuhlenschmidt Remove hittest grid log spam. The underlying problem causing this has been fixed Change 3433077 by Matt.Kuhlenschmidt Fix lighting becoming unbuilt when mesh painting #jira UE-44837 Change 3433081 by Matt.Kuhlenschmidt PR #3553: Crashfix for static array properties (Contributed by Pierdek) Change 3433104 by Alexis.Matte Make sure re-import skeletal mesh follow the import morph option #jira UE-42846 Change 3434825 by Matt.Kuhlenschmidt Fix crash when GC happens while the vr editor radial menu is open. Change 3434831 by Matt.Kuhlenschmidt Added missing file Change 3434868 by Shaun.Kime If you have a reroute node between a Material Function texture input and its usage, the parent material will fail to resolve the reroute node. #jira ue-44670 Change 3434998 by Alexis.Matte Meshes editors material/section panel are now fully transactional - Staticmesh editor: section material slot, section cast shadow, section collision, material slot instance, material slot name - Skeletal mesh editor: material slot instance, material slot name Also fix some transaction description #jira UE-44462 Change 3435195 by Jamie.Dale Fixed incorrect handling of some LTR scripts that require shaping These scripts need to go through HarfBuzz, and this also fixes a case where HarfBuzz wasn't applying font scale correctly. #jira UE-44767 Change 3435199 by Jamie.Dale Fixed some crashes/artifacts with bidirectional text It was possible for a line to compute an incorrect range, which could cause crashes or other highlighting issues. The highlighting logic has also been updated as the old code didn't handle all bidirectional cases correctly. Change 3435200 by Jamie.Dale Fixed a grapheme cluster metrics issue in the font editor viewport The viewport also now respects the default shaping method CVar. Change 3435771 by Alexis.Matte Fix degenerated bounds calculation for skeletalmesh when the skeleton is remove from a re-import (PhysicAsset API change, adding 1 function) #jira UE-44609 Change 3436856 by Jamie.Dale Added some missing Unicode block ranges Change 3436914 by Jamie.Dale Adding some missing combining character ranges to the text shaper Change 3436923 by Alexis.Matte PR #3463: Get bounds for all triangles, not just the first one. WedgeIndex was . (Contributed by DaveC79) #jira UE-43764 Change 3436948 by Jamie.Dale Updated the Portal to use the predefined Unicode block ranges Change 3436961 by Max.Chen Sequencer: Show camera shake/anim track menus even if there aren't any assets. Change 3437244 by Max.Chen Sequencer: Clear locked cameras when releasing Sequencer. #jira UE-44967 Change 3437515 by Arciel.Rekman UBT: improvements for LocalExecutor. - Larger number of parallel jobs on 16GB+ machines. - Use WaitForExit() instead of polling. - Tested on Linux and Mac. Change 3437629 by Matt.Kuhlenschmidt Improve asset import data display in static and skeletal meshes Change 3438047 by Arciel.Rekman Fix overlapping ranges being passed to memcpy(). Change 3438822 by Yannick.Lange ViewportInteraction: Move gizmo handle files to make them private. Change 3438906 by Matt.Kuhlenschmidt PR #3556: Git Plugin: fix new option "init Git LFS" that make assets read-only (master/UE4.17) (Contributed by SRombauts) Change 3438907 by Matt.Kuhlenschmidt PR #3565: add -quality option to buildlighing commandlet (Contributed by kayama-shift) Change 3438908 by Matt.Kuhlenschmidt PR #3558: UE-44862: Always update SColorPicker color on OK button (Contributed by projectgheist) Change 3439393 by Matt.Kuhlenschmidt Force highest LOD for highres screenshots Change 3439819 by Matt.Kuhlenschmidt Turned FAssetData into a struct for some upcoming script exposure of FAssetData Change 3439949 by Arciel.Rekman Fixed selection logic for the UE4_LINUX_USE_LIBCXX environment variable. - Allows disabling libc++ by setting the variable to 0. - Pull request #3576 contributed by jared-improbable. Change 3441078 by Jamie.Dale The culture/language/locale console commands are now available in all build configs Change 3441109 by Jamie.Dale Text containing surrogate pairs now runs through HarfBuzz when shaping in Auto mode This is needed as the kerning-only shaping code assumes that everything is within the BMP Change 3441275 by Matt.Kuhlenschmidt Disable spinning on location and scale. These dont work because we have no notion of infinite spinning Change 3442748 by Yannick.Lange ViewportInteraction: Remove unused console variables. Change 3442775 by James.Golding Add support for editing MaterialFunctions to MaterialEditingLibrary Pull Material recompile/update code into UMaterialEditingLibrary::RecompileMaterial Pull MaterialFunction update code into UMaterialEditingLibrary::UpdateMaterialFunction util Move RebuildMaterialInstanceEditors to UMaterialEditingLibrary Added test content for Material/MaterialFunction editing Add needed BlueprintReadWrite to expressions (constants, function input/output) Expose UMaterialExpressionMaterialFunctionCall::SetMaterialFunction to BP, rename old func (which takes old function) to SetMaterialFunctionEx, also expose GetInputNameWithType Change 3442779 by James.Golding Fix header order Change 3442817 by Yannick.Lange ViewportInteraction: Add can execute checks for level editor commands. Change 3443038 by Michael.Dupuis #jira UE-43377: When you select a foliage actor we will move all instance contained in it to the new level as we can't move a foliage actor Only permit moving foliage instance if there is some selected Change 3443855 by Michael.Dupuis #jira UE-44885: Unregister from PerModuleDataObjects when the object is destroyed Change 3446096 by Max.Chen Sequencer: Add OnFinished() event when a level sequence completes playback #jira UE-45173 Change 3446097 by Max.Chen Sequencer: Evaluate one last time before the sequence is torn down and reset #jira UE-45174 Change 3446242 by Jamie.Dale Fixed caret not appearing in empty text layouts Caret selections have no range, and therefore have no width Change 3446361 by Matt.Kuhlenschmidt Fix WITH_EDITOR only functions causing generated code compile errors when the all functions on the class are WITH_EDITOR Change 3446457 by Alexis.Matte Polish the speed tree import dialog #jira UE-44963 Change 3446946 by Michael.Trepka Modified FWindowsWindow::GetRestoredDimensions to return correct window position for normal windows for which GetWindowPlacement returns position in workspace coordinates #jira UE-37934 Change 3447543 by Arciel.Rekman Reduce VMAs on Linux. - Trades off increased address space (VIRT in terms of ps/htop) for smaller number of distinct mappings (VMAs, virtual memory areas). This decreases possibility to run into vm.max_map_count limit on Linux. - Tested on Linux and Mac. Change 3448468 by Arciel.Rekman Fix race condition during creation of GMalloc. - On Mac GMalloc can be created on two different thread that are racing with each other - app's main thread and a system thread. Change 3449012 by Max.Chen Sequencer: Add time to transform, color and vector key structs so that key times are editable from the key editors. #jira UE-45089 Change 3449018 by Max.Chen Sequencer: Add OnCameraCut event that fires when there is a camera cut. #jira UE-45137 Change 3449195 by Max.Chen Sequencer: Add setting for limit scrubbing to playback range. #jira UE-43502 Change 3449198 by Max.Chen Sequencer: Reorder hierarchical bias so that group priority takes precedence. Change 3449217 by Max.Chen Sequencer: Add setting to activate realtime viewports when in sequencer. Change 3449219 by Max.Chen Sequencer: Focus on search boxes when opened. Change 3449238 by Max.Chen Sequencer: Assign actor should replace the actor itself after it has updated all the components. Also, replace components be fullname rather than by class. Change 3449239 by Max.Chen Sequencer: Fix offsets when moving multiple sections. Dragging should be clamped to the bounds that any of the selected sections hits against the unselected sections. Change 3449241 by Max.Chen Sequencer: Restore section selection after full tree rebuild. Change 3449279 by Max.Chen Sequencer: Set movie scene capture frames only when not using custom frames. This allows the user entered frame numbers to persist in config, rather than overwriting them when doing a "Render Shot" Change 3449280 by Max.Chen Sequencer: Spawn in the persistent level. Otherwise, they get spawned into whatever sublevel is current. #jira UE-44552 Change 3449294 by Max.Chen Sequencer: Null check for sequencer ed mode crash. Change 3449297 by Max.Chen Sequencer: Fix delay in sliding values. Mark changed when sliding values. Mark refresh immediately when committing values since OnValueChanged will be called and needs to have the correct value that was refreshed immediately. #jira UE-42866 Change 3449542 by Max.Chen Sequencer: Fix scrubber hit testing so that the time scrubber is really favored over the playback ranges. #jira UE-44569 Change 3451507 by Matt.Kuhlenschmidt Fix extra slate uv coords not functioning on ES2 Change 3451510 by Matt.Kuhlenschmidt PR #3595: Fixed wrong colour for level status (Contributed by ronve) Change 3451529 by Alexis.Matte fbx scene importer: Make sure we set INVALID_UNIQUE_ID to node that has no attribute. #jira UE-34410 Change 3451611 by Yannick.Lange ViewportInteraction: Dragging gizmo without second pass for snapped calculations. Change 3452134 by Jamie.Dale Fixed constant font cache flushing if a widget had no font set Change 3452239 by Jamie.Dale Fixed constant font measure flushing if a widget had no font set Change 3452243 by Jamie.Dale Removed deprecated code for creating fonts from bulk data Change 3452277 by Jamie.Dale The concept of "stale" composite fonts is now editor-only Change 3452358 by Alexis.Matte Fbx scene importer: Do not remove existing attribute reference from the blueprint if the reimport of the associate mesh attribute is not tick. #jira UE-45232 Change 3452678 by Max.Chen Sequencer: Fix crash on export if there's no shot data. Change 3453057 by Matt.Kuhlenschmidt Exposed asset exporting to script Change 3453782 by Andrew.Rodham Sequencer: Fixed deterministic cooking issues with movie scene data - Movie scene signatures are now initialized in PostInitProperties - A warning is now presented when attempting to cook old data that was never serialized with a signature. - Removed redundant legacy data upgrade logic that could dirty level sequences on load. #jira UE-44912 Change 3453788 by Yannick.Lange ViewportInteraction: Custom scene proxy for gizmo handles. Change 3453938 by Max.Chen Sequencer: Hotkeys (shift , and shift .) to step to next/previous shot #jira UE-45119 Change 3454058 by Michael.Dupuis Fixed StaticAnalysis Change 3454077 by Max.Chen Sequencer: Fix not saving the pre-animated track value when creating a track/key. On pre object change, broadcast property change so that a track or key can be created. That track/key needs to be evaluated immediately so that the pre-animated state can be saved properly. This is done now with RefreshAllImmediately and is only called when a track has been created. Also, added a return value for OnKeyProperty, so that it's known what changed in particular (ie. track created, track modified, etc) Also, fixed transform keying so that if a transform track already exists for the object or the scene component, the existing track is used. #jira UE-45130 Change 3454108 by Nick.Darnell UMG - Fixing the WIC to properly record cursor delta so that scrollbars work. Change 3454109 by Jamie.Dale Cache the text layout source info in non-shipping builds so you can inspect it in the debugger Change 3454202 by Matt.Kuhlenschmidt Fix bogus error message about the number of usable texture coordinates on ES2 when compiling a UI domain material Change 3454390 by Yannick.Lange Fix creating a plugin in a C++ project opens a second instance of Visual Studio. Use SourceCodeAccessor to open solution when necessary. #jira UE-45035 Change 3454564 by Matt.Kuhlenschmidt #rnx Fix deprecation warnings Change 3455471 by Yannick.Lange ViewportInteraction: Fix entering and exiting VR Mode disables gizmo in desktop editor viewport. #jira UE-44965 Change 3456183 by Max.Chen Sequencer: Auto key, auto track refactor. Auto key - create a key when the property changes and there's an existing track. Auto track - create a track when the property changes. This is only exposed in the level sequence editor. All - create a key and a track when the property changes. This is only exposed in VR Editor. None - do nothing. #jira UE-43469 Change 3456349 by Andrew.Rodham Sequencer: Only perform legacy signature checks on instances, and only where signatures match the CDO Change 3456678 by Alexis.Matte Allow to add null level instance override material via the advance material array. But still limit the override material number to the mesh material number. #jira UE-45306 Change 3456945 by Max.Chen UMG: Add restore state to 2d transform section. #jira UE-45372 Change 3457196 by Arciel.Rekman Linux: serialize allocations from the memory pool. Change 3458434 by Max.Chen Sequencer: Remove obsolete set tick prerequites functions. Change 3458671 by James.Golding Added MIC editing support to MaterialEditingLibrary Fix static analysis warning Change 3458888 by Matt.Kuhlenschmidt PR #3615: More detailed log messages for debugging warnings/errors (Contributed by projectgheist) Change 3458893 by Matt.Kuhlenschmidt PR #3583: UE-44960: Delta value wasn't being used (Contributed by projectgheist) Change 3458895 by Matt.Kuhlenschmidt Fix typo Change 3458902 by Matt.Kuhlenschmidt PR #3607: Improved InputKeySelector functionality (Contributed by projectgheist) Change 3458917 by Matt.Kuhlenschmidt Fix crash with invalid object properties in the class picker #jira UE-39000 Change 3458939 by Matt.Kuhlenschmidt Fix compile error Change 3458984 by andrew.porter QAGame: Initial check in of sequencer smoke test map Change 3459510 by Matt.Kuhlenschmidt Fixed ensure when deleting a map that contains build data which also happens to be the currently loaded map. #jira UE-45052 Change 3460985 by Max.Chen Sequencer: Snap play time to keys now allows scrubbing between keys and snaps to key times within a certain screenspace tolerance. #jira UE-45090 Change 3461698 by Arciel.Rekman Avoid using ARRAY_COUNT in Vulkan. - Sometimes those arrays can have no extensions whatsoever, and it is illegal to declare a 0 element C array. Change 3462053 by Max.Chen Sequencer: Show sequencer spawnables in the world outliner and add the icon overlay for spawnables. #jira UE-43470 Change 3462139 by Max.Chen Property Editor: Add objects to FPropertyAndParent Change 3462202 by Arciel.Rekman Fix FSocket::Recv() blocking with Peek when there's no data. Change 3462253 by Nick.Darnell Slate - New Clipping System Clipping is now a stateful choice made during composition of the slate hierarchy. Previously every widget got to respect or modify the clipping rect on an as needed basis. The problem was that clipping was only allowed in the layout space of the widget, and it wasn't possible to properly clip elements with render transforms. The new system permits all kinds of transforms on any widget, and they will all be clipped correctly. It tries to use Scissor Rects as they are much cheaper, but will switch over to stenciling if need be to represent a complicated masking structure with several rotated clipping rects all needed to be combined together. Here are the new clipping states a widget can have, almost all widgets are set to No. Only change it from No if your widget actually needs to clip, generally speaking most widgets don't need to clip. /** * This widget does not clip children, it and all children inherit the clipping area of the last widget that clipped. */ Inherit, /** * This widget clips content the bounds of this widget. It intersects those bounds with any previous clipping area. */ ClipToBounds, /** * This widget clips to its bounds. It does NOT intersect with any existing clipping geometry, it pushes a new clipping * state. Effectively allowing it to render outside the bounds of hierarchy that does clip. * * NOTE: This will NOT allow you ignore the clipping zone that is set to [Yes - Always]. */ ClipToBoundsWithoutIntersecting UMETA(DisplayName = "Yes - Without Intersecting (Advanced)"), /** * This widget clips to its bounds. It intersects those bounds with any previous clipping area. * * NOTE: This clipping area can NOT be ignored, it will always clip children. Useful for hard barriers * in the UI where you never want animations or other effects to break this region. */ ClipToBoundsAlways UMETA(DisplayName = "Yes - Always (Advanced)"), /** * This widget clips to its bounds when it's Desired Size is larger than the allocated geometry * the widget is given. If that occurs, it behaves like [Yes]. * * NOTE: This mode was primarily added for Text, which is often placed into containers that eventually * are resized to not be able to support the length of the text. So rather than needing to tag every * container that could contain text with [Yes], which would result in almost no batching, this mode * was added to dynamically adjust the clipping if needed. The reason not every panel is set to OnDemand, * is because not every panel returns a Desired Size that matches what it plans to render at. */ OnDemand UMETA(DisplayName = "On Demand (Advanced)") - Large API Change - All FSlateDrawElement::Make_____ calls have been deprecated that involved passing in a clipping rect. You no longer should are passed a Clipping rect via OnPaint. You are still passed a rect, but this rect represents a Culling Rect, which is valuable if you need to just out right not paint things the user can't possibly see. If you were previously trying to determine if you should cull widgets, by doing something like this, if ( FSlateRect::DoRectanglesIntersect(MyClippingRect, CurWidget.Geometry.GetRenderBoundingRect()) ) That's no longer a good option since there are ways for widgets to ignore the culling bounds. You should convert anything like above to the one below, if (!SWidget::IsWidgetCulled(MyCullingRect, CurWidget)) To assist in debugging efforts, there are several new debugging console flags in Slate, Slate.ShowClipping 1 - Controls whether we should render a clipping zone outline. Yellow = Axis Scissor Rect Clipping (cheap). Red = Stencil Clipping (expensive). Slate.DebugCulling 1 - Disables pushing clipping or stencil rects to the GPU, but continues to intersect culling rects, so that you can tell if a widget is properly culling children it can't possibly draw. Slate.ShowTextDebugging 1 - Show debugging painting for text rendering. I've added a new Experimental Feathering Option, it adds AA geometry around the outside of Box and Image brushes. Slate.Feathering 1 If you're using RenderDoc or something similar, you can now enable render events for slate, so that you can better grok how we're batching and changing states for each UI render pass. Slate.EnableDrawEvents 1 #jira UE-4659 #rn Change 3462714 by Nick.Darnell Fixing a few more compiler issues with the clipping changes. Change 3462726 by Max.Chen Switch OnEditStructChildContentsChanged to use FObjectWriter instead of FMemoryWriter which supports serializeing UObjects. This fixes a crash when adding actor array elements to a user defined event struct. #jira UE-45431 Change 3462801 by Nick.Darnell Adding a UMG dependency to EngineTestBuild. Change 3462914 by Max.Chen Sequencer: Fix regression where spawnables aren't getting saved. Caused by 3407138 #jira UE-30007 #jira UE-39003 Change 3462946 by Nick.Darnell Automation - Tweaking the UI automation tests converting them over to use the new UI Screenshot automation test. Automation - Adding a blur widget test. Change 3462987 by Matt.Kuhlenschmidt Back out changelist 3458893 Change 3464774 by Matt.Kuhlenschmidt PR #3629: Bugfix: Missing small icon in Project Launcher profile editor (Contributed by aarmbruster) Change 3464785 by Nick.Darnell Fixing some clipping stuff in the editor. Change 3464830 by andrew.porter QAGame: Second pass on sequencer smoke test map Change 3464902 by Nick.Darnell Loading - Adding some additional checks to the the loading code to ensure we're on the main thread. Additionally adding a fix from UDN that prevents deadlocks in the rare case a user hits Alt+Tab in a fullscreen game while in a hard loading screen. Change 3464988 by Max.Chen Sequencer: Add attenuation settings for attached audio components. #jira UE-33080 Change 3465024 by Nick.Darnell MoviePlayer - Impoving the playback mode displaynames. Change 3465074 by Arciel.Rekman Fix shadowing issues of GraphicsPSOInit. Change 3465097 by Matt.Kuhlenschmidt Some refactoring of the details panel Exposed new methods of adding a struct on scope to a details panel and have it work properly with customizations. The scruct on scope has a "fake" ustructproperty that allows the details panel to show the whole struct not just an individual property. Refactored the API for adding rows to details panels to make it more consistent\ AddChildCustomBuilder->AddCustomBuilder AddChildGroup->AddGroup AddChildContent->AddCustomRow AddChildPropert->AddProperty AddChildStructure->AddExternalStructureProperty AddStructure->AddAllExternalStructureProperties AddExternalProperty->AddExternalObjectProperty or AddExternalStructureProperty Change 3465186 by Max.Chen Sequencer: Save the BindingID in the pre animated token producer so that it can be destroyed properly. This fixes a bug where the default state of a spawnable isn't saved. #jira UE-43780 Change 3465315 by Matt.Kuhlenschmidt Fix Fortnite and Orion details panel customization warnings Change 3465424 by Nick.Darnell Automation - Moving the step for setting the link to the automation reports to be set before we start the engine. Change 3465488 by Nick.Darnell Automation - Forcing textures to load before taking screenshot, so that the scene gets another opportunity to render before we render with Slate. This should fix the Blur UI Test. Change 3466277 by Arciel.Rekman Linux: fix window drift when dragging (UE-40380). - Change by Cengiz Terzibas. Change 3466370 by Nick.Darnell UMG - Fixing the colors for the resize handle in the designer. Change 3466372 by Nick.Darnell UMG - Fixing the ruler ticks sometimes not being drawn. Change 3466374 by Nick.Darnell UMG - Fixing the designer showing multiple options for sequencer. Change 3466377 by Nick.Darnell UMG - Cleaning up some clipping bits. Change 3467025 by Andrew.Rodham Re-saving assets that contain legacy (<4.15) movie scene data to remove deterministic cook warning. If conflicts arise during merging of these assets, please ignore the changes made in dev-editor, and accept game-side changes. (CIS step 62283298, jobId 7773146) (CIS step 62283297, jobId 7773146) Change 3467099 by Max.Chen Fix GetObjectPropertyClass ensure logic. This was returning UObject::StaticClass when valid. Change 3467172 by Max.Chen Sequencer: Evaluation optimizations. Also, fixes subsequences not getting expired, leaving dangling spawnables. #jira UE-43690 Change 3467192 by Matt.Kuhlenschmidt Fix transactions getting stuck in the color grading controls. This prevents PIE from working properly and causes shutdown crashes #jira UE-45527 Change 3467251 by Yannick.Lange ViewportInteraction: Fix scale and rotation snap while dragging with two lasers. #jira UE-43489 Change 3467331 by Matt.Kuhlenschmidt Fix D3D shader compiler hard coding shader path and not giving proper warnings when it cannot find the shaders Change 3467335 by Matt.Kuhlenschmidt Remove DarkStyle attribute from SNumericEntryBox and allow a spin box style to be passed to it. Change 3467558 by Max.Chen Scene Outliner: Generic support to add default columns to a scene outliner. Change 3467565 by Jamie.Dale Removing old screenshot data for test Change 3467589 by Nick.Darnell Editor - Random cleanup. Change 3467596 by Nick.Darnell Progress Bar - Exposing Border Padding to UMG. Change 3467600 by Nick.Darnell Slate - Adjusting the rendering of the splitter, previously it could be off by a pixel or two, which becomes more apparent now with the clipping changes. Change 3467601 by Max.Chen Property Editor: Fix static analysis warning Change 3467662 by Nick.Darnell Automation - Fixing a bug with the screenshot comparison tool not replacing (removing) the old screenshot data. Change 3467674 by Max.Chen Property Editor: Fix static analysis warning Change 3467737 by Max.Chen Sequencer: Added OnMovieSceneBindingsChanged delegate Change 3468053 by tim.gautier QAGame: Updating Editor Smoke Map - Updated landscapes into Stations for testing - Added Foliage Sublevel Change 3468194 by Arciel.Rekman Linux: fix problems communicating with various STL-using libs. - Stop hiding global new/delete signatures. - Disable CEF3 since this change uncovers the problem with libcef.so not built to use bundled libpng. Change 3468678 by Max.Chen Sequencer: Set "Sequencer Actor" tag before setting the actor label so that the outliner refreshes after the actor has the tag. Change 3469314 by tim.gautier QAGame: Added Painted Foliage / Spline section to EditorSmoke map Change 3469377 by Nick.Darnell Slate - Fixing some warnings in a couple of sample games due to the clipping changes. #rnx Change 3469767 by Max.Chen Sequencer: Outliner column and sequencer binding data #jira UE-43470 Change 3469974 by Arciel.Rekman Fix code projects not working in Linux installed build. Change 3470082 by Nick.Darnell Slate - The GetRenderer() call on SlateApplication no longer returns a SharedPtr, rather than convert it to a thread safe ptr, going to just make accessing it a raw ptr return, so it can be safely referenced on the game thread while being used on the slate loading thread. Change 3470174 by Nick.Darnell Slate - Get the last widget in a widget path utility. Change 3470176 by Nick.Darnell UMG - User Widgets now have an easy way to know if they're part of or have been removed from the focused widget path, which is handy for doing effects. Change 3470261 by Nick.Darnell Slate - The GetRenderer() call on SlateApplication no longer returns a SharedPtr, rather than convert it to a thread safe ptr, going to just make accessing it a raw ptr return, so it can be safely referenced on the game thread while being used on the slate loading thread. Change 3470286 by Max.Chen Sequencer: Scene Component's HiddenInGame now goes through the VisibilityTrack and the visibility template. Change 3470366 by Nick.Darnell Slate - We now version focus per user, that way during focus events, we can safely abort focus events and state transitions if someone interrrupts the active focus event with something new. Change 3470649 by Matt.Kuhlenschmidt Fix deprecation warnings Change 3470695 by Matt.Kuhlenschmidt Fixed typo #jira UE-45580 Change 3470721 by Matt.Kuhlenschmidt Fix static analysis Change 3471254 by Michael.Dupuis #jira UE-42952: Keep occlusion result per view Change 3471287 by Nick.Darnell UMG - Render Focus Rule now defaults to never. Change 3471291 by Nick.Darnell Slate - Fixing FSlateRenderer* change fallout. Change 3471299 by Nick.Darnell Slate - Fixing FSlateRenderer* change. Change 3471323 by Nick.Darnell Automation - Fixing automation and Static Analysis warning. Change 3471413 by andrew.porter QAGame: Added test content for anim blending and material parameteres to sequencer smoke level Change 3471649 by Max.Chen Sequencer: Modify the track when adding animation #jira UE-45618 Change 3471659 by Matt.Kuhlenschmidt Added a way to check if a movie is playing from the engine. Prevented viewport redraws for canvas loading screens if a slate based loading movie is playing Change 3471734 by Matt.Kuhlenschmidt Added basic material hookup to USD. Similar to FBX it will find materials based on rules specified by the user in the import settings Change 3472176 by Nick.Darnell UMG - Improving the display of the +Track menu in sequencer for UMG. Renamed it from +Add, which is repetitve to +Track. Additionally, the dropdown now shows the currently selected widgets, as well as a submenu containing all the 'important' widgets, so we no longer populate that list with a ton of irrelevant widgets that are just Buton_1 - N, which is pointless in showing people, they'll never guess which is the right button. Change 3472740 by Max.Chen Sequencer: Add GetThisFrameMetaData accessor Change 3472748 by Max.Chen Sequencer: Added OnBeginScrubbing and OnEndScrubbing event delegates Change 3472753 by Max.Chen Sequencer: Add EMovieSceneDataChangeType parameter to OnMovieSceneDataChanged delegate Change 3472870 by Nick.Darnell Clipping - Fixing the deprecated tip for scissor rect boxes to be correct. Removing it's usage from UT. Change 3473340 by Max.Chen Scene Outliner: Add ability to register additional filters Change 3473348 by Max.Chen Details View: Make ForceRefresh virtual. Added accessors to delegates (ie. GetIsPropertyReadOnlyDelegate) Change 3473441 by Max.Chen Sequencer: Autokey Refactor Part 2. Autokey is now a single toggleable state. Allow Edits Mode has 3 states: Allow All Edits - Allow any edits to occur, some of which may produce tracks/keys or modify default properties. Allow Sequencer Edits Only - All edits will produce either a track or a key. Allow Level Edits Only - Properties in the details panel will be disabled if they have a track. #jira UE-45229 Change 3473670 by Nick.Darnell Modules - The module manager no longer returns sharedptrs to IModuleInterfaces, this was the source of rare hard to track down crashes due to a shared ptr reference leak when GetModule was called on non-main threads. We now store a TUniquePtr internally, and only lease out raw pointers. #rn Change 3473711 by Nick.Darnell Disabling the ensure in the module manager. Change 3473747 by Max.Chen Sequencer: Fix tooltip Change 3474091 by Jamie.Dale Added a warning when cooking a UFontFace that is outered to a UFont asset These cause issues with iterative COTF, and should be split off into their own assets (as the UI has been asking people to do for several versions) Change 3475052 by Yannick.Lange VR Editor: Fix Crash when quitting the editor with VR Mode enabled. VR Editor was being enabled when saving the map on closing the editor. #jira UE-45415 Change 3475054 by Yannick.Lange Fix crash when adding a camera to the world in VR Mode the second time. The slate application did not reset when stop dragging in VR Mode, so the second time when starting to drag a camera out of the UI it would already by in a dragging state. #jira UE-45574 Change 3475263 by Nick.Darnell Fixing some additional cases of IModuleInteface SharedPtr usage. Change 3475268 by Max.Chen Sequencer: Set jumped state when looping playback. This fixes an issue where audio doesn't stop and restart when looped. #jira UE-45654 Change 3475269 by Max.Chen Scene Outliner: Additional filters should only apply to actor browsing mode Change 3475407 by Nick.Darnell Fixing some clipping / module shared ptr changes in the launcher code. Change 3475542 by Max.Chen Sequencer: Update thumbnail and section highlighting to use new clipping behavior. #jira UE-45692 #jira UE-45689 Change 3475743 by Michael.Dupuis #jira UE-45183: When updating phyx region take into account simple collision mip Change 3475949 by Arciel.Rekman Remove PhysX deoptimization (no longer needed). - OR-24947 has been closed three months ago. Change 3476022 by Michael.Dupuis #jira UE-45560: Make sure we're not going out of range Change 3476063 by Michael.Dupuis #jira UE-45562: Do not try to unregister from static mesh if no static mesh is specified for the component Change 3476168 by Michael.Trepka Added handling of directory symlinks to FApplePlatformFile::IterateDirectory #jira UE-43704 Change 3476172 by Nick.Darnell Fixing a Imoduleinterface change. Change 3476183 by Jamie.Dale Exposing GoTo/ScrollTo to single-line editable text for API parity with multi-line editable text Change 3476385 by Arciel.Rekman Linux: handle symlinks when iterating directories. Change 3476522 by Michael.Trepka Solved a problem with Mac FMallocTBB::Malloc() returning nullptr for 0 bytes allocations, which is inconsistent with other platforms. On Mac we always scalable_aligned_malloc, which behaves differently than scalable_malloc, so for 0 bytes requests we allocate sizeof(size_t), which is exactly what scalable_malloc does internally in such case. Change 3476806 by Nick.Darnell UMG - Focus the designer after dropping a widget onto the surface. Change 3476809 by Nick.Darnell Curve Editor - Enable Clipping on the curve editor. Change 3477475 by Nick.Darnell Fixing a module interface shared ptr usage in UT. Change 3477553 by Yannick.Lange VR Editor: Removed AssetEditorPanelID and replaced it with TabManagerPanelID. A panel for AssetEditorPanelID was never created making it impossible to open an asset editor. Change3477734by Yannick.Lange VR Editor: Fix Warning: SetRelativeScale3D : Invalid Scale entered (X=inf Y=inf Z=inf). Resetting to 1.f. warning when adding CineCameraActor to World from Modes Panel. Make sure to not divide by zero when there is no boundary scale. #jira UE-44933 Change 3477761 by Jamie.Dale Some improvements to avoid loading the native .locres files twice when we don't need to Change 3477780 by Nick.Darnell PR #3250: Return correct VirtualUserIndex (Contributed by projectgheist) Change 3477786 by Nick.Darnell PR #3650: Changed TestNull to accept const pointers. (Contributed by e-agaubatz) Change 3477795 by Nick.Darnell PR #2844: UE-36936: Don't stretch container for Plugin Image (Contributed by projectgheist) Change 3478092 by Nick.Darnell PR #2341: Optional Middle Mouse Button panning in Graph Editor (Contributed by flipswitchingmonkey) Engine Edit - Made some small changes to the enum type, and some naming. Change 3478450 by Nick.Darnell Fixing some uninitialized variable errors. Change 3479827 by Andrew.Rodham Sequencer: Addressed serialization issues with some struct types Change 3479874 by Jamie.Dale Fixed "NativeGameLanguage" not being used correctly during localization initialization Change 3480012 by Andrew.Rodham Sequencer: Fixed loading tagged properties as native for track identifiers #jira UE-45823 Change 3480337 by Alexis.Matte Fix morph target crash missing some valid index check Change 3480804 by Alexis.Matte Fix crash with ColorGradingMode custom detail #jira UE-45638 Change 3480892 by Andrew.Rodham Sequencer: Ensure that movie scene sequences know about the editor object version #jira UE-45842 Change 3481073 by Nick.Darnell Fix the shader compiler error from main in Slate. Change 3481303 by Nick.Darnell UMG - Fixing a bug with the drag handle not working correctly in HDPI mode. Change 3481308 by Nick.Darnell Slate - Tweaking the IsWidgetCulled logic to consider both the layout and rendering bounds. If we do this, we get a much more desireable outcome for people that want to animate widgets and such and plan to have temporary animations to move the widget offscreen, but want the layout bounds to anchor that widget in the visible frame so that it animates even when normally it would be culled b/c the render transform and therefore the renderbounds moved it completely outside the culling rect. Change 3481629 by Max.Chen Sequencer: Add Level Sequence Actor as an output for CreateLevelSequencePlayer() #jira UE-45785 Change 3481899 by Yannick.Lange VR Editor: Added debug modetoggle command with an event that is broadcasted whenever this happens. Currently this is used to show all the floating UIs of the UI system to debug without HMD using VREd.ForceVRMode. Change 3481984 by Michael.Dupuis #jira UE-45845: always validate if we have a static mesh before trying to access it as user can decide to not assign one and use the tools Change 3482047 by Nick.Darnell Slate - Adding some comments to IsWidgetCulled. Change 3482110 by Nick.Darnell Slate - IsWidgetCulled is no longer static and is now called IsChildWidgetCulled. Change 3482136 by Jamie.Dale The CamelCase break iterator now treats digits around character tokens as part of the identifier Change 3482138 by Michael.Dupuis #jira UE-45854: Properly unregister during undo operation Change 3482150 by Michael.Dupuis #jira UE-45845 : Add missing nullcheck for GetStaticMesh Change 3482153 by Nick.Darnell Slate - IsWidgetCulled is no longer static and is now called IsChildWidgetCulled. Change 3482180 by Nick.Darnell UMG - Widget Components do not need to define a widget class to be rendererd, they can have native slate widgets only. This was a regression from main. Change 3482273 by Nick.Darnell UMG - Tweaking some more things about the widget component box outline. Change 3482308 by Alexis.Matte Fixing morph target smooth group support. Do not call FillSkeletalMeshImportData more then once on FbxNode since this fonction is doing some conversion and change the FbxNode, applying those conversion twice do not return the same faces smooth group. #jira UE-45696 Change 3482327 by Nick.Darnell UMG - More tweaks to the WidgetComponent so both shows the box outline, but works in game and VR editor. Change 3482705 by Andrew.Rodham Resaving assets that contain legacy data to suppress CIS warnings. - If conflicts arise in these assets, please take game-side changes and ignore these. Change 3484245 by Max.Chen Sequencer: Evaluate on end scrub. This fixes a bug where audio doesn't evaluate in a stopped position at the end of scrubbing, causing it to not stop all sounds. This fixes a bug introduced from 3365018 where evaluate on end scrub was removed. #jira UE-45905 Change 3484263 by Max.Chen Sequencer: Fix crash on forcing refresh of details panel on release. #jira UE-45911 Change 3484431 by Andrew.Rodham Resaving assets that contain legacy data to suppress CIS warnings. - If conflicts arise in these assets, please take game-side changes and ignore these. Change 3484474 by Alexis.Matte Fix the morph target animation curve name matching. #jira UE-20294 Change 3484475 by Alexis.Matte When removing a LOD, make sure we remove all morph target data associate to the LOD. Change 3484489 by Nick.Darnell PR #3668: UE-45908: Cache debug line locations when performing a LineTraceMulti (Contributed by projectgheist) #jira UE-45908 Change 3484692 by Nick.Darnell Slate - Reverting a change from a game stream. All Arranged Children don't need to allocated 42 to begin with. Do need to initialize WidgetPaths better. Change 3484703 by Nick.Darnell Player Input - Making the input event loop for players obey EKeys::NUM_TOUCH_KEYS, rather than being set to Touch10, as the maximum touch input amount, to make supporting greater than 10 touches easier. Also making the seeding of keys use EKeys::NUM_TOUCH_KEYS. #jira UE-43213 Change 3484918 by Jamie.Dale Fixed font measuring regression with RTL text RTL applies the character count to the next glyph, so it shouldn't process the end of the loop (this was how the older code used to work). Change 3485718 by Nick.Darnell Editor - Removing Super Search & User Feedback button. Change 3485719 by Nick.Darnell Portal - Removing SuperSearch. Change 3485751 by Matt.Kuhlenschmidt Fix crash accessing platformer game menu if the menu is open during a console based load #jira UE-45950 Change 3486047 by Arciel.Rekman Linux: add OpenEXR implementation (UE-40270). #jira UE-40270 Change 3486467 by Max.Chen Sequencer: Reset max tick rate when destroyed. #jira UE-45956 Change 3486477 by Max.Chen Sequencer: Refresh outliner when column is removed. #jira UE-45891 Change 3486667 by Andrew.Rodham Added missing include Change 3486724 by Andrew.Rodham Sequencer: Fixed curves with no default value, and no keys being evaluated and applied to properties - Also fixed an edge case where a zero (but non-animated) channel could be applied to a final transform Change 3486730 by Alexis.Matte In the Auto-Reimport options, hide the mout point only for the default /Game/ folder #UE-45684 Change 3486749 by Alexis.Matte Make sure the parent window of the monitor directory browse folder is set properly #jira UE-45682 Change 3486805 by Matt.Kuhlenschmidt Additional safety against invalid objects being accessed by slate Change 3486848 by Alexis.Matte Make sure Monitor folder feature support root mount point map folder During auto import, give priority to asset import factory over the scene import factory #jira UE-45691 Change 3486879 by Andrew.Rodham Removing obsolete QA assets Change 3486950 by Nick.Darnell PR #2281: Scrollbar missing features and SScrollbar fixes (Contributed by SNikon) Review - made some adjustments from the original. Change 3486954 by Nick.Darnell Slate - Moving the STableViewBase over to the FOverscroll class, rather than it's own clone. Change 3486967 by Nick.Darnell Slate - Fixing some HDPI calculations for fitting new windows on screen, it was using the unscaled size of the widgets for fitting, when it needed to scale them up. Change 3486970 by Andrew.Rodham Sequencer: Delay mouse capture until drag for sequencer time slider - Fixes context menus not opening as a result of mouse capture being taken on mouse down #jira UE-45937 Change 3486984 by Andrew.Rodham Sequencer: Improved blending type iconography Change 3486996 by Nick.Darnell UMG - Adding a way for games to opt-out of the slow widget path, to completely prevent them from being cooked. UMG - The movie data is no longer cloned for each new instance that inhabits. It now keeps a reference to the now publically accessible movie scene data on the class instead. Change 3487070 by Andrew.Rodham Sequencer: Added graphics for key areas that represent empty space Change 3487195 by Andrew.Rodham Sequencer: Changed evaluation groups to always flush implicitly - Due to the latent nature of blended token types, it's no longer safe to rely solely on execution token order between tracks - This fixes an issue where events set in the PostEvaluation stage were executed before blended token actuation Change 3487322 by Nick.Darnell PR #2457: Add .gitdeps.xml-files for plugins support (Contributed by bozaro) Change 3487363 by Nick.Darnell PR #2481: Fix for packing of a project with users plugins (Contributed by yatagarasu25) Change 3487439 by Nick.Darnell PR #2642: Changed private to protected in SVirtualJoystick.h (Contributed by Skylonxe) Change 3487500 by Arciel.Rekman Removed LinuxNativeDialogs. - No longer used; has been superceded by SlateDialogs since UE 4.8 (2 years ago). Change 3487630 by Lauren.Ridge Don't create Landscape Info Maps for Editor Preview Worlds or thumbnail worlds #jira UE-44885 Change 3487864 by Matt.Kuhlenschmidt Exposed the asset registry to blueprints and script. Works in editor scripts and runtime scripts AssetRegistry is now a UInterface object. Blueprint users can access various asset registry methods using the asset registry interface (via GetAssetRegistry) and various static helpers in the AssetRegistryHelpers object C++ users should still continue to use IAssetRegistry. Change 3487879 by Matt.Kuhlenschmidt Renamed asset tools uobject helper to UAssetToolsHelpers Change 3487926 by Lauren.Ridge Fixing reset to default not showing up for custom widgets #jira UE-44164 Change 3488184 by Matt.Kuhlenschmidt PR #3656: Make References/Referencers List copyable (Contributed by user37337) #jira UE-45763 Change 3488240 by Matt.Kuhlenschmidt Fix compiler issue Change 3488350 by Lauren.Ridge Landscape info map transactional state is based on its world's transactional state #jira UE-44885 #jira UE-46019 Change 3488412 by Matt.Kuhlenschmidt Fix reset to default showing up in two different places for some customizations Change 3488413 by Matt.Kuhlenschmidt Fix slate font customization Change 3488414 by Matt.Kuhlenschmidt Fix slate font customization Change 3488415 by Matt.Kuhlenschmidt Missed file Change 3488565 by Arciel.Rekman Add pretty printers for gdb (UETOOL-1171). - Committing shelf by Cengiz.Terzibas, with some modifications. #jira UETOOL-1171 Change 3489094 by Nick.Darnell Slate - The Slate RHI Renderer now caches the TextureLODGroups so that it can properly lookup the filtering of different texture groups that are set to Default, instead of a particular filter override on a texture. Engine/Rendering - Simplifying some of the setup logic in TextureLODSettings so that code is shared for setting them up properly after loading from a config file. Change 3489095 by Nick.Darnell PR #2699: GameViewportClient - Added a method to allow setting the viewport cur. (Contributed by rfenner) Review - Fixed spacing. Change 3489108 by Matt.Kuhlenschmidt Fix deprecation warning Change 3489120 by Nick.Darnell PR #3478: Fix possible UComboBoxString crash (Contributed by nakosung) Change 3489147 by Andrew.Rodham Sequencer: Adding return value to function to appease static analysis - This function is never compiled, and if it is, it won't compile, but static analysis doesn't know that Change 3489264 by Nick.Darnell Testing - Finishing the thought behind an enum comment. Change 3489265 by Nick.Darnell PR #2750: UE-35164: Button padding (Contributed by projectgheist) Change 3489267 by Nick.Darnell PR #3645: UE-45464: Handle SSlider mouse interaction more accurately (Contributed by projectgheist) Change 3489632 by Arciel.Rekman Correctness changes to MallocPoisonProxy. - Missing forwarding functions added. Incorrect comment removed. - Change by Steve.Robb, doing here so it is in 4.17. Change 3489689 by Arciel.Rekman More MallocPoisonProxy changes I missed in previous CL. Change 3489751 by Matt.Kuhlenschmidt Moved editor performance settings out of per-project settings so they can be shared across projects Change 3489837 by Lauren.Ridge Keyboard shortcut for entering/leaving VR Mode is now Alt+V Change 3491082 by Arciel.Rekman Linux: better fix for the crash due to name collision (UE-46040). - Put classes in Sequencer module into Sequencer namespace instead of SceneOutliner namespace. - Undid change in the SceneOutliner module. #jira UE-46040 Change 3491096 by Arciel.Rekman Fix UAT compilation on the newest mono. Change 3491240 by Max.Chen Sequencer: Disable key button when allow level edits only is on. #jira UE-46060 Change 3491406 by Yannick.Lange Fix editor crashes when opening a project that includes a plugin with more than two custom Volume classes. This issue was caused because registering show volume commands is based on finding volume classes. Finding these classes at multiple times resulted in a mismatch of the returned array of volume classes because modules/plugins were still being loaded. #jira UE-45806 Change 3491559 by Alexis.Matte Make sure we use the good preview mesh when doing a preview #jira UE-45963 Change 3491563 by Alexis.Matte Fix crash with staticmesh editor LodLevel selection Change 3491564 by Nick.Darnell UMG - Fixing an offset with the grab handles in HDPI mode. Change 3491595 by Nick.Darnell Editor - Fixing a clipping artifact in the pin type dropdown in the blueprint editor. Change 3491604 by Nick.Darnell Back out changelist 3489265 Change 3491615 by Arciel.Rekman Added malloc replay proxy (Linux only for now). - Allows to dump malloc callstream (without regard to threads) and replay later to study the behavior of different mallocs and/or repro problems. Change 3491684 by Arciel.Rekman Added FMalloc functions I missed. - Also moved function bodies into the .cpp file, this does not make a difference in performance in this case. Change 3491692 by Matt.Kuhlenschmidt Some minor fixes to the static mesh editor - Fix UV combo button looking non-standard on the toolbar - Fix a few combo buttons in the details panel looking too big. Change 3491702 by Arciel.Rekman Do not compile replay proxy-specific code when not used. Change 3491717 by Michael.Dupuis #jira UE-35083: The component is now the owner of the PerInstanceRenderData instead of the proxy Add an Update path to only update specified instances range Always call BuildTreeIfOutdated so we have a standard code path to make sure static mesh are fully loaded before trying to build the tree Moved the Instance Buffer aysnc to the base class, as it's not related to UHierarchicalInstancedStaticMeshComponent Expose a new property to decide if we require dynamic instance buffer Change 3491758 by Matt.Kuhlenschmidt Fix crash on static mesh editor shutdown Change 3491873 by Cody.Albert Fixed clipping issue in Sequencer curve editor #rnx Change 3491956 by Matt.Kuhlenschmidt Fix crash opening the Previewing sub-menu in the level editor settings menu #jira UE-46095 Change 3492046 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3492076 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3492165 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3492222 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3492274 by Michael.Dupuis #jira UE-46105: Fixed Clang warning Change 3492338 by andrew.porter QAGame: Testing ensure when submitting Change 3492371 by Nick.Darnell UMG - Reverting the animation sharing, cossed GLEO regressions in cooking. Will look for a better solution. Change 3492462 by Matt.Kuhlenschmidt Fix ensure checking in files through perforce Change 3492491 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3492505 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3492517 by Jamie.Dale The package localization ID is no longer used at all at runtime, and is now truly editor-only This should have always been the case, but it was leaked into manifest/archives/PO files in 4.14, and while 4.15 removed it from PO files it was still present in the manifest/archives. This change removes it entirely (unless gathering editor-only data, and even then the PO file will still collapse the entries together for translation), and the deprecated 4.14 export behavior will now produce an error if you attempt to use it. After taking this change you'll need to run a gather, import, and compile of your LocRes files to update your game localization to use the new localization IDs. Change 3492630 by Nick.Darnell UMG - Removing some extra cleanup code that's probably overkill and is causing a crash for uses of "Within" in class meta. #jira UE-46124 Change 3492692 by Matt.Kuhlenschmidt Fix drop shadows inheriting the outline color of the font. The outline should still appear but not have a different outline color from fill color Change3492714by Matt.Kuhlenschmidt Added outline with drop shadow to font automation test Change 3492737 by Matt.Kuhlenschmidt Fix linux Change 3492992 by tim.gautier Resaving Ocean Widget Blueprints / Sequences to resolve Legacy Sequence Data warnings #jira UE-46132 Change 3493089 by Jamie.Dale Ensure that the composite font of a font asset is flushed when the font object is GC'd Change 3493322 by Jamie.Dale Fixing null crash #jira UE-45758 Change 3494467 by Andrew.Rodham Fix Xbox warning Change3494852by tim.gautier QAGame: Changed streaming method of QA-EditorSmoke-Landscape to Always Loaded Change 3494853 by Nick.Darnell Another attempt at fixing the automation blueprint SA warning. Change 3494896 by Arciel.Rekman Fix possible null pointer access during Vulkan init. - May fix static analysis warnings in UE-46142, although warnings seem to be referring to something else. #jira UE-46142 Change 3494987 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3495010 by Matt.Kuhlenschmidt Adding additional logging to track down html5 issue Change 3495212 by Michael.Dupuis #jira UE-46143: Properly init the InstanceRenderData during the cooking phase (required by fortnite) Change 3495536 by Jamie.Dale Updating UGameEngine to call its Super::PreExit after performing its own teardown This prevents UEngine cleaning up resources that UGameEngine still needs. #jira UE-46159 Change 3495551 by Arciel.Rekman Another attempt to fix analyzer problem (UE-46142). Change 3495794 by Jamie.Dale Fixing some font cooking warnings by splitting out font faces from their font assets #jira UE-45843 Change 3495905 by Matt.Kuhlenschmidt Fix USD crash when importing a meshwith no material [CL 3499771 by Matt Kuhlenschmidt in Main branch]
1558 lines
52 KiB
C++
1558 lines
52 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "STreeMap.h"
|
|
#include "Rendering/DrawElements.h"
|
|
#include "Widgets/SWindow.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Framework/Application/MenuStack.h"
|
|
#include "Fonts/FontMeasure.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Widgets/Input/SEditableTextBox.h"
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "STreeMap"
|
|
|
|
|
|
namespace STreeMapDefs
|
|
{
|
|
/** Minimum pixels the user must drag the cursor before a drag+drop starts on a visual */
|
|
const float MinCursorDistanceForDraggingVisual = 3.0f;
|
|
}
|
|
|
|
|
|
|
|
void STreeMap::Construct( const FArguments& InArgs, const TSharedRef<FTreeMapNodeData>& InTreeMapNodeData, const TSharedPtr< ITreeMapCustomization >& InCustomization )
|
|
{
|
|
CurrentUndoLevel = INDEX_NONE;
|
|
Customization = InCustomization;
|
|
|
|
TreeMapNodeData = InTreeMapNodeData;
|
|
|
|
AllowEditing = InArgs._AllowEditing;
|
|
|
|
BackgroundImage = InArgs._BackgroundImage;
|
|
NodeBackground = InArgs._NodeBackground;
|
|
HoveredNodeBackground = InArgs._HoveredNodeBackground;
|
|
BorderPadding = InArgs._BorderPadding;
|
|
RelativeDragStartMouseCursorPos = FVector2D::ZeroVector;
|
|
RelativeMouseCursorPos = FVector2D::ZeroVector;
|
|
|
|
{
|
|
FTreeMapOptions TreeMapOptions;
|
|
NameFont = TreeMapOptions.NameFont;
|
|
Name2Font = TreeMapOptions.Name2Font;
|
|
CenterTextFont = TreeMapOptions.CenterTextFont;
|
|
|
|
if( InArgs._NameFont.IsSet() )
|
|
{
|
|
NameFont = InArgs._NameFont;
|
|
}
|
|
if( InArgs._Name2Font.IsSet() )
|
|
{
|
|
Name2Font = InArgs._Name2Font;
|
|
}
|
|
if( InArgs._CenterTextFont.IsSet() )
|
|
{
|
|
CenterTextFont = InArgs._CenterTextFont;
|
|
}
|
|
}
|
|
|
|
MinimumVisualTreeNodeSize = InArgs._MinimumVisualTreeNodeSize;
|
|
TopLevelContainerOuterPadding = InArgs._TopLevelContainerOuterPadding;
|
|
NestedContainerOuterPadding = InArgs._NestedContainerOuterPadding;
|
|
ContainerInnerPadding = InArgs._ContainerInnerPadding;
|
|
ChildContainerTextPadding = InArgs._ChildContainerTextPadding;
|
|
|
|
TreeMapSize = FVector2D( 0, 0 );
|
|
NavigateAnimationCurve.AddCurve( 0.0f, InArgs._NavigationTransitionTime, ECurveEaseFunction::CubicOut );
|
|
MouseOverVisual = NULL;
|
|
DraggingVisual = NULL;
|
|
DragVisualDistance = 0.0f;
|
|
|
|
bIsNamingNewNode = false;
|
|
|
|
HighlightPulseStartTime = -99999.0;
|
|
|
|
OnTreeMapNodeDoubleClicked = InArgs._OnTreeMapNodeDoubleClicked;
|
|
|
|
if( Customization.IsValid() )
|
|
{
|
|
SizeNodesByAttribute = Customization->GetDefaultSizeByAttribute();
|
|
ColorNodesByAttribute = Customization->GetDefaultColorByAttribute();
|
|
}
|
|
|
|
const bool bShouldPlayTransition = false;
|
|
SetTreeRoot( TreeMapNodeData.ToSharedRef(), bShouldPlayTransition );
|
|
}
|
|
|
|
|
|
void STreeMap::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
|
|
{
|
|
SLeafWidget::Tick( AllottedGeometry, InCurrentTime, InDeltaTime );
|
|
|
|
if( AllottedGeometry.Size != TreeMapSize || !TreeMap.IsValid() )
|
|
{
|
|
// Stop renaming a node if we were doing that
|
|
StopRenamingNode();
|
|
|
|
// Make a tree map!
|
|
FTreeMapOptions TreeMapOptions;
|
|
TreeMapOptions.TreeMapType = ETreeMapType::Squarified;
|
|
TreeMapOptions.DisplayWidth = AllottedGeometry.GetLocalSize().X;
|
|
TreeMapOptions.DisplayHeight = AllottedGeometry.GetLocalSize().Y;
|
|
TreeMapOptions.TopLevelContainerOuterPadding = TopLevelContainerOuterPadding;
|
|
TreeMapOptions.NestedContainerOuterPadding = NestedContainerOuterPadding;
|
|
TreeMapOptions.ContainerInnerPadding = ContainerInnerPadding;
|
|
TreeMapOptions.NameFont = NameFont.Get();
|
|
TreeMapOptions.Name2Font = Name2Font.Get();
|
|
TreeMapOptions.CenterTextFont = CenterTextFont.Get();
|
|
TreeMapOptions.FontSizeChangeBasedOnDepth = 2; // @todo treemap custom: Expose as a customization option to STreeMap
|
|
TreeMapOptions.MinimumInteractiveNodeSize = MinimumVisualTreeNodeSize;
|
|
TreeMap = ITreeMap::CreateTreeMap( TreeMapOptions, ActiveRootTreeMapNode.ToSharedRef() );
|
|
|
|
TreeMapSize = AllottedGeometry.Size;
|
|
|
|
CachedNodeVisuals = TreeMap->GetVisuals();
|
|
MouseOverVisual = NULL;
|
|
DraggingVisual = NULL;
|
|
|
|
// Map the new visuals back to the old ones!
|
|
NodeVisualIndicesToLastIndices.Reset();
|
|
TSet<int32> ValidLastIndices;
|
|
for( auto VisualIndex = 0; VisualIndex < CachedNodeVisuals.Num(); ++VisualIndex )
|
|
{
|
|
const auto& Visual = CachedNodeVisuals[ VisualIndex ];
|
|
for( auto LastVisualIndex = 0; LastVisualIndex < LastCachedNodeVisuals.Num(); ++LastVisualIndex )
|
|
{
|
|
const auto& LastVisual = LastCachedNodeVisuals[ LastVisualIndex ];
|
|
if( LastVisual.NodeData == Visual.NodeData )
|
|
{
|
|
NodeVisualIndicesToLastIndices.Add( VisualIndex, LastVisualIndex );
|
|
ValidLastIndices.Add( LastVisualIndex );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find all of the orphans
|
|
OrphanedLastIndices.Reset();
|
|
for( auto LastVisualIndex = 0; LastVisualIndex < LastCachedNodeVisuals.Num(); ++LastVisualIndex )
|
|
{
|
|
if( !ValidLastIndices.Contains( LastVisualIndex ) )
|
|
{
|
|
OrphanedLastIndices.Add( LastVisualIndex );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void STreeMap::MakeBlendedNodeVisual( const int32 VisualIndex, const float NavigationAlpha, FTreeMapNodeVisualInfo& OutVisual ) const
|
|
{
|
|
OutVisual = CachedNodeVisuals[ VisualIndex ]; // NOTE: Copying visual
|
|
|
|
// Do we need to interp?
|
|
if( NavigationAlpha < 1.0f )
|
|
{
|
|
// Did the visual exist before we navigated?
|
|
const int32* LastVisualIndexPtr = NodeVisualIndicesToLastIndices.Find( VisualIndex );
|
|
if( LastVisualIndexPtr != NULL )
|
|
{
|
|
// It did exist!
|
|
const auto LastVisualIndex = *LastVisualIndexPtr;
|
|
const auto& LastVisual = LastCachedNodeVisuals[ LastVisualIndex ];
|
|
|
|
// Blend before "before" and "now"
|
|
OutVisual.Position = FMath::Lerp( LastVisual.Position, OutVisual.Position, NavigationAlpha );
|
|
OutVisual.Size = FMath::Lerp( LastVisual.Size, OutVisual.Size, NavigationAlpha );
|
|
|
|
// Do an HSV color lerp; it just looks more sensible!
|
|
OutVisual.Color = FLinearColor::LerpUsingHSV( LastVisual.Color, OutVisual.Color, NavigationAlpha );
|
|
|
|
// The blended visual is considered interactive only if both the new version and the old version were interactive
|
|
OutVisual.bIsInteractive = LastVisual.bIsInteractive && OutVisual.bIsInteractive;
|
|
}
|
|
else
|
|
{
|
|
// Didn't exist before. Fade in from nothing!
|
|
OutVisual.Color.A *= NavigationAlpha;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int32 STreeMap::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
|
|
{
|
|
const bool bEnabled = ShouldBeEnabled( bParentEnabled );
|
|
const auto DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
|
|
|
|
// Draw background border layer
|
|
{
|
|
const FSlateBrush* ThisBackgroundImage = BackgroundImage.Get();
|
|
FSlateDrawElement::MakeBox(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry(),
|
|
ThisBackgroundImage,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * ThisBackgroundImage->TintColor.GetColor( InWidgetStyle )
|
|
);
|
|
}
|
|
|
|
float NavigationAlpha = NavigateAnimationCurve.GetLerp();
|
|
|
|
|
|
// Draw tree map
|
|
if( TreeMap.IsValid() )
|
|
{
|
|
// Figure out all the nodes we need to draw in a big ordered list. This can sometimes include nodes from the previous tree map we were
|
|
// looking at before we "navigated", as those nodes will be briefly visible during the animated transition
|
|
static TArray< const FTreeMapNodeVisualInfo* > NodeVisualsToDraw;
|
|
NodeVisualsToDraw.Reset();
|
|
|
|
// Draw the orphan's first
|
|
// @todo treemap visuals: During transitions, nodes look "on top" should be drawn last. Right now they will underlap and overlap adjacents at the same time. Looks weird.
|
|
for( auto OrphanedVisualIndex : OrphanedLastIndices )
|
|
{
|
|
NodeVisualsToDraw.Add( &LastCachedNodeVisuals[ OrphanedVisualIndex ] );
|
|
}
|
|
const int32 FirstNewVisualIndex = NodeVisualsToDraw.Num();
|
|
for( const auto& Visual : CachedNodeVisuals )
|
|
{
|
|
NodeVisualsToDraw.Add( &Visual );
|
|
}
|
|
|
|
const FTreeMapNodeData* RenamingNodeDataPtr = RenamingNodeData.Pin().Get();
|
|
|
|
// Draw background boxes for all visuals
|
|
{
|
|
++LayerId;
|
|
|
|
const FSlateBrush* ThisHoveredNodeBackground = HoveredNodeBackground.Get();
|
|
const FSlateBrush* ThisNodeBackground = NodeBackground.Get();
|
|
|
|
for( auto DrawVisualIndex = 0; DrawVisualIndex < NodeVisualsToDraw.Num(); ++DrawVisualIndex )
|
|
{
|
|
FTreeMapNodeVisualInfo BlendedVisual;
|
|
if( DrawVisualIndex >= FirstNewVisualIndex )
|
|
{
|
|
MakeBlendedNodeVisual( DrawVisualIndex - FirstNewVisualIndex, NavigationAlpha, BlendedVisual );
|
|
}
|
|
else
|
|
{
|
|
// This is an orphan
|
|
BlendedVisual = *NodeVisualsToDraw[ DrawVisualIndex ];
|
|
BlendedVisual.Color.A *= 1.0f - NavigationAlpha; // Fade orphans out when navigating
|
|
}
|
|
|
|
// Don't draw if completely faded out
|
|
if( BlendedVisual.Color.A > KINDA_SMALL_NUMBER )
|
|
{
|
|
const bool bIsMouseOverNode = MouseOverVisual != NULL && MouseOverVisual->NodeData == BlendedVisual.NodeData;
|
|
|
|
// Draw the visual's background box
|
|
const FVector2D VisualPosition = BlendedVisual.Position;
|
|
const FSlateRect VisualClippingRect = TransformRect( AllottedGeometry.GetAccumulatedLayoutTransform(), FSlateRect( VisualPosition, VisualPosition + BlendedVisual.Size ) );
|
|
const auto VisualPaintGeometry = AllottedGeometry.ToPaintGeometry( VisualPosition, BlendedVisual.Size );
|
|
auto DrawColor = InWidgetStyle.GetColorAndOpacityTint() * ThisNodeBackground->TintColor.GetColor( InWidgetStyle ) * BlendedVisual.Color;
|
|
FSlateDrawElement::MakeBox(
|
|
OutDrawElements,
|
|
LayerId,
|
|
VisualPaintGeometry,
|
|
bIsMouseOverNode ? ThisHoveredNodeBackground : ThisNodeBackground,
|
|
DrawEffects,
|
|
DrawColor
|
|
);
|
|
|
|
if( BlendedVisual.NodeData->BackgroundBrush != nullptr )
|
|
{
|
|
// Preserve aspect ratio, but stretch to fill the whole rectangle, even if we have to crop the smaller edge
|
|
const float LargestSize = FMath::Max( BlendedVisual.Size.X, BlendedVisual.Size.Y );
|
|
const FVector2D BackgroundSize( LargestSize, LargestSize );
|
|
FVector2D BackgroundPosition = VisualPosition;
|
|
if( BlendedVisual.Size.X > BlendedVisual.Size.Y ) // Is our box wider than tall?
|
|
{
|
|
const float SizeDifference = LargestSize - BlendedVisual.Size.Y;
|
|
BackgroundPosition.Y -= SizeDifference * 0.5f;
|
|
}
|
|
else
|
|
{
|
|
const float SizeDifference = LargestSize - BlendedVisual.Size.X;
|
|
BackgroundPosition.X -= SizeDifference * 0.5f;
|
|
}
|
|
const auto BackgroundPaintGeometry = AllottedGeometry.ToPaintGeometry( BackgroundPosition, BackgroundSize );
|
|
|
|
const FSlateRect BackgroundClippingRect = VisualClippingRect.InsetBy( FMargin( 1 ) );
|
|
|
|
// Draw the background brush
|
|
FSlateDrawElement::MakeBox(
|
|
OutDrawElements,
|
|
LayerId,
|
|
BackgroundPaintGeometry,
|
|
BlendedVisual.NodeData->BackgroundBrush,
|
|
DrawEffects,
|
|
DrawColor );
|
|
}
|
|
|
|
const bool bIsHighlightPulseNode = HighlightPulseNode.IsValid() && HighlightPulseNode.Pin().Get() == BlendedVisual.NodeData;
|
|
if( bIsHighlightPulseNode )
|
|
{
|
|
const float HighlightPulseAnimDuration = 1.5f; // @todo treemap: Probably should be customizable in the widget's settings
|
|
const float HighlightPulseAnimProgress = ( FSlateApplication::Get().GetCurrentTime() - HighlightPulseStartTime ) / HighlightPulseAnimDuration;
|
|
if( HighlightPulseAnimProgress >= 0.0f && HighlightPulseAnimProgress <= 1.0f )
|
|
{
|
|
DrawColor = FLinearColor( 1.0f, 1.0f, 1.0f, FMath::MakePulsatingValue( HighlightPulseAnimProgress, 6.0f, 0.5f ) );
|
|
FSlateDrawElement::MakeBox(
|
|
OutDrawElements,
|
|
LayerId,
|
|
VisualPaintGeometry,
|
|
ThisHoveredNodeBackground,
|
|
DrawEffects,
|
|
DrawColor );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Draw text layers. We draw it twice for all visuals. Once for a drop shadow, and then again for the foreground text.
|
|
const TSharedRef< FSlateFontMeasure >& FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
|
for( auto TextLayerIndex = 0; TextLayerIndex < 2; ++TextLayerIndex )
|
|
{
|
|
++LayerId;
|
|
|
|
// @todo treemap: Want ellipses for truncated text
|
|
// @todo treemap: Squash text based on box size rather than strictly depth?
|
|
|
|
const float ShadowOpacity = 0.5f;
|
|
const auto ShadowOffset = ( TextLayerIndex == 0 ) ? FVector2D( -1.0f, 1.0f ) : FVector2D::ZeroVector;
|
|
const auto TextColorScale = ( TextLayerIndex == 0 ) ? FLinearColor( 0.0f, 0.0f, 0.0f, ShadowOpacity ) : FLinearColor::White;
|
|
|
|
for( auto DrawVisualIndex = 0; DrawVisualIndex < NodeVisualsToDraw.Num(); ++DrawVisualIndex )
|
|
{
|
|
FTreeMapNodeVisualInfo BlendedVisual;
|
|
if( DrawVisualIndex >= FirstNewVisualIndex )
|
|
{
|
|
MakeBlendedNodeVisual( DrawVisualIndex - FirstNewVisualIndex, NavigationAlpha, BlendedVisual );
|
|
}
|
|
else
|
|
{
|
|
// This is an orphan
|
|
BlendedVisual = *NodeVisualsToDraw[ DrawVisualIndex ];
|
|
BlendedVisual.Color.A *= 1.0f - NavigationAlpha; // Fade orphans out when navigating
|
|
}
|
|
|
|
// Don't draw text if completely faded out, or if not interactive
|
|
if( BlendedVisual.Color.A > KINDA_SMALL_NUMBER && BlendedVisual.bIsInteractive )
|
|
{
|
|
// Don't draw a title for a node that is actively being renamed -- the text box is already visible, after all
|
|
if( BlendedVisual.NodeData != RenamingNodeDataPtr )
|
|
{
|
|
const FVector2D VisualPosition = BlendedVisual.Position;
|
|
|
|
// Allow text to fade out with its parent box
|
|
auto VisualTextColor = TextColorScale;
|
|
VisualTextColor.A *= BlendedVisual.Color.A;
|
|
|
|
const bool bIsMouseOverNode = MouseOverVisual != NULL && MouseOverVisual->NodeData == BlendedVisual.NodeData;
|
|
|
|
// If the visual is crushed down pretty small in screen space, we don't want to bother even try drawing text
|
|
const FVector2D ScreenSpaceVisualSize( AllottedGeometry.Scale * BlendedVisual.Size );
|
|
if( ScreenSpaceVisualSize.X > 20 )
|
|
{
|
|
const auto VisualPaintGeometry = AllottedGeometry.ToPaintGeometry( VisualPosition, BlendedVisual.Size );
|
|
|
|
// Clip the text to the visual's rectangle, with some extra inner padding to avoid overlapping the visual's border
|
|
auto TextClippingRect = FSlateRect( VisualPaintGeometry.DrawPosition, VisualPaintGeometry.DrawPosition + VisualPaintGeometry.GetLocalSize() );
|
|
TextClippingRect = TextClippingRect.IntersectionWith( MyCullingRect );
|
|
TextClippingRect = TextClippingRect.InsetBy( FMargin( ChildContainerTextPadding, 0, ChildContainerTextPadding, 0 ) );
|
|
if( TextClippingRect.IsValid() )
|
|
{
|
|
// Name (first line)
|
|
float NameTextHeight = 0.0f;
|
|
if( !BlendedVisual.NodeData->Name.IsEmpty() )
|
|
{
|
|
const FVector2D TextSize = FontMeasureService->Measure( BlendedVisual.NodeData->Name, BlendedVisual.NameFont );
|
|
NameTextHeight = TextSize.Y;
|
|
const auto TextX = VisualPosition.X + FMath::Max( ChildContainerTextPadding, (float)BlendedVisual.Size.X * 0.5f - TextSize.X * 0.5f ); // Clamp to left edge if cropped, so the user can at least read the beginning
|
|
const auto TextY = VisualPosition.Y + ChildContainerTextPadding;
|
|
|
|
FSlateDrawElement::MakeText(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToOffsetPaintGeometry( ShadowOffset + FVector2D( TextX, TextY ) ),
|
|
BlendedVisual.NodeData->Name,
|
|
BlendedVisual.NameFont,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * VisualTextColor );
|
|
}
|
|
|
|
// Name (second line)
|
|
float Name2TextHeight = 0.0f;
|
|
if( !BlendedVisual.NodeData->Name2.IsEmpty() && BlendedVisual.NodeData->IsLeafNode() )
|
|
{
|
|
const FVector2D TextSize = FontMeasureService->Measure( BlendedVisual.NodeData->Name2, BlendedVisual.Name2Font );
|
|
Name2TextHeight = TextSize.Y;
|
|
const auto TextX = VisualPosition.X + FMath::Max( ChildContainerTextPadding, (float)BlendedVisual.Size.X * 0.5f - TextSize.X * 0.5f ); // Clamp to left edge if cropped, so the user can at least read the beginning
|
|
const auto TextY = VisualPosition.Y + ChildContainerTextPadding + NameTextHeight;
|
|
|
|
// Clip the text to the visual's rectangle, with some extra inner padding to avoid overlapping the visual's border
|
|
FSlateDrawElement::MakeText(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToOffsetPaintGeometry( ShadowOffset + FVector2D( TextX, TextY ) ),
|
|
BlendedVisual.NodeData->Name2,
|
|
BlendedVisual.Name2Font,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * VisualTextColor );
|
|
}
|
|
|
|
// Center text
|
|
if( !BlendedVisual.NodeData->CenterText.IsEmpty() && BlendedVisual.NodeData->IsLeafNode() )
|
|
{
|
|
// If the visual is smaller than the text we're going to draw, try using a smaller font
|
|
const FSlateFontInfo* BlendedVisualCenterTextFont = &BlendedVisual.CenterTextFont;
|
|
const FVector2D FullTextSize = FontMeasureService->Measure( BlendedVisual.NodeData->CenterText, *BlendedVisualCenterTextFont );
|
|
if( ScreenSpaceVisualSize.X < FullTextSize.X || ScreenSpaceVisualSize.Y < FullTextSize.Y ) // @todo treemap: assumption that NameFont will be smaller than CenterText font
|
|
{
|
|
BlendedVisualCenterTextFont = &BlendedVisual.NameFont;
|
|
}
|
|
|
|
const FVector2D TextSize = FontMeasureService->Measure( BlendedVisual.NodeData->CenterText, *BlendedVisualCenterTextFont );
|
|
const auto TextX = VisualPosition.X + FMath::Max( ChildContainerTextPadding, (float)BlendedVisual.Size.X * 0.5f - TextSize.X * 0.5f ); // Clamp to left edge if cropped, so the user can at least read the beginning
|
|
const auto TextY = VisualPosition.Y + FMath::Max( ChildContainerTextPadding + NameTextHeight + Name2TextHeight, (float)BlendedVisual.Size.Y * 0.5f - TextSize.Y * 0.5f ); // Clamp to bottom of first line of text if cropped
|
|
|
|
// Clip the text to the visual's rectangle, with some extra inner padding to avoid overlapping the visual's border
|
|
FSlateDrawElement::MakeText(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToOffsetPaintGeometry( ShadowOffset + FVector2D( TextX, TextY ) ),
|
|
BlendedVisual.NodeData->CenterText,
|
|
*BlendedVisualCenterTextFont,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * VisualTextColor );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if( DraggingVisual != NULL && DragVisualDistance >= STreeMapDefs::MinCursorDistanceForDraggingVisual )
|
|
{
|
|
const FVector2D DragStartCursorPos = RelativeDragStartMouseCursorPos;
|
|
const FVector2D NewCursorPos = RelativeMouseCursorPos;
|
|
|
|
const FVector2D SplineStart = DragStartCursorPos;
|
|
const FVector2D SplineEnd = NewCursorPos;
|
|
|
|
const FVector2D SplineStartDir = ( DragStartCursorPos - ( DraggingVisual->Position + DraggingVisual->Size * 0.5f ) ); // @todo treemap: Point away from the center of the dragged visual
|
|
const FVector2D SplineEndDir = FVector2D( 0.0f, 200.0f ); // @todo treemap: Probably needs better customization support
|
|
|
|
// @todo treemap: Draw line in red if drop won't work?
|
|
|
|
// Draw two passes, the first one is an drop shadow
|
|
for( auto SplineLayerIndex = 0; SplineLayerIndex < 2; ++SplineLayerIndex )
|
|
{
|
|
++LayerId;
|
|
|
|
const float ShadowOpacity = 0.5f;
|
|
const float SplineThickness = ( SplineLayerIndex == 0 ) ? 5.0f : 4.0f;
|
|
const auto ShadowOffset = ( SplineLayerIndex == 0 ) ? FVector2D( -1.0f, 1.0f ) : FVector2D::ZeroVector;
|
|
const auto SplineColorScale = ( SplineLayerIndex == 0 ) ? FLinearColor( 0.0f, 0.0f, 0.0f, ShadowOpacity ) : FLinearColor::White;
|
|
|
|
FSlateDrawElement::MakeSpline(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry( ShadowOffset, FVector2D( 1.0, 1.0f ) ),
|
|
SplineStart,
|
|
SplineStartDir,
|
|
SplineEnd,
|
|
SplineEndDir,
|
|
SplineThickness,
|
|
DrawEffects,
|
|
InWidgetStyle.GetColorAndOpacityTint() * DraggingVisual->Color * SplineColorScale );
|
|
}
|
|
}
|
|
|
|
|
|
return LayerId;
|
|
}
|
|
|
|
FVector2D STreeMap::ComputeDesiredSize(float LayoutScaleMultiplier) const
|
|
{
|
|
// TreeMap widgets have no desired size -- their size is always determined by their container
|
|
return FVector2D::ZeroVector;
|
|
}
|
|
|
|
FReply STreeMap::OnMouseMove( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
|
|
// Update window-relative cursor position
|
|
RelativeMouseCursorPos = InMyGeometry.AbsoluteToLocal( InMouseEvent.GetScreenSpacePosition() );
|
|
|
|
// Clear current hover
|
|
MouseOverVisual = NULL;
|
|
|
|
// Don't hover while transitioning. It looks bad!
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
// Figure out which visual the cursor is over
|
|
FTreeMapNodeVisualInfo* NodeVisualUnderCursor = FindNodeVisualUnderCursor( InMyGeometry, InMouseEvent.GetScreenSpacePosition() );
|
|
if( NodeVisualUnderCursor != NULL )
|
|
{
|
|
// Mouse is over a visual
|
|
MouseOverVisual = NodeVisualUnderCursor;
|
|
}
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
|
|
if( DraggingVisual != NULL )
|
|
{
|
|
DragVisualDistance += InMouseEvent.GetCursorDelta().Size();
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
void STreeMap::OnMouseLeave( const FPointerEvent& InMouseEvent )
|
|
{
|
|
MouseOverVisual = NULL;
|
|
}
|
|
|
|
FReply STreeMap::OnMouseButtonDown( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
if( InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton )
|
|
{
|
|
// Wait until we've finished animating a transition
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
if( AllowEditing.Get() )
|
|
{
|
|
// Figure out what was clicked on
|
|
FTreeMapNodeVisualInfo* NodeVisualUnderCursor = FindNodeVisualUnderCursor( InMyGeometry, InMouseEvent.GetScreenSpacePosition() );
|
|
if( NodeVisualUnderCursor != NULL )
|
|
{
|
|
// Mouse was pressed on a node
|
|
DraggingVisual = NodeVisualUnderCursor;
|
|
DragVisualDistance = 0.0f;
|
|
RelativeDragStartMouseCursorPos = InMyGeometry.AbsoluteToLocal( InMouseEvent.GetScreenSpacePosition() );
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
FReply STreeMap::OnMouseButtonUp( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
if( InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton )
|
|
{
|
|
auto* DroppedVisual = DraggingVisual;
|
|
DraggingVisual = NULL;
|
|
|
|
if( DroppedVisual != NULL && DragVisualDistance >= STreeMapDefs::MinCursorDistanceForDraggingVisual )
|
|
{
|
|
// Wait until we've finished animating a transition
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
// Find the node under the cursor
|
|
FTreeMapNodeVisualInfo* NodeVisualUnderCursor = FindNodeVisualUnderCursor( InMyGeometry, InMouseEvent.GetScreenSpacePosition() );
|
|
if( NodeVisualUnderCursor != NULL )
|
|
{
|
|
// The dropped node will become a child of the node we dropped onto
|
|
auto NewParentNode = NodeVisualUnderCursor->NodeData->AsShared();
|
|
auto DroppedNode = DroppedVisual->NodeData->AsShared();
|
|
|
|
// Reparent it!
|
|
ReparentNode( DroppedNode, NewParentNode );
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FTreeMapNodeVisualInfo* NodeVisualUnderCursor = FindNodeVisualUnderCursor( InMyGeometry, InMouseEvent.GetScreenSpacePosition() );
|
|
if( NodeVisualUnderCursor != NULL )
|
|
{
|
|
if( AllowEditing.Get() )
|
|
{
|
|
// Start renaming!
|
|
const bool bIsNewNode = false;
|
|
StartRenamingNode( InMyGeometry, NodeVisualUnderCursor->NodeData->AsShared(), NodeVisualUnderCursor->Position, bIsNewNode );
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
|
|
DragVisualDistance = 0.0f;
|
|
}
|
|
else if( InMouseEvent.GetEffectingButton() == EKeys::RightMouseButton )
|
|
{
|
|
// Show a pop-up menu!
|
|
ShowOptionsMenuAt( InMouseEvent );
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
FReply STreeMap::OnMouseButtonDoubleClick( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
|
|
if( InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton )
|
|
{
|
|
// Wait until we're done transitioning before allowing another transition
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
// Figure out what was clicked on
|
|
const FTreeMapNodeVisualInfo* NodeVisualUnderCursor = FindNodeVisualUnderCursor( InMyGeometry, InMouseEvent.GetScreenSpacePosition() );
|
|
if( NodeVisualUnderCursor != NULL )
|
|
{
|
|
// Double-clicked on a tree map visual! Check to see if we were asked to customize how double-click is handled.
|
|
if( OnTreeMapNodeDoubleClicked.IsBound() )
|
|
{
|
|
OnTreeMapNodeDoubleClicked.Execute( *NodeVisualUnderCursor->NodeData );
|
|
}
|
|
else
|
|
{
|
|
// Double-click was not overridden, so just do our default thing and re-root the tree directly on the node that was double-clicked on
|
|
|
|
// Re-root the tree
|
|
const bool bShouldPlayTransition = true;
|
|
SetTreeRoot( NodeVisualUnderCursor->NodeData->AsShared(), bShouldPlayTransition );
|
|
}
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
|
|
FReply STreeMap::OnMouseWheel( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
|
|
// Don't zoom in or out while already transitioning. It feels too frenetic.
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
if( MouseEvent.GetWheelDelta() > 0 )
|
|
{
|
|
// Figure out what was clicked on
|
|
const FTreeMapNodeVisualInfo* NodeVisualUnderCursor = FindNodeVisualUnderCursor( MyGeometry, MouseEvent.GetScreenSpacePosition() );
|
|
if( NodeVisualUnderCursor != NULL )
|
|
{
|
|
// From the node that was scrolled over, visits nodes upward until we find one whose parent is our current active root
|
|
FTreeMapNodeDataPtr NextNode = NodeVisualUnderCursor->NodeData->AsShared();
|
|
do
|
|
{
|
|
FTreeMapNodeDataPtr NodeParent;
|
|
if( NextNode->Parent != NULL )
|
|
{
|
|
NodeParent = NextNode->Parent->AsShared();
|
|
}
|
|
|
|
if( NodeParent == ActiveRootTreeMapNode )
|
|
{
|
|
break;
|
|
}
|
|
NextNode = NodeParent;
|
|
}
|
|
while( NextNode.IsValid() );
|
|
|
|
// Zoom in one level
|
|
if( NextNode.IsValid() )
|
|
{
|
|
const bool bShouldPlayTransition = true;
|
|
SetTreeRoot( NextNode.ToSharedRef(), bShouldPlayTransition );
|
|
|
|
if( !Reply.IsEventHandled() )
|
|
{
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( MouseEvent.GetWheelDelta() < 0 )
|
|
{
|
|
// Zoom out one level
|
|
if( ActiveRootTreeMapNode->Parent != NULL )
|
|
{
|
|
const bool bShouldPlayTransition = true;
|
|
SetTreeRoot( ActiveRootTreeMapNode->Parent->AsShared(), bShouldPlayTransition );
|
|
|
|
if( !Reply.IsEventHandled() )
|
|
{
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
|
|
void STreeMap::SetTreeRoot( const FTreeMapNodeDataRef& NewRoot, const bool bShouldPlayTransition )
|
|
{
|
|
if( ActiveRootTreeMapNode != NewRoot )
|
|
{
|
|
ActiveRootTreeMapNode = NewRoot;
|
|
|
|
// Freshen the visualization data for the node since it may be stale after being copied off the undo stack
|
|
RebuildTreeMap( bShouldPlayTransition );
|
|
}
|
|
}
|
|
|
|
|
|
void STreeMap::RebuildTreeMap( const bool bShouldPlayTransition )
|
|
{
|
|
// Stop renaming anything. We don't want pop-up windows persisting during the transition
|
|
StopRenamingNode();
|
|
|
|
// Keep track of the last tree
|
|
LastTreeMap = TreeMap;
|
|
LastCachedNodeVisuals = CachedNodeVisuals;
|
|
if( bShouldPlayTransition )
|
|
{
|
|
NavigateAnimationCurve.Play( AsShared() );
|
|
}
|
|
else
|
|
{
|
|
NavigateAnimationCurve.JumpToEnd();
|
|
}
|
|
|
|
// Kill the tree map so that it will be regenerated
|
|
TreeMap.Reset();
|
|
CachedNodeVisuals.Reset();
|
|
DraggingVisual = NULL;
|
|
MouseOverVisual = NULL;
|
|
|
|
// Customize the size and coloring of the source nodes before we rebuild it
|
|
ApplyVisualizationToNodes( ActiveRootTreeMapNode.ToSharedRef() );
|
|
}
|
|
|
|
|
|
FTreeMapNodeVisualInfo* STreeMap::FindNodeVisualUnderCursor( const FGeometry& MyGeometry, const FVector2D& ScreenSpaceCursorPosition )
|
|
{
|
|
if( TreeMap.IsValid() )
|
|
{
|
|
const FVector2D LocalCursorPosition = MyGeometry.AbsoluteToLocal( ScreenSpaceCursorPosition );
|
|
|
|
// NOTE: Iterating backwards so that child-most nodes are checked first
|
|
for( auto VisualIndex = CachedNodeVisuals.Num() - 1; VisualIndex >= 0; --VisualIndex )
|
|
{
|
|
auto& Visual = CachedNodeVisuals[ VisualIndex ];
|
|
|
|
// We only allow interactive visuals to be moused over
|
|
if( Visual.bIsInteractive )
|
|
{
|
|
const auto VisualRect = FSlateRect( Visual.Position, Visual.Position + Visual.Size );
|
|
if( VisualRect.ContainsPoint( LocalCursorPosition ) )
|
|
{
|
|
return &Visual;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool STreeMap::IsNavigationTransitionActive() const
|
|
{
|
|
return NavigateAnimationCurve.IsPlaying();
|
|
}
|
|
|
|
|
|
void STreeMap::TakeUndoSnapshot()
|
|
{
|
|
// If we've already undone some state, then we'll remove any undo state beyond the level that
|
|
// we've already undone up to.
|
|
if( CurrentUndoLevel != INDEX_NONE )
|
|
{
|
|
UndoStates.RemoveAt( CurrentUndoLevel, UndoStates.Num() - CurrentUndoLevel );
|
|
|
|
// Reset undo level as we haven't undone anything since this latest undo
|
|
CurrentUndoLevel = INDEX_NONE;
|
|
}
|
|
|
|
// Take an Undo snapshot before we change anything
|
|
UndoStates.Add( this->TreeMapNodeData->CopyNodeRecursively() );
|
|
|
|
// If we've hit the undo limit, then delete previous entries
|
|
const int32 MaxUndoLevels = 200; // @todo treemap custom: Make customizable in the settings of the widget?
|
|
if( UndoStates.Num() > MaxUndoLevels )
|
|
{
|
|
UndoStates.RemoveAt( 0 );
|
|
}
|
|
}
|
|
|
|
|
|
FTreeMapNodeDataPtr STreeMap::FindNodeInCopiedTree( const FTreeMapNodeDataRef& NodeToFind, const FTreeMapNodeDataRef& OriginalNode, const FTreeMapNodeDataRef& CopiedNode ) const
|
|
{
|
|
if( NodeToFind == OriginalNode )
|
|
{
|
|
return CopiedNode;
|
|
}
|
|
|
|
for( int32 ChildNodeIndex = 0; ChildNodeIndex < OriginalNode->Children.Num(); ++ChildNodeIndex )
|
|
{
|
|
const auto& OriginalChildNode = OriginalNode->Children[ ChildNodeIndex ];
|
|
const auto& CopiedChildNode = CopiedNode->Children[ ChildNodeIndex ];
|
|
|
|
const auto& FoundMatchingCopy = FindNodeInCopiedTree( NodeToFind, OriginalChildNode.ToSharedRef(), CopiedChildNode.ToSharedRef() );
|
|
if( FoundMatchingCopy.IsValid() )
|
|
{
|
|
return FoundMatchingCopy;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void STreeMap::Undo()
|
|
{
|
|
if( UndoStates.Num() > 0 )
|
|
{
|
|
// Restore from undo state
|
|
int32 UndoStateIndex;
|
|
if( CurrentUndoLevel == INDEX_NONE )
|
|
{
|
|
// We haven't undone anything since the last time a new undo state was added
|
|
UndoStateIndex = UndoStates.Num() - 1;
|
|
|
|
// Store an undo state for the current state (before undo was pressed)
|
|
TakeUndoSnapshot();
|
|
}
|
|
else
|
|
{
|
|
// Move down to the next undo level
|
|
UndoStateIndex = CurrentUndoLevel - 1;
|
|
}
|
|
|
|
// Is there anything else to undo?
|
|
if( UndoStateIndex >= 0 )
|
|
{
|
|
// Undo from history
|
|
{
|
|
const FTreeMapNodeDataRef NewTreeMapNodeData = UndoStates[ UndoStateIndex ]->CopyNodeRecursively();
|
|
const FTreeMapNodeDataRef NewActiveRoot = FindNodeInCopiedTree( ActiveRootTreeMapNode.ToSharedRef(), TreeMapNodeData.ToSharedRef(), NewTreeMapNodeData ).ToSharedRef();
|
|
TreeMapNodeData = NewTreeMapNodeData;
|
|
|
|
const bool bShouldPlayTransition = true; // @todo treemap: A bit worried about volatility of LastTreeMap node refs here
|
|
SetTreeRoot( NewActiveRoot, bShouldPlayTransition );
|
|
}
|
|
|
|
CurrentUndoLevel = UndoStateIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void STreeMap::Redo()
|
|
{
|
|
// Is there anything to redo? If we've haven't tried to undo since the last time new
|
|
// undo state was added, then CurrentUndoLevel will be INDEX_NONE
|
|
if( CurrentUndoLevel != INDEX_NONE )
|
|
{
|
|
const int32 NextUndoLevel = CurrentUndoLevel + 1;
|
|
if( UndoStates.Num() > NextUndoLevel )
|
|
{
|
|
// Restore from undo state
|
|
{
|
|
const FTreeMapNodeDataRef NewTreeMapNodeData = UndoStates[ NextUndoLevel ]->CopyNodeRecursively();
|
|
const FTreeMapNodeDataRef NewActiveRoot = FindNodeInCopiedTree( ActiveRootTreeMapNode.ToSharedRef(), TreeMapNodeData.ToSharedRef(), NewTreeMapNodeData ).ToSharedRef();
|
|
TreeMapNodeData = NewTreeMapNodeData;
|
|
|
|
const bool bShouldPlayTransition = true; // @todo treemap: A bit worried about volatility of LastTreeMap node refs here
|
|
SetTreeRoot( NewActiveRoot, bShouldPlayTransition );
|
|
}
|
|
|
|
CurrentUndoLevel = NextUndoLevel;
|
|
|
|
if( UndoStates.Num() <= CurrentUndoLevel + 1 )
|
|
{
|
|
// We've redone all available undo states
|
|
CurrentUndoLevel = INDEX_NONE;
|
|
|
|
// Pop last undo state that we created on initial undo
|
|
UndoStates.RemoveAt( UndoStates.Num() - 1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void STreeMap::ReparentNode( const FTreeMapNodeDataRef DroppedNode, const FTreeMapNodeDataRef NewParentNode )
|
|
{
|
|
// Can't reparent the tree root node
|
|
if( DroppedNode != TreeMapNodeData )
|
|
{
|
|
struct Local
|
|
{
|
|
/** Checks to see if the specified Node is a child of 'PossibleParent' at any level */
|
|
static bool IsMyParent( const FTreeMapNodeDataRef& Node, const FTreeMapNodeDataRef& PossibleParent )
|
|
{
|
|
if( Node == PossibleParent )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( PossibleParent->Parent != NULL )
|
|
{
|
|
return IsMyParent( Node, PossibleParent->Parent->AsShared() );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Can't parent owning nodes to their children
|
|
if( !Local::IsMyParent( DroppedNode, NewParentNode ) )
|
|
{
|
|
// Can't reparent to self
|
|
if( NewParentNode != DroppedNode )
|
|
{
|
|
// Only if parent has changed
|
|
if( &NewParentNode.Get() != DroppedNode->Parent )
|
|
{
|
|
// Take an Undo snapshot before we change anything
|
|
TakeUndoSnapshot();
|
|
|
|
if( DroppedNode->Parent != NULL )
|
|
{
|
|
DroppedNode->Parent->Children.RemoveSingle( DroppedNode );
|
|
}
|
|
NewParentNode->Children.Add( DroppedNode );
|
|
DroppedNode->Parent = &NewParentNode.Get();
|
|
|
|
// If we container node became a leaf node, we need to make sure it has a valid size set
|
|
// @todo treemap: This seems a bit... weird. It won't reverse either, if you put the node back (save with overridden size!) Maybe we need a bool for "auto size"=true. OR, have a separate size for Node vs. Leaf?
|
|
if( DroppedNode->Size == 0.0f && DroppedNode->IsLeafNode() )
|
|
{
|
|
DroppedNode->Size = 1.0f;
|
|
}
|
|
|
|
// Invalidate the tree
|
|
const bool bShouldPlayTransition = true;
|
|
RebuildTreeMap( bShouldPlayTransition );
|
|
|
|
// After we have a new tree, play a highlight effect on the reparented node so the user can see where it ended
|
|
// up in the tree. Tree map layout can be fairly volatile after a parenting change.
|
|
HighlightPulseNode = DroppedNode;
|
|
HighlightPulseStartTime = FSlateApplication::Get().GetCurrentTime();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
FReply STreeMap::DeleteHoveredNode()
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
|
|
// Wait until we've finished animating a transition
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
if( MouseOverVisual != NULL )
|
|
{
|
|
// Only non-root nodes can be deleted
|
|
auto NodeToDelete = MouseOverVisual->NodeData->AsShared();
|
|
if( NodeToDelete->Parent != NULL )
|
|
{
|
|
// Don't allow the current active tree root to be deleted. The user should zoom out first!
|
|
if( NodeToDelete != ActiveRootTreeMapNode )
|
|
{
|
|
TakeUndoSnapshot();
|
|
|
|
// Delete the node
|
|
{
|
|
NodeToDelete->Parent->Children.RemoveSingle( NodeToDelete );
|
|
NodeToDelete->Parent = NULL;
|
|
|
|
// Note: NodeToDelete will actually be deleted when it goes out of scope later in this function (shared pointer), but it is already removed from our tree
|
|
}
|
|
|
|
const bool bShouldPlayTransition = true;
|
|
RebuildTreeMap( bShouldPlayTransition );
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
|
|
FReply STreeMap::InsertNewNodeAsChildOfHoveredNode( const FGeometry& MyGeometry )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
|
|
// Wait until we've finished animating a transition
|
|
if( !IsNavigationTransitionActive() )
|
|
{
|
|
if( MouseOverVisual != NULL )
|
|
{
|
|
auto ParentNode = MouseOverVisual->NodeData->AsShared();
|
|
|
|
// Allocate the new node but don't add it to the tree yet. We want the user to give the node a name first!
|
|
|
|
// NOTE: Ownership of this node will be transferred to StartRenamingNode, where it will be referenced
|
|
// in the delegate callback for the editable text change commit handler. If the user opts to not type anything, the
|
|
// node will be deleted instead of being added to the tree.
|
|
FTreeMapNodeDataRef NewNode( new FTreeMapNodeData() );
|
|
NewNode->Name = TEXT( "" );
|
|
NewNode->Size = 1.0f; // Leaf nodes always get a size of 1.0 for now
|
|
NewNode->Parent = MouseOverVisual->NodeData;
|
|
|
|
// Start editing the new node before we insert it
|
|
const bool bIsNewNode = true;
|
|
StartRenamingNode( MyGeometry, NewNode, MouseOverVisual->Position, bIsNewNode );
|
|
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
|
|
bool STreeMap::SupportsKeyboardFocus() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
FReply STreeMap::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyboardEvent )
|
|
{
|
|
FReply Reply = FReply::Unhandled();
|
|
|
|
const FKey Key = InKeyboardEvent.GetKey();
|
|
|
|
if( AllowEditing.Get() )
|
|
{
|
|
if( Key == EKeys::Delete )
|
|
{
|
|
// If the cursor is over a node, delete it!
|
|
DeleteHoveredNode();
|
|
Reply = FReply::Handled();
|
|
}
|
|
else if( Key == EKeys::Insert )
|
|
{
|
|
// If the cursor is over a node, insert a new node as a child!
|
|
InsertNewNodeAsChildOfHoveredNode( MyGeometry );
|
|
Reply = FReply::Handled();
|
|
}
|
|
else if( Key == EKeys::Z && InKeyboardEvent.IsControlDown() )
|
|
{
|
|
// Undo
|
|
Undo();
|
|
Reply = FReply::Handled();
|
|
}
|
|
else if( Key == EKeys::Y && InKeyboardEvent.IsControlDown() )
|
|
{
|
|
// Redo
|
|
Redo();
|
|
Reply = FReply::Handled();
|
|
}
|
|
}
|
|
|
|
return Reply;
|
|
}
|
|
|
|
|
|
void STreeMap::RenamingNode_OnTextCommitted( const FText& NewText, ETextCommit::Type, TSharedRef<FTreeMapNodeData> NodeToRename )
|
|
{
|
|
TSharedPtr<SWidget> RenamingNodeWidgetPinned( RenamingNodeWidget.Pin() );
|
|
if( RenamingNodeWidgetPinned.IsValid() )
|
|
{
|
|
// Kill the window after the text has been committed
|
|
TSharedRef<SWindow> ParentWindow = FSlateApplication::Get().FindWidgetWindow( RenamingNodeWidgetPinned.ToSharedRef() ).ToSharedRef();
|
|
|
|
RenamingNodeWidget.Reset(); // Avoid reentrancy
|
|
RenamingNodeData.Reset();
|
|
FSlateApplication::Get().RequestDestroyWindow( ParentWindow );
|
|
|
|
|
|
// Make sure new name is OK
|
|
if( NewText.ToString() != NodeToRename->Name &&
|
|
!NewText.IsEmpty() )
|
|
{
|
|
// Store undo state
|
|
TakeUndoSnapshot();
|
|
|
|
// Rename it!
|
|
NodeToRename->Name = NewText.ToString();
|
|
|
|
if( bIsNamingNewNode )
|
|
{
|
|
// Add the new node to the tree
|
|
TakeUndoSnapshot();
|
|
|
|
// Insert the new node
|
|
{
|
|
auto ParentNode = NodeToRename->Parent->AsShared();
|
|
ParentNode->Children.Add( NodeToRename );
|
|
}
|
|
|
|
const bool bShouldPlayTransition = true;
|
|
RebuildTreeMap( bShouldPlayTransition );
|
|
}
|
|
else
|
|
{
|
|
// NOTE: No refresh needed, as node labels are pulled directly from nodes
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void STreeMap::StartRenamingNode( const FGeometry& MyGeometry, const FTreeMapNodeDataRef& NodeData, const FVector2D& RelativePosition, const bool bIsNewNode )
|
|
{
|
|
TSharedRef<SBorder> RenamerWidget = SNew( SBorder );
|
|
|
|
TSharedRef<SEditableTextBox> EditableText =
|
|
SNew( SEditableTextBox )
|
|
.Text( FText::FromString( NodeData->Name ) )
|
|
.SelectAllTextWhenFocused( true )
|
|
.RevertTextOnEscape( true )
|
|
.MinDesiredWidth( 100 )
|
|
.OnTextCommitted( this, &STreeMap::RenamingNode_OnTextCommitted, NodeData->AsShared() )
|
|
;
|
|
RenamerWidget->SetContent( EditableText );
|
|
|
|
RenamingNodeWidget = RenamerWidget;
|
|
RenamingNodeData = NodeData;
|
|
bIsNamingNewNode = bIsNewNode;
|
|
|
|
const bool bFocusImmediately = true;
|
|
FSlateApplication::Get().PushMenu( AsShared(), FWidgetPath(), RenamerWidget, MyGeometry.LocalToAbsolute( RelativePosition ), FPopupTransitionEffect::None, bFocusImmediately );
|
|
|
|
// Focus the text box right after we spawn it so that the user can start typing
|
|
FSlateApplication::Get().SetKeyboardFocus( EditableText, EKeyboardFocusCause::SetDirectly );
|
|
}
|
|
|
|
|
|
|
|
void STreeMap::StopRenamingNode()
|
|
{
|
|
TSharedPtr<SWidget> RenamingNodeWidgetPinned( RenamingNodeWidget.Pin() );
|
|
if( RenamingNodeWidgetPinned.IsValid() )
|
|
{
|
|
// Kill the window after the text has been committed
|
|
TSharedRef<SWindow> ParentWindow = FSlateApplication::Get().FindWidgetWindow( RenamingNodeWidgetPinned.ToSharedRef() ).ToSharedRef();
|
|
FSlateApplication::Get().RequestDestroyWindow( ParentWindow );
|
|
}
|
|
}
|
|
|
|
|
|
void STreeMap::ApplyVisualizationToNodes( const FTreeMapNodeDataRef& Node )
|
|
{
|
|
const FLinearColor RootDefaultColor = FLinearColor( 0.125f, 0.125f, 0.125f ); // @todo treemap custom: Make configurable in the STreeMap settings?
|
|
int32 TreeDepth = 0;
|
|
ApplyVisualizationToNodesRecursively( Node, RootDefaultColor, TreeDepth );
|
|
}
|
|
|
|
|
|
void STreeMap::ApplyVisualizationToNodesRecursively( const FTreeMapNodeDataRef& Node, const FLinearColor& DefaultColor, const int32 TreeDepth )
|
|
{
|
|
// Select default color saturation based on tree depth
|
|
FLinearColor MyDefaultColor = DefaultColor;
|
|
auto HSV = MyDefaultColor.LinearRGBToHSV();
|
|
const float SaturationReductionPerDepthLevel = 0.05f; // @todo treemap custom: Make configurable in the tree map settings? Calculate tree depth?
|
|
const float MinAllowedSaturation = 0.1f;
|
|
HSV.G = FMath::Max( MinAllowedSaturation, HSV.G - TreeDepth * SaturationReductionPerDepthLevel );
|
|
MyDefaultColor = HSV.HSVToLinearRGB();
|
|
|
|
|
|
// Size
|
|
{
|
|
if( SizeNodesByAttribute.IsValid() )
|
|
{
|
|
FTreeMapAttributeDataPtr* AttributeDataPtr = Node->Attributes.Find( SizeNodesByAttribute->Name );
|
|
if( AttributeDataPtr != NULL )
|
|
{
|
|
const FTreeMapAttributeData& AttributeData = *AttributeDataPtr->Get();
|
|
|
|
// Make sure the data value is in range
|
|
if( ensure( SizeNodesByAttribute->Values.Contains( AttributeData.Value ) ) )
|
|
{
|
|
// @todo treemap: This doesn't work well with container nodes. Our range of sizes doesn't go big enough to compete with cumulative sizes!
|
|
Node->Size = SizeNodesByAttribute->Values[ AttributeData.Value ]->NodeSize;
|
|
}
|
|
else
|
|
{
|
|
// Invalid attribute data!
|
|
Node->Size = Node->IsLeafNode() ? SizeNodesByAttribute->DefaultValue->NodeSize : 0.0f; // Leaf nodes get a default size, but container nodes use auto-sizing
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The node doesn't have this attribute on it. Use default.
|
|
Node->Size = Node->IsLeafNode() ? SizeNodesByAttribute->DefaultValue->NodeSize : 0.0f; // Leaf nodes get a default size, but container nodes use auto-sizing
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
// @todo treemap: Really we want this to "restore the original size set by the user", not make up new defaults. Disabled for now.
|
|
|
|
// Default size
|
|
// Node->Size = Node->IsLeafNode() ? 1.0f : 0.0f; // Leaf nodes get a default size, but container nodes use auto-sizing
|
|
}
|
|
}
|
|
|
|
|
|
// Color
|
|
{
|
|
if( ColorNodesByAttribute.IsValid() )
|
|
{
|
|
FTreeMapAttributeDataPtr* AttributeDataPtr = Node->Attributes.Find( ColorNodesByAttribute->Name );
|
|
if( AttributeDataPtr != NULL )
|
|
{
|
|
const FTreeMapAttributeData& AttributeData = *AttributeDataPtr->Get();
|
|
|
|
// Make sure the data value is in range
|
|
if( ensure( ColorNodesByAttribute->Values.Contains( AttributeData.Value ) ) )
|
|
{
|
|
Node->Color = ColorNodesByAttribute->Values[ AttributeData.Value ]->NodeColor;
|
|
}
|
|
else
|
|
{
|
|
// Invalid attribute data!
|
|
Node->Color = ColorNodesByAttribute->DefaultValue->NodeColor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The node doesn't have this attribute on it. Use default.
|
|
Node->Color = ColorNodesByAttribute->DefaultValue->NodeColor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Default color
|
|
Node->Color = MyDefaultColor;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for( int32 ChildIndex = 0; ChildIndex < Node->Children.Num(); ++ChildIndex )
|
|
{
|
|
const auto& ChildNode = Node->Children[ ChildIndex ];
|
|
|
|
// Make up a distinct color for all of the root's top level nodes
|
|
FLinearColor ChildColor = MyDefaultColor;
|
|
if( TreeDepth == 0 )
|
|
{
|
|
// Choose a hue evenly spaced across the spectrum
|
|
float ColorHue = 360.0f * (float)( ChildIndex + 1 ) / (float)Node->Children.Num();
|
|
auto ChildColorHSV = FLinearColor::White;
|
|
ChildColorHSV.R = ColorHue;
|
|
ChildColorHSV.G = 1.0f; // Full saturation!
|
|
ChildColor = ChildColorHSV.HSVToLinearRGB();
|
|
}
|
|
|
|
ApplyVisualizationToNodesRecursively( ChildNode.ToSharedRef(), ChildColor, TreeDepth + 1 );
|
|
}
|
|
|
|
}
|
|
|
|
void STreeMap::ShowOptionsMenuAt(const FVector2D& ScreenSpacePosition)
|
|
{
|
|
// deprecated
|
|
ShowOptionsMenuAtInternal(ScreenSpacePosition, FWidgetPath());
|
|
}
|
|
|
|
void STreeMap::ShowOptionsMenuAt(const FPointerEvent& InMouseEvent)
|
|
{
|
|
FWidgetPath WidgetPath = InMouseEvent.GetEventPath() != nullptr ? *InMouseEvent.GetEventPath() : FWidgetPath();
|
|
const FVector2D& ScreenSpacePosition = InMouseEvent.GetScreenSpacePosition();
|
|
|
|
ShowOptionsMenuAtInternal(ScreenSpacePosition, WidgetPath);
|
|
}
|
|
|
|
void STreeMap::ShowOptionsMenuAtInternal(const FVector2D& ScreenSpacePosition, const FWidgetPath& WidgetPath)
|
|
{
|
|
struct Local
|
|
{
|
|
static void MakeEditNodeAttributeMenu( FMenuBuilder& MenuBuilder, FTreeMapNodeDataPtr EditingNode, TSharedPtr< FTreeMapAttribute > Attribute, STreeMap* Self )
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT( "RemoveAttribute", "Not Set" ),
|
|
FText(), // No tooltip (intentional)
|
|
FSlateIcon(), // Icon
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic( &Local::EditNodeAttribute_Execute, EditingNode, Attribute, TSharedPtr< FTreeMapAttributeValue >(), Self ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &Local::EditNodeAttribute_IsChecked, EditingNode, Attribute, TSharedPtr< FTreeMapAttributeValue >(), Self ) ),
|
|
NAME_None, // Extension point
|
|
EUserInterfaceActionType::ToggleButton );
|
|
|
|
MenuBuilder.AddMenuSeparator();
|
|
|
|
// @todo treemap: These probably should be sorted rather than hash order
|
|
for( auto HashIter( Attribute->Values.CreateConstIterator() ); HashIter; ++HashIter )
|
|
{
|
|
FTreeMapAttributeValuePtr Value = HashIter.Value();
|
|
|
|
MenuBuilder.AddMenuEntry(
|
|
FText::FromName( Value->Name ),
|
|
FText(), // No tooltip (intentional)
|
|
FSlateIcon(), // Icon
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic( &Local::EditNodeAttribute_Execute, EditingNode, Attribute, Value, Self ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &Local::EditNodeAttribute_IsChecked, EditingNode, Attribute, Value, Self ) ),
|
|
NAME_None, // Extension point
|
|
EUserInterfaceActionType::ToggleButton );
|
|
}
|
|
}
|
|
|
|
|
|
static void EditNodeAttribute_Execute( FTreeMapNodeDataPtr EditingNode, TSharedPtr< FTreeMapAttribute > Attribute, TSharedPtr< FTreeMapAttributeValue > Value, STreeMap* Self )
|
|
{
|
|
bool bAnyChanges = false;
|
|
if( Value.IsValid() )
|
|
{
|
|
if( !EditingNode->Attributes.Contains( Attribute->Name ) )
|
|
{
|
|
EditingNode->Attributes.Add( Attribute->Name, MakeShareable( new FTreeMapAttributeData( Value->Name ) ) );
|
|
bAnyChanges = true;
|
|
}
|
|
else if( EditingNode->Attributes[ Attribute->Name ]->Value != Value->Name )
|
|
{
|
|
EditingNode->Attributes[ Attribute->Name ]->Value = Value->Name;
|
|
bAnyChanges = true;
|
|
}
|
|
else
|
|
{
|
|
// Nothing changed
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Clearing attribute
|
|
if( EditingNode->Attributes.Contains( Attribute->Name ) )
|
|
{
|
|
EditingNode->Attributes.Remove( Attribute->Name );
|
|
bAnyChanges = true;
|
|
}
|
|
else
|
|
{
|
|
// Nothing changed
|
|
}
|
|
}
|
|
|
|
// Has anything changed?
|
|
if( bAnyChanges )
|
|
{
|
|
const bool bShouldPlayTransition = true;
|
|
Self->RebuildTreeMap( bShouldPlayTransition );
|
|
}
|
|
}
|
|
|
|
|
|
static bool EditNodeAttribute_IsChecked( FTreeMapNodeDataPtr EditingNode, TSharedPtr< FTreeMapAttribute > Attribute, TSharedPtr< FTreeMapAttributeValue > Value, STreeMap* Self )
|
|
{
|
|
if( Value.IsValid() )
|
|
{
|
|
return EditingNode->Attributes.Contains( Attribute->Name ) && EditingNode->Attributes[ Attribute->Name ]->Value == Value->Name;
|
|
}
|
|
else
|
|
{
|
|
return !EditingNode->Attributes.Contains( Attribute->Name );
|
|
}
|
|
}
|
|
|
|
|
|
static void SizeByAttribute_Execute( TSharedPtr< FTreeMapAttribute > Attribute, STreeMap* Self )
|
|
{
|
|
// Has anything changed?
|
|
if( Self->SizeNodesByAttribute != Attribute )
|
|
{
|
|
// Apply the new visualization!
|
|
Self->SizeNodesByAttribute = Attribute;
|
|
|
|
const bool bShouldPlayTransition = true;
|
|
Self->RebuildTreeMap( bShouldPlayTransition );
|
|
}
|
|
}
|
|
|
|
|
|
static bool SizeByAttribute_IsChecked( TSharedPtr< FTreeMapAttribute > Attribute, STreeMap* Self )
|
|
{
|
|
return Self->SizeNodesByAttribute == Attribute;
|
|
}
|
|
|
|
|
|
static void MakeSizeByAttributeMenu( FMenuBuilder& MenuBuilder, STreeMap* Self )
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT( "NoSizeByAttribute", "Off" ),
|
|
FText(), // No tooltip (intentional)
|
|
FSlateIcon(), // Icon
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic( &Local::SizeByAttribute_Execute, TSharedPtr< FTreeMapAttribute >(), Self ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &Local::SizeByAttribute_IsChecked, TSharedPtr< FTreeMapAttribute >(), Self ) ),
|
|
NAME_None, // Extension point
|
|
EUserInterfaceActionType::ToggleButton );
|
|
|
|
if( Self->Customization.IsValid() )
|
|
{
|
|
MenuBuilder.AddMenuSeparator();
|
|
|
|
// @todo treemap: These probably should be sorted rather than hash order
|
|
for( auto HashIter( Self->Customization->GetAttributes().CreateConstIterator() ); HashIter; ++HashIter )
|
|
{
|
|
FTreeMapAttributePtr Attribute = HashIter.Value();
|
|
|
|
MenuBuilder.AddMenuEntry(
|
|
FText::FromName( Attribute->Name ),
|
|
FText(), // No tooltip (intentional)
|
|
FSlateIcon(), // Icon
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic( &Local::SizeByAttribute_Execute, Attribute, Self ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &Local::SizeByAttribute_IsChecked, Attribute, Self ) ),
|
|
NAME_None, // Extension point
|
|
EUserInterfaceActionType::ToggleButton );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void ColorByAttribute_Execute( TSharedPtr< FTreeMapAttribute > Attribute, STreeMap* Self )
|
|
{
|
|
// Has anything changed?
|
|
if( Self->ColorNodesByAttribute != Attribute )
|
|
{
|
|
// Apply the new visualization!
|
|
Self->ColorNodesByAttribute = Attribute;
|
|
|
|
const bool bShouldPlayTransition = true;
|
|
Self->RebuildTreeMap( bShouldPlayTransition );
|
|
}
|
|
}
|
|
|
|
|
|
static bool ColorByAttribute_IsChecked( TSharedPtr< FTreeMapAttribute > Attribute, STreeMap* Self )
|
|
{
|
|
return Self->ColorNodesByAttribute == Attribute;
|
|
}
|
|
|
|
|
|
static void MakeColorByAttributeMenu( FMenuBuilder& MenuBuilder, STreeMap* Self )
|
|
{
|
|
MenuBuilder.AddMenuEntry(
|
|
LOCTEXT( "NoColorByAttribute", "Off" ),
|
|
FText(), // No tooltip (intentional)
|
|
FSlateIcon(), // Icon
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic( &Local::ColorByAttribute_Execute, TSharedPtr< FTreeMapAttribute >(), Self ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &Local::ColorByAttribute_IsChecked, TSharedPtr< FTreeMapAttribute >(), Self ) ),
|
|
NAME_None, // Extension point
|
|
EUserInterfaceActionType::ToggleButton );
|
|
|
|
if( Self->Customization.IsValid() )
|
|
{
|
|
MenuBuilder.AddMenuSeparator();
|
|
|
|
// @todo treemap: These probably should be sorted rather than hash order
|
|
for( auto HashIter( Self->Customization->GetAttributes().CreateConstIterator() ); HashIter; ++HashIter )
|
|
{
|
|
FTreeMapAttributePtr Attribute = HashIter.Value();
|
|
|
|
MenuBuilder.AddMenuEntry(
|
|
FText::FromName( Attribute->Name ),
|
|
FText(), // No tooltip (intentional)
|
|
FSlateIcon(), // Icon
|
|
FUIAction(
|
|
FExecuteAction::CreateStatic( &Local::ColorByAttribute_Execute, Attribute, Self ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateStatic( &Local::ColorByAttribute_IsChecked, Attribute, Self ) ),
|
|
NAME_None, // Extension point
|
|
EUserInterfaceActionType::ToggleButton );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Only present a context menu if the tree has been customized
|
|
if( Customization.IsValid() )
|
|
{
|
|
const bool bShouldCloseMenuAfterSelection = true;
|
|
FMenuBuilder OptionsMenuBuilder( bShouldCloseMenuAfterSelection, NULL );
|
|
|
|
if( AllowEditing.Get() && Customization.IsValid() && MouseOverVisual != NULL )
|
|
{
|
|
// Node editing options
|
|
OptionsMenuBuilder.BeginSection( NAME_None, LOCTEXT( "EditNodeAttributesSection", "Edit Node" ) );
|
|
{
|
|
const auto& EditingNode = MouseOverVisual->NodeData->AsShared();
|
|
|
|
for( auto HashIter( Customization->GetAttributes().CreateConstIterator() ); HashIter; ++HashIter )
|
|
{
|
|
FTreeMapAttributePtr Attribute = HashIter.Value();
|
|
|
|
OptionsMenuBuilder.AddSubMenu(
|
|
FText::FromName( Attribute->Name ),
|
|
LOCTEXT( "EditAttribute_ToolTip", "Edits this attribute on the node under the curosr." ),
|
|
FNewMenuDelegate::CreateStatic( &Local::MakeEditNodeAttributeMenu, FTreeMapNodeDataPtr( EditingNode ), Attribute, this ) );
|
|
}
|
|
}
|
|
OptionsMenuBuilder.EndSection();
|
|
}
|
|
|
|
OptionsMenuBuilder.BeginSection( NAME_None, LOCTEXT( "ChangeLayoutSection", "Layout" ) );
|
|
{
|
|
OptionsMenuBuilder.AddSubMenu( LOCTEXT( "SizeBy", "Size by" ), LOCTEXT( "SizeBy_ToolTip", "Sets which criteria to base the size of the nodes in the tree map on." ), FNewMenuDelegate::CreateStatic( &Local::MakeSizeByAttributeMenu, this ) );
|
|
OptionsMenuBuilder.AddSubMenu( LOCTEXT( "ColorBy", "Color by" ), LOCTEXT( "ColorBy_ToolTip", "Sets which criteria to base the color of the nodes in the tree map on." ), FNewMenuDelegate::CreateStatic( &Local::MakeColorByAttributeMenu, this ) );
|
|
}
|
|
OptionsMenuBuilder.EndSection();
|
|
|
|
TSharedRef< SWidget > WindowContent =
|
|
SNew( SBorder )
|
|
[
|
|
OptionsMenuBuilder.MakeWidget()
|
|
];
|
|
|
|
const bool bFocusImmediately = false;
|
|
FSlateApplication::Get().PushMenu(AsShared(), WidgetPath, WindowContent, ScreenSpacePosition, FPopupTransitionEffect::ContextMenu, bFocusImmediately);
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|