diff --git a/Resources/DTFluxRaceResult16x16.svg b/Resources/DTFluxRaceResult16x16.svg
new file mode 100644
index 0000000..66659fe
--- /dev/null
+++ b/Resources/DTFluxRaceResult16x16.svg
@@ -0,0 +1,41 @@
+
+
+
+
diff --git a/Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs b/Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs
new file mode 100644
index 0000000..1b3f118
--- /dev/null
+++ b/Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs
@@ -0,0 +1,34 @@
+using UnrealBuildTool;
+
+public class DTFluxRaceResult : ModuleRules
+{
+ public DTFluxRaceResult(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core"
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "Slate",
+ "SlateCore",
+ "DTFluxProjectSettings",
+ "UMG",
+ "WebBrowser",
+ "Projects",
+ "ToolMenus",
+ "HTTP",
+ "JsonUtilities",
+ "Json"
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp b/Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp
new file mode 100644
index 0000000..f9d99a0
--- /dev/null
+++ b/Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp
@@ -0,0 +1,161 @@
+#include "DTFluxRaceResultModule.h"
+
+#include "LevelEditor.h"
+#include "Widget/SDTFluxRaceResultWidget.h"
+#include "Widget/Style/DTFluxRaceResultStyle.h"
+
+#define LOCTEXT_NAMESPACE "FDTFluxRaceResultModule"
+
+DEFINE_LOG_CATEGORY(logDTFluxRaceResult)
+
+FName DTFLUXRACERESULT_API FDTFluxRaceResult::RaceResultTabId = "DTFluxRaceResult";
+FText DTFLUXRACERESULT_API FDTFluxRaceResult::RaceResultTabDisplayName = FText::FromString(TEXT("DTFlux RaceResult"));
+
+
+void DTFLUXRACERESULT_API FDTFluxRaceResult::StartupModule()
+{
+ UE_LOG(logDTFluxRaceResult, Warning, TEXT("DTFluxRaceResult Module Started"))
+ FDTFluxRaceResultStyle::RegisterStyle();
+ InitMenuExtension();
+ RegisterRaceResultTab();
+}
+
+#pragma region MenuExtension
+
+
+void DTFLUXRACERESULT_API FDTFluxRaceResult::InitMenuExtension()
+{
+ UToolMenus::RegisterStartupCallback(
+ FSimpleMulticastDelegate::FDelegate::CreateRaw(this,
+ &FDTFluxRaceResult::RegisterMenuExtensions));
+}
+
+
+void DTFLUXRACERESULT_API FDTFluxRaceResult::RegisterMenuExtensions()
+{
+
+ // Étendre le menu enregistré
+ if (UToolMenu* DTFluxMenu = UToolMenus::Get()->ExtendMenu("DTFlux.MainMenu"))
+ {
+ FToolMenuSection& ToolsSection = DTFluxMenu->FindOrAddSection("Tools");
+ ToolsSection.AddMenuEntry(
+ "DTFluxRaceResult",
+ FText::FromString("RaceResult"),
+ FText::FromString("Launch DTFlux RaceResult Control Panel"),
+ FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
+ FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked))
+ );
+ }
+
+}
+
+void FDTFluxRaceResult::CreateSubmenu(UToolMenu* Menu)
+{
+ // Section 2 : Tools
+ FToolMenuSection& ToolsSection = Menu->FindOrAddSection("Tools");
+ ToolsSection.Label = FText::FromString("Tools");
+ ToolsSection.AddMenuEntry(
+ "DTFluxRaceResult",
+ FText::FromString("RaceResult"),
+ FText::FromString("Launch Race Result WebAdmin Console"),
+ FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
+ // Adaptez selon votre icône
+ FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked))
+ );
+}
+
+void DTFLUXRACERESULT_API FDTFluxRaceResult::OnButtonClicked()
+{
+ FGlobalTabmanager::Get()->TryInvokeTab(RaceResultTabId);
+ UE_LOG(LogTemp, Log, TEXT("Race Result Launched"))
+}
+
+#pragma endregion EditorTab
+
+#pragma region
+void DTFLUXRACERESULT_API FDTFluxRaceResult::RegisterRaceResultTab()
+{
+ FTabSpawnerEntry& SpawnerEntry =
+ FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
+ RaceResultTabId,
+ FOnSpawnTab::CreateRaw(this, &FDTFluxRaceResult::OnSpawnTab)
+ )
+ .SetDisplayName(RaceResultTabDisplayName)
+ .SetTooltipText(FText::FromString(TEXT("Race Result Control Panel")));
+}
+
+TSharedRef DTFLUXRACERESULT_API FDTFluxRaceResult::OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs)
+{
+ return
+ SNew(
+ SDockTab
+ )
+ .TabRole(ETabRole::NomadTab)
+ // .ShouldAutosize(true)
+ [
+ SNew(SDTFluxRaceResultWidget)
+ ];
+}
+
+#pragma endregion
+void DTFLUXRACERESULT_API FDTFluxRaceResult::ShutdownModule()
+{
+ FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(RaceResultTabId);
+ FDTFluxRaceResultStyle::UnregisterStyle();
+}
+
+
+//
+// // Dans votre code de debug
+// void DTFLUXRACERESULT_API FDTFluxRaceResult::DebugMenus()
+// {
+// UToolMenus* ToolMenus = UToolMenus::Get();
+// if (ToolMenus)
+// {
+// TArray MenuNames;
+// ToolMenus->GetAllMenuNames(MenuNames);
+//
+// UE_LOG(logDTFluxRaceResult, Warning, TEXT("=== ALL AVAILABLE MENUS ==="));
+// for (const FName& MenuName : MenuNames)
+// {
+// UE_LOG(logDTFluxRaceResult, Warning, TEXT("Menu: %s"), *MenuName.ToString());
+//
+// // Obtenir les sections de chaque menu
+// UToolMenu* Menu = ToolMenus->FindMenu(MenuName);
+// if (Menu)
+// {
+// for (const FToolMenuSection& Section : Menu->Sections)
+// {
+// UE_LOG(logDTFluxRaceResult, Warning, TEXT(" Section: %s"), *Section.Name.ToString());
+// }
+// }
+// }
+// UE_LOG(logDTFluxRaceResult, Warning, TEXT("=== END MENU LIST ==="));
+// }
+// }
+
+
+// void DTFLUXRACERESULT_API FDTFluxRaceResult::AddMenu(FMenuBarBuilder& MenuBarBuilder)
+// {
+// MenuBarBuilder.AddPullDownMenu(
+// FText::FromString("DTFlux"),
+// FText::FromString("DTFlux API Tools"),
+// FNewMenuDelegate::CreateRaw(this, &FDTFluxRaceResult::FillMenu)
+// );
+// }
+
+// void DTFLUXRACERESULT_API FDTFluxRaceResult::FillMenu(FMenuBuilder& MenuBuilder)
+// {
+// MenuBuilder.AddMenuEntry(
+// FText::FromString("RaceResult ControlPanel"),
+// FText::FromString("Launch RaceResult Control Panel"),
+// FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
+// FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked)
+// );
+// MenuBuilder.EndSection();
+// }
+
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FDTFluxRaceResult, DTFluxRaceResult)
diff --git a/Source/DTFluxRaceResult/Private/Widget/SDTFluxRaceResultWidget.cpp b/Source/DTFluxRaceResult/Private/Widget/SDTFluxRaceResultWidget.cpp
new file mode 100644
index 0000000..e601412
--- /dev/null
+++ b/Source/DTFluxRaceResult/Private/Widget/SDTFluxRaceResultWidget.cpp
@@ -0,0 +1,283 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Widget/SDTFluxRaceResultWidget.h"
+
+#include "DTFluxRaceResultModule.h"
+#include "HttpModule.h"
+#include "IWebBrowserCookieManager.h"
+#include "IWebBrowserWindow.h"
+#include "SlateOptMacros.h"
+#include "WebBrowserModule.h"
+#include "Interfaces/IHttpRequest.h"
+#include "Interfaces/IHttpResponse.h"
+
+BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
+
+void SDTFluxRaceResultWidget::Construct(const FArguments& InArgs)
+{
+
+ FWebBrowserInitSettings browserInitSettings = FWebBrowserInitSettings();
+
+ IWebBrowserModule::Get().CustomInitialize(browserInitSettings);
+ WindowSettings.InitialURL = TEXT("about:blank");
+ WindowSettings.BrowserFrameRate = 25;
+
+ if (IWebBrowserModule::IsAvailable() && IWebBrowserModule::Get().IsWebModuleAvailable())
+ {
+ IWebBrowserSingleton* WebBrowserSingleton = IWebBrowserModule::Get().GetSingleton();
+ Browser = WebBrowserSingleton->CreateBrowserWindow(WindowSettings);
+ // Browser->OnLoadUrl().BindRaw(this, &SDTFluxRaceResultWidget::OnLoadOverride);
+
+ }
+
+
+ ChildSlot
+ [
+ SNew(SBox)
+ .Padding(5.0f)
+ [
+ SNew(SBorder)
+ .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
+ [
+ SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ [
+ SAssignNew(WebBrowser, SWebBrowser, Browser)
+ .ShowControls(true)
+ .SupportsTransparency(true)
+ .OnUrlChanged(this, &SDTFluxRaceResultWidget::OnUrlChanged)
+ .OnBeforeNavigation(this, &SDTFluxRaceResultWidget::OnBeforeNavigation)
+ .OnLoadCompleted(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadCompleted))
+ .OnLoadError(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadError))
+ .OnLoadStarted(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadStarted))
+ .ShowErrorMessage(true)
+ .ShowAddressBar(true)
+ ]
+ ]
+ ]
+ ];
+
+}
+
+void SDTFluxRaceResultWidget::OnCookieSet(bool bSuccess)
+{
+}
+
+
+void SDTFluxRaceResultWidget::LoadContentViaHTTP()
+{
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading initial content via HTTP: %s"), *RaceResultUrl);
+ LoadSpecificURL(RaceResultUrl);
+}
+
+
+void SDTFluxRaceResultWidget::LoadSpecificURL(const FString& Url)
+{
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading via HTTP: %s"), *Url);
+
+ // Créer la requête HTTP
+ TSharedRef Request = FHttpModule::Get().CreateRequest();
+
+ // Ajouter l'authentification Basic pour TOUTES les requêtes
+ FString Credentials = Username + TEXT(":") + Password;
+ FString EncodedCredentials = FBase64::Encode(Credentials);
+
+ Request->SetURL(Url);
+ Request->SetVerb("GET");
+ Request->SetHeader("Authorization", "Basic " + EncodedCredentials);
+ Request->SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
+ Request->SetHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
+ Request->SetHeader("Accept-Language", "en-US,en;q=0.5");
+ Request->SetHeader("Accept-Encoding", "gzip, deflate");
+ Request->SetHeader("Connection", "keep-alive");
+
+ Request->OnProcessRequestComplete().BindRaw(this, &SDTFluxRaceResultWidget::OnHTTPContentLoaded);
+ Request->ProcessRequest();
+
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("HTTP request sent with Basic Auth"));
+}
+
+void SDTFluxRaceResultWidget::OnHTTPContentLoaded(TSharedPtr Request,
+ TSharedPtr HttpResponse, bool bWasSuccessful)
+{
+ if (bWasSuccessful && HttpResponse.IsValid())
+ {
+ int32 ResponseCode = HttpResponse->GetResponseCode();
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("HTTP Response Code: %d"), ResponseCode);
+
+ if (ResponseCode == 200)
+ {
+ FString Content = HttpResponse->GetContentAsString();
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Content loaded successfully, size: %d characters"), Content.Len());
+
+ // Traiter le contenu HTML
+ FString ProcessedContent = ProcessHTMLContent(Content, Request->GetURL());
+
+ // Charger le contenu dans le browser via LoadString
+ if (Browser.IsValid())
+ {
+ Browser->LoadString(ProcessedContent, Request->GetURL());
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Content loaded into browser"));
+ }
+ }
+ else if (ResponseCode == 401)
+ {
+ UE_LOG(logDTFluxRaceResult, Error, TEXT("Authentication failed - 401 Unauthorized. Check your credentials."));
+ }
+ else if (ResponseCode == 403)
+ {
+ UE_LOG(logDTFluxRaceResult, Error, TEXT("Access forbidden - 403 Forbidden"));
+ }
+ else if (ResponseCode == 404)
+ {
+ UE_LOG(logDTFluxRaceResult, Error, TEXT("Page not found - 404 Not Found"));
+ }
+ else
+ {
+ UE_LOG(logDTFluxRaceResult, Error, TEXT("HTTP request failed with code: %d"), ResponseCode);
+ }
+ }
+ else
+ {
+ UE_LOG(logDTFluxRaceResult, Error, TEXT("HTTP request failed completely"));
+ }
+}
+
+void SDTFluxRaceResultWidget::OnLoadOverride()
+{
+}
+
+
+void SDTFluxRaceResultWidget::OnUrlChanged(const FText& NewUrl)
+{
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("URL changed to: %s"), *NewUrl.ToString());
+}
+
+FString SDTFluxRaceResultWidget::ProcessHTMLContent(const FString& Content, const FString& BaseUrl)
+{
+ FString ProcessedContent = Content;
+
+ // Extraire le domaine de base
+ FString BaseDomain = ExtractDomain(BaseUrl);
+
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Processing HTML content, base domain: %s"), *BaseDomain);
+
+ // Convertir tous les liens relatifs en liens absolus
+ ProcessedContent = ProcessedContent.Replace(TEXT("src=\"/"), *FString::Printf(TEXT("src=\"%s/"), *BaseDomain));
+ ProcessedContent = ProcessedContent.Replace(TEXT("href=\"/"), *FString::Printf(TEXT("href=\"%s/"), *BaseDomain));
+ ProcessedContent = ProcessedContent.Replace(TEXT("src='/"), *FString::Printf(TEXT("src='%s/"), *BaseDomain));
+ ProcessedContent = ProcessedContent.Replace(TEXT("href='/"), *FString::Printf(TEXT("href='%s/"), *BaseDomain));
+
+ // Gérer les liens relatifs avec ./
+ ProcessedContent = ProcessedContent.Replace(TEXT("src=\"./"), *FString::Printf(TEXT("src=\"%s/"), *BaseDomain));
+ ProcessedContent = ProcessedContent.Replace(TEXT("href=\"./"), *FString::Printf(TEXT("href=\"%s/"), *BaseDomain));
+
+ // Ajouter une balise base pour aider avec les liens relatifs restants
+ FString BaseTag = FString::Printf(TEXT(""), *BaseDomain);
+ if (ProcessedContent.Contains(TEXT("")))
+ {
+ FString HeadReplacement = TEXT("") + BaseTag;
+ ProcessedContent = ProcessedContent.Replace(TEXT(""), *HeadReplacement);
+ }
+ else if (ProcessedContent.Contains(TEXT("")))
+ {
+ FString HeadReplacement = TEXT("") + BaseTag;
+ ProcessedContent = ProcessedContent.Replace(TEXT(""), *HeadReplacement);
+ }
+
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("HTML content processed"));
+ return ProcessedContent;
+}
+
+
+FString SDTFluxRaceResultWidget::ExtractDomain(const FString& Url)
+{
+ FString Domain = Url;
+
+ // Enlever le protocole
+ if (Url.StartsWith(TEXT("https://")))
+ {
+ Domain = Url.Mid(8);
+ }
+ else if (Url.StartsWith(TEXT("http://")))
+ {
+ Domain = Url.Mid(7);
+ }
+
+ // Trouver le premier slash (début du path)
+ int32 SlashIndex;
+ if (Domain.FindChar(TEXT('/'), SlashIndex))
+ {
+ Domain = Url.Left(Url.Len() - (Domain.Len() - SlashIndex));
+ }
+
+ // Enlever le port si présent
+ FString DomainPart = Domain;
+ if (Domain.StartsWith(TEXT("https://")))
+ {
+ DomainPart = Domain.Mid(8);
+ }
+ else if (Domain.StartsWith(TEXT("http://")))
+ {
+ DomainPart = Domain.Mid(7);
+ }
+
+ int32 ColonIndex;
+ if (DomainPart.FindChar(TEXT(':'), ColonIndex))
+ {
+ Domain = Domain.Left(Domain.Len() - (DomainPart.Len() - ColonIndex));
+ }
+
+ return Domain;
+}
+
+bool SDTFluxRaceResultWidget::OnBeforeNavigation(const FString& Url, const FWebNavigationRequest& Request)
+{
+
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading via HTTP: %s"), *Url);
+
+ TSharedRef HTTPRequest = FHttpModule::Get().CreateRequest();
+
+ // Authentification pour toutes les requêtes
+ FString Credentials = Username + TEXT(":") + Password;
+ FString EncodedCredentials = FBase64::Encode(Credentials);
+
+ HTTPRequest->SetURL(RaceResultUrl);
+ HTTPRequest->SetVerb("GET");
+ HTTPRequest->SetHeader("Authorization", "Basic " + EncodedCredentials);
+ HTTPRequest->SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
+ HTTPRequest->SetHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
+
+ HTTPRequest->OnProcessRequestComplete().BindRaw(this, &SDTFluxRaceResultWidget::OnHTTPContentLoaded);
+ HTTPRequest->ProcessRequest();
+
+ return true;
+}
+
+void SDTFluxRaceResultWidget::OnLoadCompleted()
+{
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Completed"));
+
+}
+
+void SDTFluxRaceResultWidget::OnLoadStarted()
+{
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Started"));
+}
+
+void SDTFluxRaceResultWidget::OnLoadError()
+{
+ UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Error"));
+}
+
+void SDTFluxRaceResultWidget::OnBeforeResourceLoad(FString Url, FString ResourceType, FContextRequestHeaders& AdditionalHeaders, const bool AllowUserCredentials)
+{
+}
+
+void SDTFluxRaceResultWidget::SetupBasicAuth()
+{
+
+}
+
+END_SLATE_FUNCTION_BUILD_OPTIMIZATION
diff --git a/Source/DTFluxRaceResult/Private/Widget/Style/DTFluxRaceResultStyle.cpp b/Source/DTFluxRaceResult/Private/Widget/Style/DTFluxRaceResultStyle.cpp
new file mode 100644
index 0000000..bb36f95
--- /dev/null
+++ b/Source/DTFluxRaceResult/Private/Widget/Style/DTFluxRaceResultStyle.cpp
@@ -0,0 +1,47 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Widget/Style/DTFluxRaceResultStyle.h"
+
+
+#include "Interfaces/IPluginManager.h"
+#include "Styling/SlateStyleRegistry.h"
+#include "Styling/SlateStyleMacros.h"
+
+#define RootToContentDir Style->RootToContentDir
+
+TSharedPtr FDTFluxRaceResultStyle::StyleSet = nullptr;
+
+void FDTFluxRaceResultStyle::RegisterStyle()
+{
+ if(StyleSet.IsValid()) return;
+
+ StyleSet = Create();
+ FSlateStyleRegistry::RegisterSlateStyle(*StyleSet);
+}
+
+void FDTFluxRaceResultStyle::UnregisterStyle()
+{
+ if(StyleSet.IsValid())
+ {
+ FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet);
+ ensure(StyleSet.IsUnique());
+ StyleSet.Reset();
+ }
+
+
+}
+
+void FDTFluxRaceResultStyle::ReloadTextures()
+{
+}
+
+TSharedPtr FDTFluxRaceResultStyle::Create()
+{
+ TSharedPtr Style = MakeShareable(new FSlateStyleSet("DTFluxRaceResultStyle"));
+ Style->SetContentRoot(IPluginManager::Get().FindPlugin("DTFluxAPI")->GetBaseDir()/TEXT("Resources"));
+
+ Style->Set("LevelEditor.Tab.IconRaceResult", new IMAGE_BRUSH_SVG("DTFluxRaceResult16x16", FVector2d(16)) );
+ return Style;
+}
+
diff --git a/Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h b/Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h
new file mode 100644
index 0000000..444a717
--- /dev/null
+++ b/Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Modules/ModuleManager.h"
+
+DTFLUXRACERESULT_API DECLARE_LOG_CATEGORY_EXTERN(logDTFluxRaceResult, All, All);
+
+class DTFLUXRACERESULT_API FDTFluxRaceResult : public IModuleInterface
+{
+public:
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+
+#pragma region MenuExtention
+ void RegisterMenuExtensions();
+ void InitMenuExtension();
+ void CreateSubmenu(UToolMenu* Menu);
+
+ // void AddMenu(FMenuBarBuilder& MenuBarBuilder);
+ // void FillMenu(FMenuBuilder& MenuBuilder);
+ void OnButtonClicked();
+#pragma endregion
+
+#pragma region EditorTab
+ void RegisterRaceResultTab();
+ TSharedRef OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs);
+private:
+ // static void DebugMenus();
+ static FName RaceResultTabId;
+ static FText RaceResultTabDisplayName;
+ TSharedPtr RaceResultWidget;
+#pragma endregion
+};
diff --git a/Source/DTFluxRaceResult/Public/Widget/SDTFluxRaceResultWidget.h b/Source/DTFluxRaceResult/Public/Widget/SDTFluxRaceResultWidget.h
new file mode 100644
index 0000000..25bc2f0
--- /dev/null
+++ b/Source/DTFluxRaceResult/Public/Widget/SDTFluxRaceResultWidget.h
@@ -0,0 +1,54 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "SWebBrowser.h"
+#include "Widgets/SCompoundWidget.h"
+
+class IHttpResponse;
+class IHttpRequest;
+/**
+ *
+ */
+class DTFLUXRACERESULT_API SDTFluxRaceResultWidget : public SCompoundWidget
+{
+public:
+ SLATE_BEGIN_ARGS(SDTFluxRaceResultWidget)
+ {
+ }
+
+ SLATE_END_ARGS()
+
+ /** Constructs this widget with InArgs */
+ void Construct(const FArguments& InArgs);
+ void OnCookieSet(bool bSuccess);
+ void LoadSpecificURL(const FString& Url);
+ void LoadContentViaHTTP();
+
+private:
+ TSharedPtr WebBrowser;
+ TSharedPtr Browser;
+ TSharedPtr BrowserAdapter;
+ FCreateBrowserWindowSettings WindowSettings;
+
+ void OnUrlChanged(const FText& NewUrl);
+ FString ProcessHTMLContent(const FString& Content, const FString& BaseUrl);
+ FString ExtractDomain(const FString& Url);
+ void OnHTTPContentLoaded(TSharedPtr Request, TSharedPtr HttpResponse, bool bWasSuccessful);
+ bool OnBeforeNavigation(const FString& Url, const FWebNavigationRequest& Request);
+ void OnLoadCompleted();
+ void OnLoadStarted();
+ void OnLoadError();
+
+ void OnLoadOverride();
+
+ void OnBeforeResourceLoad(FString Url, FString ResourceType, FContextRequestHeaders& AdditionalHeaders, const bool AllowUserCredentials);
+
+ FString RaceResultUrl = "https://raceresult.tds-france.com";
+ FString Username = "sporkrono";
+ FString Password = "Notre 3ème décennie d'action pour le climat";
+
+
+ void SetupBasicAuth();
+};
diff --git a/Source/DTFluxRaceResult/Public/Widget/Style/DTFluxRaceResultStyle.h b/Source/DTFluxRaceResult/Public/Widget/Style/DTFluxRaceResultStyle.h
new file mode 100644
index 0000000..ad9fc1e
--- /dev/null
+++ b/Source/DTFluxRaceResult/Public/Widget/Style/DTFluxRaceResultStyle.h
@@ -0,0 +1,34 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Styling/ISlateStyle.h"
+
+
+/**
+ *
+ */
+class DTFLUXRACERESULT_API FDTFluxRaceResultStyle
+{
+
+public:
+ static void RegisterStyle();
+ static void UnregisterStyle();
+
+ static void ReloadTextures();
+
+ static const ISlateStyle& Get()
+ {
+ return *StyleSet;
+ }
+
+ static const FName& GetStyleSetName()
+ {
+ return StyleSet->GetStyleSetName();
+ }
+
+private:
+ static TSharedPtr Create();
+ static TSharedPtr StyleSet;
+};