A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
SFindInDialogues.cpp
Go to the documentation of this file.
1// Copyright Csaba Molnar, Daniel Butum. All Rights Reserved.
2#include "SFindInDialogues.h"
3
4#include "Editor.h"
5#include "Widgets/Views/STableRow.h"
6#include "Widgets/Input/SSearchBox.h"
7#include "Widgets/Input/SComboButton.h"
8#include "Widgets/Input/SButton.h"
9#include "Widgets/Input/SCheckBox.h"
10#include "Framework/MultiBox/MultiBoxBuilder.h"
11#include "Framework/Application/SlateApplication.h"
12#include "Framework/Commands/GenericCommands.h"
13
17
18
19#define LOCTEXT_NAMESPACE "SFindInDialogues"
20
22// SFindInDialogues
23void SFindInDialogues::Construct(const FArguments& InArgs, const TSharedPtr<FDialogueEditor>& InDialogueEditor)
24{
25 DialogueEditorPtr = InDialogueEditor;
26 HostTab = InArgs._ContainingTab;
27
28 if (HostTab.IsValid())
29 {
30 HostTab.Pin()->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateSP(this, &Self::HandleHostTabClosed));
31 }
32
33 if (InArgs._bIsSearchWindow)
34 {
35 //RegisterCommands();
36 }
37
38 // Only search in the current Dialogue
40
41 constexpr bool bHostFindInDialoguesInGlobalTab = true;
42 ChildSlot
43 [
44 SAssignNew(MainVerticalBoxWidget, SVerticalBox)
45
46 // Top bar, search
47 +SVerticalBox::Slot()
48 .AutoHeight()
49 [
50 SNew(SHorizontalBox)
51
52 // Search field
53 +SHorizontalBox::Slot()
54 .FillWidth(1)
55 [
56 SAssignNew(SearchTextBoxWidget, SSearchBox)
57 .HintText(LOCTEXT("DialogueSearchHint", "Enter function or event name to find references..."))
58 .OnTextChanged(this, &Self::HandleSearchTextChanged)
59 .OnTextCommitted(this, &Self::HandleSearchTextCommitted)
60 .Visibility(InArgs._bHideSearchBar? EVisibility::Collapsed : EVisibility::Visible)
61 ]
62
63 // Filter Options
64 +SHorizontalBox::Slot()
65 .AutoWidth()
66 .Padding(2.0f, 2.0f)
67 [
68 SNew(SComboButton)
69 .ComboButtonStyle(FEditorStyle::Get(), "GenericFilters.ComboButtonStyle")
70 .ForegroundColor(FLinearColor::White)
71 .ContentPadding(0)
72 .ToolTipText(LOCTEXT("Filters_Tooltip", "Filter options for the Dialogue Search."))
73 .OnGetMenuContent(this, &Self::FillFilterEntries)
74 .HasDownArrow(true)
75 .ContentPadding(FMargin(1, 0))
76 .ButtonContent()
77 [
78 SNew(SHorizontalBox)
79 + SHorizontalBox::Slot()
80 .AutoWidth()
81 [
82 SNew(STextBlock)
83 .TextStyle(FEditorStyle::Get(), "GenericFilters.TextStyle")
84 .Font(FEditorStyle::Get().GetFontStyle("FontAwesome.9"))
85 .Text(FText::FromString(FString(TEXT("\xf0b0"))) /*fa-filter*/)
86 ]
87 +SHorizontalBox::Slot()
88 .AutoWidth()
89 .Padding(2, 0, 0, 0)
90 [
91 SNew(STextBlock)
92 .TextStyle(FEditorStyle::Get(), "GenericFilters.TextStyle")
93 .Text(LOCTEXT("Filters", "Filters"))
94 ]
95 ]
96 ]
97
98 // Change find mode to global
99 +SHorizontalBox::Slot()
100 .Padding(4.f, 0.f, 2.f, 0.f)
101 .AutoWidth()
102 [
103 SNew(SButton)
104 .OnClicked(this, &Self::HandleOpenGlobalFindResults)
105 // Show button if we are in a DialogueEditor
106 .Visibility(DialogueEditorPtr.IsValid() && bHostFindInDialoguesInGlobalTab ? EVisibility::Visible : EVisibility::Collapsed)
107 .ToolTipText(LOCTEXT("OpenInGlobalFindResultsButtonTooltip", "Find in all Dialogues"))
108 [
109 SNew(STextBlock)
110 .TextStyle(FEditorStyle::Get(), "FindResults.FindInBlueprints")
111 .Text(FText::FromString(FString(TEXT("\xf1e5"))) /*fa-binoculars*/)
112 ]
113 ]
114
115 // Change find mode to local
116 +SHorizontalBox::Slot()
117 .Padding(2.f, 0.f)
118 .AutoWidth()
119 [
120 SNew(SCheckBox)
121 .OnCheckStateChanged(this, &Self::HandleFindModeChanged)
122 .IsChecked(this, &Self::HandleGetFindModeChecked)
123 .Visibility(InArgs._bHideSearchBar || bHostFindInDialoguesInGlobalTab ? EVisibility::Collapsed : EVisibility::Visible)
124 [
125 SNew(STextBlock)
126 .Text(LOCTEXT("BlueprintSearchModeChange", "Find In Current Blueprint Only"))
127 ]
128 ]
129 ]
130
131 // Results tree
132 +SVerticalBox::Slot()
133 .FillHeight(1.0f)
134 .Padding(0.f, 4.f, 0.f, 0.f)
135 [
136 SNew(SBorder)
137 .BorderImage(FEditorStyle::GetBrush("Menu.Background"))
138 [
139 SAssignNew(TreeView, STreeView<TSharedPtr<FDialogueSearchResult>>)
140 .ItemHeight(24)
141 .TreeItemsSource(&ItemsFound)
142 .OnGenerateRow(this, &Self::HandleGenerateRow)
143 .OnGetChildren(this, &Self::HandleGetChildren)
144 .OnMouseButtonDoubleClick(this, &Self::HandleTreeSelectionDoubleClicked)
145 .SelectionMode(ESelectionMode::Multi)
146 .OnContextMenuOpening(this, &Self::HandleContextMenuOpening)
147 ]
148 ]
149 ];
150}
151
153{
154 // TODO clear all cache for this
155}
156
157void SFindInDialogues::FocusForUse(bool bSetFindWithinDialogue, const FDialogueSearchFilter& SearchFilter, bool bSelectFirstResult)
158{
159 // NOTE: Careful, GeneratePathToWidget can be reentrant in that it can call visibility delegates and such
160 FWidgetPath FilterTextBoxWidgetPath;
161 FSlateApplication::Get().GeneratePathToWidgetUnchecked(SearchTextBoxWidget.ToSharedRef(), FilterTextBoxWidgetPath);
162
163 // Set keyboard focus directly
164 FSlateApplication::Get().SetKeyboardFocus(FilterTextBoxWidgetPath, EFocusCause::SetDirectly);
165
166 // Set the filter mode
167 bIsInFindWithinDialogueMode = bSetFindWithinDialogue;
168
169 // Set the new search terms
170 if (!SearchFilter.SearchString.IsEmpty())
171 {
172 SearchTextBoxWidget->SetText(FText::FromString(SearchFilter.SearchString));
174
175 // Select the first result
176 if (bSelectFirstResult && ItemsFound.Num())
177 {
178 auto ItemToFocusOn = ItemsFound[0];
179
180 // Focus the deepest child
181 while (ItemToFocusOn->HasChildren())
182 {
183 ItemToFocusOn = ItemToFocusOn->GetChildren()[0];
184 }
185 TreeView->SetSelection(ItemToFocusOn);
186 ItemToFocusOn->OnClick();
187 }
188 }
189}
190
191void SFindInDialogues::MakeSearchQuery(const FDialogueSearchFilter& SearchFilter, bool bInIsFindWithinDialogue)
192{
193 SearchTextBoxWidget->SetText(FText::FromString(SearchFilter.SearchString));
194
195 // Reset the scroll to the top
196 if (ItemsFound.Num())
197 {
198 TreeView->RequestScrollIntoView(ItemsFound[0]);
199 }
200 ItemsFound.Empty();
201
202 // Nothing to search for :(
203 if (SearchFilter.SearchString.IsEmpty())
204 {
205 return;
206 }
207
208 HighlightText = FText::FromString(SearchFilter.SearchString);
209 RootSearchResult = MakeShared<FDialogueSearchResult_RootNode>();
210
211 // TODO use threads extend FRunnable
212 if (bInIsFindWithinDialogue)
213 {
214 // Local
215 if (DialogueEditorPtr.IsValid())
216 {
218 ->QuerySingleDialogue(SearchFilter, DialogueEditorPtr.Pin()->GetDialogueBeingEdited(), RootSearchResult);
219
220 // Do now show the Dialogue in the search results.
221 const TArray<TSharedPtr<FDialogueSearchResult>>& Children = RootSearchResult->GetChildren();
222 if (Children.Num() == 1 && Children[0].IsValid())
223 {
224 // Make the root be the first result (aka de dialogue).
225 // NOTE: we must keep a reference here otherwise it crashes inside the parent reset
226 TSharedPtr<FDialogueSearchResult> TempChild = Children[0];
227 RootSearchResult = TempChild;
228 RootSearchResult->ClearParent();
229 }
230 }
231 }
232 else
233 {
234 // Global
235 FDialogueSearchManager::Get()->QueryAllDialogues(SearchFilter, RootSearchResult);
236 }
237
238 ItemsFound = RootSearchResult->GetChildren();
239 if (ItemsFound.Num() == 0)
240 {
241 // Some Items found
242 ItemsFound.Add(MakeShared<FDialogueSearchResult>(LOCTEXT("DialogueSearchNoResults", "No Results found"), RootSearchResult));
243 HighlightText = FText::GetEmpty();
244
245 }
246 else
247 {
248 // No Items found
249 RootSearchResult->ExpandAllChildren(TreeView);
250 }
251
252 TreeView->RequestTreeRefresh();
253}
254
256{
257 TSharedPtr<SDockTab> HostTabPtr = HostTab.Pin();
258 if (HostTabPtr.IsValid())
259 {
260 return HostTabPtr->GetLayoutIdentifier().TabType;
261 }
262
263 return NAME_None;
264}
265
267{
268 TSharedPtr<SDockTab> HostTabPtr = HostTab.Pin();
269 if (HostTabPtr.IsValid())
270 {
271 HostTabPtr->RequestCloseTab();
272 }
273}
274
275void SFindInDialogues::HandleHostTabClosed(TSharedRef<SDockTab> DockTab)
276{
277 FDialogueSearchManager::Get()->CloseGlobalFindResults(SharedThis(this));
278}
279
281{
282 CurrentFilter.SearchString = Text.ToString();
283}
284
285void SFindInDialogues::HandleSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType)
286{
287 if (CommitType == ETextCommit::OnEnter)
288 {
289 CurrentFilter.SearchString = Text.ToString();
291 }
292}
293
295{
296 TSharedPtr<SFindInDialogues> GlobalFindResults = FDialogueSearchManager::Get()->GetGlobalFindResults();
297 if (GlobalFindResults.IsValid())
298 {
299 GlobalFindResults->FocusForUse(false, CurrentFilter, true);
300 }
301
302 return FReply::Handled();
303}
304
305void SFindInDialogues::HandleGetChildren(TSharedPtr<FDialogueSearchResult> InItem, TArray<TSharedPtr<FDialogueSearchResult>>& OutChildren)
306{
307 OutChildren += InItem->GetChildren();
308}
309
310void SFindInDialogues::HandleTreeSelectionDoubleClicked(TSharedPtr<FDialogueSearchResult> Item)
311{
312 if (Item.IsValid())
313 {
314 Item->OnClick();
315 }
316}
317
318TSharedRef<ITableRow> SFindInDialogues::HandleGenerateRow(TSharedPtr<FDialogueSearchResult> InItem, const TSharedRef<STableViewBase>& OwnerTable)
319{
320 // We have categories if we are searching in multiple Dialogues
321 // OR the grandparent of this item is not valid (aka root node)
322 const bool bIsCategoryWidget = !bIsInFindWithinDialogueMode &&
323 (!InItem->GetParent().IsValid() || (InItem->GetParent().IsValid() && InItem->GetParent().Pin()->IsRoot()));
324
325 // Category entry
326 if (bIsCategoryWidget)
327 {
328 return SNew(STableRow<TSharedPtr<FDialogueSearchResult>>, OwnerTable)
329 [
330 SNew(SBorder)
331 .VAlign(VAlign_Center)
332 .BorderImage(FEditorStyle::GetBrush("PropertyWindow.CategoryBackground"))
333 .Padding(FMargin(2.0f))
334 .ForegroundColor(FEditorStyle::GetColor("PropertyWindow.CategoryForeground"))
335 [
336 SNew(SHorizontalBox)
337
338 // Icon
339 +SHorizontalBox::Slot()
340 .VAlign(VAlign_Center)
341 .AutoWidth()
342 [
343 InItem->CreateIcon()
344 ]
345
346 // Display text
347 +SHorizontalBox::Slot()
348 .AutoWidth()
349 .VAlign(VAlign_Center)
350 .Padding(2, 0)
351 [
352 SNew(STextBlock)
353 .Text(InItem.Get(), &FDialogueSearchResult::GetDisplayText)
354 .ToolTipText(LOCTEXT("DialogueCatSearchToolTip", "Dialogue"))
355 ]
356 ]
357 ];
358 }
359
360 // Normal entry
361 FText CommentText = FText::GetEmpty();
362 if (!InItem->GetCommentString().IsEmpty())
363 {
364 FFormatNamedArguments Args;
365 Args.Add(TEXT("Comment"), FText::FromString(InItem->GetCommentString()));
366 CommentText = FText::Format(LOCTEXT("NodeComment", "{Comment}"), Args);
367 }
368
369 FFormatNamedArguments Args;
370 Args.Add(TEXT("Category"), InItem->GetCategory());
371 Args.Add(TEXT("DisplayTitle"), InItem->GetDisplayText());
372 FText Tooltip = FText::Format(LOCTEXT("DialogueResultSearchToolTip", "{Category} : {DisplayTitle}"), Args);
373
374 return SNew(STableRow<TSharedPtr<FDialogueSearchResult>>, OwnerTable)
375 [
376 SNew(SHorizontalBox)
377
378 // Icon
379 +SHorizontalBox::Slot()
380 .VAlign(VAlign_Center)
381 .AutoWidth()
382 [
383 InItem->CreateIcon()
384 ]
385
386 // Display text
387 +SHorizontalBox::Slot()
388 .AutoWidth()
389 .VAlign(VAlign_Center)
390 .Padding(2,0)
391 [
392 SNew(STextBlock)
393 .Text(InItem.Get(), &FDialogueSearchResult::GetDisplayText)
394 .HighlightText(HighlightText)
395 .ToolTipText(Tooltip)
396 ]
397
398 // Comment Block
399 +SHorizontalBox::Slot()
400 .FillWidth(1)
401 .HAlign(HAlign_Right)
402 .VAlign(VAlign_Center)
403 .Padding(2,0)
404 [
405 SNew(STextBlock)
406 .Text(CommentText)
407 .ColorAndOpacity(FLinearColor::Yellow)
408 ]
409 ];
410}
411
413{
414 const bool bShouldCloseWindowAfterMenuSelection = true;
415 FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, CommandList);
416
417 MenuBuilder.BeginSection("BasicOperations");
418 {
419 MenuBuilder.AddMenuEntry(FGenericCommands::Get().SelectAll);
420 MenuBuilder.AddMenuEntry(FGenericCommands::Get().Copy);
421 }
422
423 return MenuBuilder.MakeWidget();
424}
425
427{
428 FMenuBuilder MenuBuilder(true, nullptr);
429 MenuBuilder.AddMenuEntry(
430 LOCTEXT("IncludeIndices", "Include Indices"),
431 LOCTEXT("IncludeIndices_ToolTip", "Include indices of nodes/edges in the search result"),
432 FSlateIcon(),
433 FUIAction(
434 FExecuteAction::CreateLambda([this]()
435 {
438 }),
439 FCanExecuteAction(),
440 FIsActionChecked::CreateLambda([this]() -> bool
441 {
443 })
444 ),
445 NAME_None,
446 EUserInterfaceActionType::ToggleButton
447 );
448 MenuBuilder.AddMenuEntry(
449 LOCTEXT("IncludeDialogueGUID", "Include Dialogue GUIDs"),
450 LOCTEXT("IncludeDialogueGUID_ToolTip", "Include Dialogue GUIDs in search in the search result"),
451 FSlateIcon(),
452 FUIAction(
453 FExecuteAction::CreateLambda([this]()
454 {
457 }),
458 FCanExecuteAction(),
459 FIsActionChecked::CreateLambda([this]() -> bool
460 {
462 })
463 ),
464 NAME_None,
465 EUserInterfaceActionType::ToggleButton
466 );
467 MenuBuilder.AddMenuEntry(
468 LOCTEXT("IncludeNodeGUID", "Include Node GUIDs"),
469 LOCTEXT("IncludeNodeGUID_ToolTip", "Include Node GUIDs in search in the search result"),
470 FSlateIcon(),
471 FUIAction(
472 FExecuteAction::CreateLambda([this]()
473 {
476 }),
477 FCanExecuteAction(),
478 FIsActionChecked::CreateLambda([this]() -> bool
479 {
481 })
482 ),
483 NAME_None,
484 EUserInterfaceActionType::ToggleButton
485 );
486 MenuBuilder.AddMenuEntry(
487 LOCTEXT("IncludeComments", "Include Comments"),
488 LOCTEXT("IncludeComments_ToolTip", "Include Comments from nodes and comments on nodes in the search result"),
489 FSlateIcon(),
490 FUIAction(
491 FExecuteAction::CreateLambda([this]()
492 {
495 }),
496 FCanExecuteAction(),
497 FIsActionChecked::CreateLambda([this]() -> bool
498 {
500 })
501 ),
502 NAME_None,
503 EUserInterfaceActionType::ToggleButton
504 );
505 MenuBuilder.AddMenuEntry(
506 LOCTEXT("IncludeNumericalTypes", "Include Integers & Floats"),
507 LOCTEXT("IncludeNumericalTypes_ToolTip", "Include int32/floats in search result"),
508 FSlateIcon(),
509 FUIAction(
510 FExecuteAction::CreateLambda([this]()
511 {
514 }),
515 FCanExecuteAction(),
516 FIsActionChecked::CreateLambda([this]() -> bool
517 {
519 })
520 ),
521 NAME_None,
522 EUserInterfaceActionType::ToggleButton
523 );
524 MenuBuilder.AddMenuEntry(
525 LOCTEXT("IncludeTextLocalizationData", "Include Text Localization Data"),
526 LOCTEXT("IncludeTextLocalizationData_ToolTip", "Include text localization data in search (key/namespace)"),
527 FSlateIcon(),
528 FUIAction(
529 FExecuteAction::CreateLambda([this]()
530 {
533 }),
534 FCanExecuteAction(),
535 FIsActionChecked::CreateLambda([this]() -> bool
536 {
538 })
539 ),
540 NAME_None,
541 EUserInterfaceActionType::ToggleButton
542 );
543 MenuBuilder.AddMenuEntry(
544 LOCTEXT("IncludeCustomObjectNames", "Include Custom Object Names"),
545 LOCTEXT("IncludeCustomObjectNames_ToolTip", "Include the Custom Text Argument/Condition/Event/Node Data object names"),
546 FSlateIcon(),
547 FUIAction(
548 FExecuteAction::CreateLambda([this]()
549 {
552 }),
553 FCanExecuteAction(),
554 FIsActionChecked::CreateLambda([this]() -> bool
555 {
557 })
558 ),
559 NAME_None,
560 EUserInterfaceActionType::ToggleButton
561 );
562
563 return MenuBuilder.MakeWidget();
564}
565
566#undef LOCTEXT_NAMESPACE
TSharedRef< SWidget > FillFilterEntries()
void HandleGetChildren(TSharedPtr< FDialogueSearchResult > InItem, TArray< TSharedPtr< FDialogueSearchResult > > &OutChildren)
FName GetHostTabId() const
void FocusForUse(bool bSetFindWithinDialogue, const FDialogueSearchFilter &SearchFilter=FDialogueSearchFilter(), bool bSelectFirstResult=false)
TSharedPtr< SWidget > HandleContextMenuOpening()
void HandleTreeSelectionDoubleClicked(TSharedPtr< FDialogueSearchResult > Item)
TSharedRef< ITableRow > HandleGenerateRow(TSharedPtr< FDialogueSearchResult > InItem, const TSharedRef< STableViewBase > &OwnerTable)
void HandleHostTabClosed(TSharedRef< SDockTab > DockTab)
TWeakPtr< SDockTab > HostTab
TSharedPtr< SSearchBox > SearchTextBoxWidget
TSharedPtr< STreeView< TSharedPtr< FDialogueSearchResult > > > TreeView
TSharedPtr< FUICommandList > CommandList
void HandleFindModeChanged(ECheckBoxState CheckState)
FReply HandleOpenGlobalFindResults()
TWeakPtr< FDialogueEditor > DialogueEditorPtr
void HandleSearchTextCommitted(const FText &Text, ETextCommit::Type CommitType)
TWeakPtr< SVerticalBox > MainVerticalBoxWidget
void HandleSearchTextChanged(const FText &Text)
void MakeSearchQuery(const FDialogueSearchFilter &SearchFilter, bool bInIsFindWithinDialogue)
FDialogueSearchFilter CurrentFilter
TArray< TSharedPtr< FDialogueSearchResult > > ItemsFound
ECheckBoxState HandleGetFindModeChecked() const
TSharedPtr< FDialogueSearchResult > RootSearchResult
void Construct(const FArguments &InArgs, const TSharedPtr< FDialogueEditor > &InDialogueEditor=nullptr)