SExtDocumentationToolTip.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
  2. #include "SExtDocumentationToolTip.h"
  3. #include "Misc/Paths.h"
  4. #include "HAL/FileManager.h"
  5. #include "Modules/ModuleManager.h"
  6. #include "Widgets/SBoxPanel.h"
  7. #include "Framework/Application/SlateApplication.h"
  8. #include "Widgets/Text/STextBlock.h"
  9. #include "Widgets/Layout/SBox.h"
  10. #include "Widgets/SToolTip.h"
  11. #include "EditorStyleSet.h"
  12. #include "Editor/EditorPerProjectUserSettings.h"
  13. #include "IDocumentationPage.h"
  14. #include "IDocumentation.h"
  15. #include "IDocumentationProvider.h"
  16. #include "DocumentationLink.h"
  17. #include "ISourceCodeAccessor.h"
  18. #include "ISourceCodeAccessModule.h"
  19. #include "SourceControlHelpers.h"
  20. #include "EngineAnalytics.h"
  21. #include "AnalyticsEventAttribute.h"
  22. #include "Interfaces/IAnalyticsProvider.h"
  23. #include "Widgets/Input/SHyperlink.h"
  24. #include "Framework/Notifications/NotificationManager.h"
  25. #include "Widgets/Notifications/SNotificationList.h"
  26. #include "ExtDocumentation.h"
  27. void SExtDocumentationToolTip::Construct( const FArguments& InArgs )
  28. {
  29. TextContent = InArgs._Text;
  30. StyleInfo = FAppStyle::GetWidgetStyle<FTextBlockStyle>(InArgs._Style);
  31. SubduedStyleInfo = FAppStyle::GetWidgetStyle<FTextBlockStyle>(InArgs._SubduedStyle);
  32. HyperlinkTextStyleInfo = FAppStyle::GetWidgetStyle<FTextBlockStyle>(InArgs._HyperlinkTextStyle);
  33. HyperlinkButtonStyleInfo = FAppStyle::GetWidgetStyle<FButtonStyle>(InArgs._HyperlinkButtonStyle);
  34. ColorAndOpacity = InArgs._ColorAndOpacity;
  35. DocumentationLink = InArgs._DocumentationLink;
  36. IsDisplayingDocumentationLink = false;
  37. bAddDocumentation = InArgs._AddDocumentation;
  38. DocumentationMargin = InArgs._DocumentationMargin;
  39. if ( !DocumentationLink.IsEmpty() )
  40. {
  41. // All in-editor udn documents must live under the Shared/ folder
  42. ensure( InArgs._DocumentationLink.StartsWith( TEXT("Shared/") ) );
  43. }
  44. ExcerptName = InArgs._ExcerptName;
  45. IsShowingFullTip = false;
  46. if( InArgs._Content.Widget != SNullWidget::NullWidget )
  47. {
  48. // Widget content argument takes precedence
  49. // overrides the text content.
  50. OverrideContent = InArgs._Content.Widget;
  51. }
  52. AlwaysShowFullToolTip = InArgs._AlwaysShowFullToolTip;
  53. ConstructSimpleTipContent();
  54. ChildSlot
  55. [
  56. SAssignNew(WidgetContent, SBox)
  57. [
  58. SimpleTipContent.ToSharedRef()
  59. ]
  60. ];
  61. // else
  62. // {
  63. // IsShowingFullTip = true;
  64. // ConstructFullTipContent();
  65. // ChildSlot
  66. // [
  67. // SAssignNew(WidgetContent, SBox)
  68. // [
  69. // FullTipContent.ToSharedRef()
  70. // ]
  71. // ];
  72. // }
  73. }
  74. void SExtDocumentationToolTip::ConstructSimpleTipContent()
  75. {
  76. TSharedPtr< SVerticalBox > VerticalBox;
  77. if ( !OverrideContent.IsValid() )
  78. {
  79. SAssignNew( SimpleTipContent, SBox )
  80. [
  81. SAssignNew( VerticalBox, SVerticalBox )
  82. +SVerticalBox::Slot()
  83. .FillHeight( 1.0f )
  84. [
  85. SNew( STextBlock )
  86. .Text( TextContent )
  87. .TextStyle( &StyleInfo )
  88. .ColorAndOpacity( ColorAndOpacity )
  89. .WrapTextAt_Static( &SToolTip::GetToolTipWrapWidth )
  90. ]
  91. ];
  92. }
  93. else
  94. {
  95. SAssignNew( SimpleTipContent, SBox )
  96. [
  97. SAssignNew( VerticalBox, SVerticalBox )
  98. +SVerticalBox::Slot()
  99. .FillHeight( 1.0f )
  100. [
  101. OverrideContent.ToSharedRef()
  102. ]
  103. ];
  104. }
  105. if (bAddDocumentation)
  106. {
  107. AddDocumentation(VerticalBox);
  108. }
  109. }
  110. void SExtDocumentationToolTip::AddDocumentation(TSharedPtr< SVerticalBox > VerticalBox)
  111. {
  112. if ( !DocumentationLink.IsEmpty() )
  113. {
  114. IsDisplayingDocumentationLink = GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink;
  115. if ( IsDisplayingDocumentationLink )
  116. {
  117. FString OptionalExcerptName;
  118. if ( !ExcerptName.IsEmpty() )
  119. {
  120. OptionalExcerptName = FString( TEXT(" [") ) + ExcerptName + TEXT("]");
  121. }
  122. VerticalBox->AddSlot()
  123. .AutoHeight()
  124. .Padding(0, 5, 0, 0)
  125. .HAlign( HAlign_Center )
  126. [
  127. SNew( STextBlock )
  128. .Text( FText::FromString(DocumentationLink + OptionalExcerptName) )
  129. .TextStyle( &SubduedStyleInfo )
  130. ];
  131. }
  132. if ( !DocumentationPage.IsValid() )
  133. {
  134. if (const IDocumentationProvider* Provider = FDocumentationProvider::Get().GetProvider(FName(*DocumentationHostPluginName)))
  135. {
  136. DocumentationPage = Provider->GetDocumentation()->GetPage(DocumentationLink, NULL);
  137. }
  138. }
  139. if (DocumentationPage.IsValid() && DocumentationPage->HasExcerpt( ExcerptName ) )
  140. {
  141. FText MacShortcut = NSLOCTEXT("SToolTip", "MacRichTooltipShortcut", "(Cmd + Alt)");
  142. FText WinShortcut = NSLOCTEXT("SToolTip", "WinRichTooltipShortcut", "(Ctrl + Alt)");
  143. FText KeyboardShortcut;
  144. #if PLATFORM_MAC
  145. KeyboardShortcut = MacShortcut;
  146. #else
  147. KeyboardShortcut = WinShortcut;
  148. #endif
  149. VerticalBox->AddSlot()
  150. .AutoHeight()
  151. .HAlign( HAlign_Center )
  152. .Padding(0, 5, 0, 0)
  153. [
  154. SNew( STextBlock )
  155. .TextStyle( &SubduedStyleInfo )
  156. .Text( FText::Format( NSLOCTEXT( "SToolTip", "AdvancedToolTipMessage", "hold {0} for more" ), KeyboardShortcut) )
  157. ];
  158. }
  159. else
  160. {
  161. if ( IsDisplayingDocumentationLink && FSlateApplication::Get().SupportsSourceAccess() )
  162. {
  163. FString DocPath = FExtDocumentationLink::ToSourcePath( DocumentationLink, FInternationalization::Get().GetCurrentCulture() );
  164. if ( !FPaths::FileExists(DocPath) )
  165. {
  166. DocPath = FPaths::ConvertRelativePathToFull(DocPath);
  167. }
  168. VerticalBox->AddSlot()
  169. .AutoHeight()
  170. .Padding(0, 5, 0, 0)
  171. .HAlign( HAlign_Center )
  172. [
  173. SNew( SHyperlink )
  174. .Text( NSLOCTEXT( "SToolTip", "EditDocumentationMessage_Create", "create" ) )
  175. .TextStyle( &HyperlinkTextStyleInfo )
  176. .UnderlineStyle( &HyperlinkButtonStyleInfo )
  177. .OnNavigate( this, &SExtDocumentationToolTip::CreateExcerpt, DocPath, ExcerptName )
  178. ];
  179. }
  180. }
  181. }
  182. }
  183. void SExtDocumentationToolTip::CreateExcerpt( FString FileSource, FString InExcerptName )
  184. {
  185. FText CheckoutFailReason;
  186. bool bNewFile = true;
  187. bool bCheckoutOrAddSucceeded = true;
  188. if (FPaths::FileExists(FileSource))
  189. {
  190. // Check out the existing file
  191. bNewFile = false;
  192. bCheckoutOrAddSucceeded = SourceControlHelpers::CheckoutOrMarkForAdd(FileSource, NSLOCTEXT("SToolTip", "DocumentationSCCActionDesc", "tool tip excerpt"), FOnPostCheckOut(), /*out*/ CheckoutFailReason);
  193. }
  194. FArchive* FileWriter = IFileManager::Get().CreateFileWriter( *FileSource, EFileWrite::FILEWRITE_Append | EFileWrite::FILEWRITE_AllowRead | EFileWrite::FILEWRITE_EvenIfReadOnly );
  195. if (bNewFile)
  196. {
  197. FString UdnHeader;
  198. UdnHeader += "Availability:NoPublish";
  199. UdnHeader += LINE_TERMINATOR;
  200. UdnHeader += "Title:";
  201. UdnHeader += LINE_TERMINATOR;
  202. UdnHeader += "Crumbs:";
  203. UdnHeader += LINE_TERMINATOR;
  204. UdnHeader += "Description:";
  205. UdnHeader += LINE_TERMINATOR;
  206. FileWriter->Serialize( TCHAR_TO_ANSI( *UdnHeader ), UdnHeader.Len() );
  207. }
  208. FString NewExcerpt;
  209. NewExcerpt += LINE_TERMINATOR;
  210. NewExcerpt += "[EXCERPT:";
  211. NewExcerpt += InExcerptName;
  212. NewExcerpt += "]";
  213. NewExcerpt += LINE_TERMINATOR;
  214. NewExcerpt += TextContent.Get().ToString();
  215. NewExcerpt += LINE_TERMINATOR;
  216. NewExcerpt += "[/EXCERPT:";
  217. NewExcerpt += InExcerptName;
  218. NewExcerpt += "]";
  219. NewExcerpt += LINE_TERMINATOR;
  220. if (!bNewFile)
  221. {
  222. FileWriter->Seek( FMath::Max( FileWriter->TotalSize(), (int64)0 ) );
  223. }
  224. FileWriter->Serialize( TCHAR_TO_ANSI( *NewExcerpt ), NewExcerpt.Len() );
  225. FileWriter->Close();
  226. delete FileWriter;
  227. if (bNewFile)
  228. {
  229. // Add the new file
  230. bCheckoutOrAddSucceeded = SourceControlHelpers::CheckoutOrMarkForAdd(FileSource, NSLOCTEXT("SToolTip", "DocumentationSCCActionDesc", "tool tip excerpt"), FOnPostCheckOut(), /*out*/ CheckoutFailReason);
  231. }
  232. ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>("SourceCodeAccess");
  233. SourceCodeAccessModule.GetAccessor().OpenFileAtLine(FileSource, 0);
  234. if (!bCheckoutOrAddSucceeded)
  235. {
  236. FNotificationInfo Info(CheckoutFailReason);
  237. Info.ExpireDuration = 3.0f;
  238. FSlateNotificationManager::Get().AddNotification(Info);
  239. }
  240. ReloadDocumentation();
  241. }
  242. void SExtDocumentationToolTip::ConstructFullTipContent()
  243. {
  244. TArray< FExcerpt > Excerpts;
  245. DocumentationPage->GetExcerpts( Excerpts );
  246. if ( Excerpts.Num() > 0 )
  247. {
  248. int32 ExcerptIndex = 0;
  249. if ( !ExcerptName.IsEmpty() )
  250. {
  251. for (int Index = 0; Index < Excerpts.Num(); Index++)
  252. {
  253. if ( Excerpts[ Index ].Name == ExcerptName )
  254. {
  255. ExcerptIndex = Index;
  256. break;
  257. }
  258. }
  259. }
  260. if ( !Excerpts[ ExcerptIndex ].Content.IsValid() )
  261. {
  262. DocumentationPage->GetExcerptContent( Excerpts[ ExcerptIndex ] );
  263. }
  264. if ( Excerpts[ ExcerptIndex ].Content.IsValid() )
  265. {
  266. TSharedPtr< SVerticalBox > Box;
  267. FullTipContent =
  268. SNew(SBox)
  269. .Padding(DocumentationMargin)
  270. [
  271. SAssignNew(Box, SVerticalBox)
  272. + SVerticalBox::Slot()
  273. .HAlign(HAlign_Center)
  274. .AutoHeight()
  275. [
  276. Excerpts[ExcerptIndex].Content.ToSharedRef()
  277. ]
  278. ];
  279. FString* FullDocumentationLink = Excerpts[ ExcerptIndex ].Variables.Find( TEXT("ToolTipFullLink") );
  280. if ( FullDocumentationLink != NULL && !FullDocumentationLink->IsEmpty() )
  281. {
  282. struct Local
  283. {
  284. static void OpenLink( FString Link )
  285. {
  286. if (!IDocumentation::Get()->Open(Link, FDocumentationSourceInfo(TEXT("rich_tooltips"))))
  287. {
  288. FNotificationInfo Info( NSLOCTEXT("SToolTip", "FailedToOpenLink", "Failed to Open Link") );
  289. FSlateNotificationManager::Get().AddNotification(Info);
  290. }
  291. }
  292. };
  293. Box->AddSlot()
  294. .HAlign( HAlign_Center )
  295. .AutoHeight()
  296. [
  297. SNew( SHyperlink )
  298. .Text( NSLOCTEXT( "SToolTip", "GoToFullDocsLinkMessage", "see full documentation" ) )
  299. .TextStyle( &HyperlinkTextStyleInfo )
  300. .UnderlineStyle( &HyperlinkButtonStyleInfo )
  301. .OnNavigate_Static( &Local::OpenLink, *FullDocumentationLink )
  302. ];
  303. }
  304. if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink && FSlateApplication::Get().SupportsSourceAccess() )
  305. {
  306. struct Local
  307. {
  308. static void EditSource( FString Link, int32 LineNumber )
  309. {
  310. ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>("SourceCodeAccess");
  311. SourceCodeAccessModule.GetAccessor().OpenFileAtLine(Link, LineNumber);
  312. }
  313. };
  314. Box->AddSlot()
  315. .AutoHeight()
  316. .HAlign( HAlign_Center )
  317. [
  318. SNew( SHyperlink )
  319. .Text( NSLOCTEXT( "SToolTip", "EditDocumentationMessage_Edit", "edit" ) )
  320. .TextStyle( &HyperlinkTextStyleInfo )
  321. .UnderlineStyle( &HyperlinkButtonStyleInfo )
  322. .OnNavigate_Static(&Local::EditSource, FPaths::ConvertRelativePathToFull(FExtDocumentationLink::ToSourcePath(DocumentationLink, FInternationalization::Get().GetCurrentCulture())), Excerpts[ExcerptIndex].LineNumber)
  323. ];
  324. }
  325. }
  326. }
  327. }
  328. FReply SExtDocumentationToolTip::ReloadDocumentation()
  329. {
  330. SimpleTipContent.Reset();
  331. FullTipContent.Reset();
  332. ConstructSimpleTipContent();
  333. if ( DocumentationPage.IsValid() )
  334. {
  335. DocumentationPage->Reload();
  336. if ( DocumentationPage->HasExcerpt( ExcerptName ) )
  337. {
  338. ConstructFullTipContent();
  339. }
  340. }
  341. return FReply::Handled();
  342. }
  343. void SExtDocumentationToolTip::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
  344. {
  345. const FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
  346. const bool NeedsUpdate = IsDisplayingDocumentationLink != GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink;
  347. if ( !IsShowingFullTip && (AlwaysShowFullToolTip || (!AlwaysShowFullToolTip && ModifierKeys.IsAltDown() && ModifierKeys.IsControlDown())) )
  348. {
  349. if ( !FullTipContent.IsValid() && DocumentationPage.IsValid() && DocumentationPage->HasExcerpt( ExcerptName ) )
  350. {
  351. ConstructFullTipContent();
  352. }
  353. else if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
  354. {
  355. ReloadDocumentation();
  356. }
  357. if ( FullTipContent.IsValid() )
  358. {
  359. WidgetContent->SetContent( FullTipContent.ToSharedRef() );
  360. IsShowingFullTip = true;
  361. // Analytics event
  362. if (FEngineAnalytics::IsAvailable())
  363. {
  364. TArray<FAnalyticsEventAttribute> Params;
  365. Params.Add(FAnalyticsEventAttribute(TEXT("Page"), DocumentationLink));
  366. Params.Add(FAnalyticsEventAttribute(TEXT("Excerpt"), ExcerptName));
  367. FEngineAnalytics::GetProvider().RecordEvent(TEXT("Editor.Usage.Documentation.FullTooltipShown"), Params);
  368. }
  369. }
  370. }
  371. else if ( ( IsShowingFullTip || NeedsUpdate ) && !AlwaysShowFullToolTip && ( !ModifierKeys.IsAltDown() || !ModifierKeys.IsControlDown() ) )
  372. {
  373. if ( NeedsUpdate )
  374. {
  375. ReloadDocumentation();
  376. IsDisplayingDocumentationLink = GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink;
  377. }
  378. WidgetContent->SetContent( SimpleTipContent.ToSharedRef() );
  379. IsShowingFullTip = false;
  380. }
  381. }
  382. bool SExtDocumentationToolTip::IsInteractive() const
  383. {
  384. const FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
  385. return ( DocumentationPage.IsValid() && ModifierKeys.IsAltDown() && ModifierKeys.IsControlDown() );
  386. }
  387. #ifdef EXT_DOC_NAMESPACE
  388. #undef EXT_DOC_NAMESPACE
  389. #endif