2022-02-01 18:09:40 +01:00
# include <hex.hpp>
2024-02-26 21:48:56 +01:00
# include <hex/api/workspace_manager.hpp>
2022-02-01 18:09:40 +01:00
# include <hex/api/content_registry.hpp>
2023-11-21 14:38:01 +01:00
# include <hex/api/localization_manager.hpp>
2022-12-29 19:26:00 +01:00
# include <hex/api/theme_manager.hpp>
2023-05-11 18:44:50 +02:00
# include <hex/api/layout_manager.hpp>
2023-08-06 21:33:15 +02:00
# include <hex/api/achievement_manager.hpp>
2023-06-20 11:55:56 +02:00
# include <hex/api_urls.hpp>
2024-02-26 21:48:56 +01:00
impr: Refactor and restructure Event Manager (#2082)
### Problem description
This PR addresses issue #2013 that described a cluttered Event Manager.
This is a DX issue and should not impact the users whatsoever.
### Implementation description
The changes revolve around three main points:
1. the Event Manager (`event_manager.hpp`) was split into four
categories: GUI, Interaction, Lifecycle, and Provider, and two types:
Events, and Requests. This results in the following files:
- `events_gui.hpp`
- `events_interaction.hpp`
- `events_lifecycle.hpp`
- `events_provider.hpp`
- `requests_gui.hpp`
- `requests_interaction.hpp`
- `requests_lifecycle.hpp`
- `requests_provider.hpp`
2. Every event and request now has its own piece of documentation, with
a `@brief`, accompanied by a longer comment if needed, and gets its
`@param`s described.
3. The old `event_manager.hpp` import was removed and replaced by the
correct imports wherever needed, as to reduce spread of those files only
to where they are truly useful.
### Additional things
The commits have been split into (chrono-)logical steps:
- `feat`: split the Event Manager, and replace the imports
- `refactor`, `chore`: make various small changes to match the required
structure
- `docs`: add documentation for events and requests
Hopefully, this will help to review the PR.
*Note: Beware of very long rebuild times in between the commits, use
them sparingly! The Actions will ensure this PR builds anyways*
Closes #2013
---------
Signed-off-by: BioTheWolff <47079795+BioTheWolff@users.noreply.github.com>
Co-authored-by: Nik <werwolv98@gmail.com>
2025-01-25 16:32:07 +01:00
# include <hex/api/events/events_provider.hpp>
# include <hex/api/events/events_gui.hpp>
# include <hex/api/events/requests_gui.hpp>
2022-02-01 23:33:42 +01:00
# include <hex/ui/view.hpp>
2024-02-26 21:48:56 +01:00
# include <toasts/toast_notification.hpp>
# include <hex/helpers/http_requests.hpp>
2022-03-04 11:36:37 +01:00
# include <hex/helpers/fs.hpp>
2022-02-01 18:09:40 +01:00
# include <hex/helpers/logger.hpp>
2024-06-22 10:44:55 +02:00
# include <hex/helpers/default_paths.hpp>
2022-02-01 23:33:42 +01:00
2022-08-08 21:23:52 +02:00
# include <hex/api/project_file_manager.hpp>
2022-02-01 18:09:40 +01:00
# include <imgui.h>
# include <hex/ui/imgui_imhex_extensions.h>
2022-02-01 23:33:42 +01:00
# include <nlohmann/json.hpp>
2022-02-01 18:09:40 +01:00
# include <romfs/romfs.hpp>
2023-03-12 18:27:29 +01:00
# include <wolv/io/file.hpp>
# include <wolv/io/fs.hpp>
2024-12-14 20:36:09 +01:00
# include <fonts/vscode_icons.hpp>
2022-02-01 18:09:40 +01:00
2023-05-27 16:59:30 +02:00
# include <content/recent.hpp>
2023-04-08 00:58:53 +02:00
2022-08-24 00:18:10 +02:00
# include <string>
2022-10-27 13:21:54 +02:00
# include <random>
2025-02-15 11:15:56 +01:00
# include <banners/banner_button.hpp>
# include <banners/banner_icon.hpp>
2022-02-01 18:09:40 +01:00
namespace hex : : plugin : : builtin {
2023-12-07 11:18:49 +01:00
namespace {
2025-01-27 22:10:30 +01:00
AutoReset < ImGuiExt : : Texture > s_bannerTexture , s_nightlyTexture , s_backdropTexture , s_infoBannerTexture ;
2023-12-07 11:18:49 +01:00
std : : string s_tipOfTheDay ;
bool s_simplifiedWelcomeScreen = false ;
class PopupRestoreBackup : public Popup < PopupRestoreBackup > {
private :
std : : fs : : path m_logFilePath ;
std : : function < void ( ) > m_restoreCallback ;
std : : function < void ( ) > m_deleteCallback ;
bool m_reportError = true ;
public :
PopupRestoreBackup ( std : : fs : : path logFilePath , const std : : function < void ( ) > & restoreCallback , const std : : function < void ( ) > & deleteCallback )
: Popup ( " hex.builtin.popup.safety_backup.title " ) ,
m_logFilePath ( std : : move ( logFilePath ) ) ,
m_restoreCallback ( restoreCallback ) ,
m_deleteCallback ( deleteCallback ) {
2024-02-03 12:16:36 +01:00
m_reportError = ContentRegistry : : Settings : : read < bool > ( " hex.builtin.setting.general " , " hex.builtin.setting.general.upload_crash_logs " , true ) ;
2023-12-07 11:18:49 +01:00
}
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
void drawContent ( ) override {
ImGui : : TextUnformatted ( " hex.builtin.popup.safety_backup.desc " _lang ) ;
2023-12-19 13:10:25 +01:00
if ( ! m_logFilePath . empty ( ) ) {
2023-12-07 11:18:49 +01:00
ImGui : : NewLine ( ) ;
ImGui : : TextUnformatted ( " hex.builtin.popup.safety_backup.log_file " _lang ) ;
ImGui : : SameLine ( 0 , 2 _scaled ) ;
2023-12-19 13:10:25 +01:00
if ( ImGuiExt : : Hyperlink ( m_logFilePath . filename ( ) . string ( ) . c_str ( ) ) ) {
fs : : openFolderWithSelectionExternal ( m_logFilePath ) ;
2023-12-07 11:18:49 +01:00
}
2023-12-19 13:10:25 +01:00
ImGui : : Checkbox ( " hex.builtin.popup.safety_backup.report_error " _lang , & m_reportError ) ;
2023-12-07 11:18:49 +01:00
ImGui : : NewLine ( ) ;
2023-05-22 13:24:48 +02:00
}
2023-06-20 11:55:56 +02:00
2023-12-07 11:18:49 +01:00
auto width = ImGui : : GetWindowWidth ( ) ;
ImGui : : SetCursorPosX ( width / 9 ) ;
if ( ImGui : : Button ( " hex.builtin.popup.safety_backup.restore " _lang , ImVec2 ( width / 3 , 0 ) ) ) {
2023-12-19 13:10:25 +01:00
m_restoreCallback ( ) ;
m_deleteCallback ( ) ;
2023-12-07 11:18:49 +01:00
2023-12-19 13:10:25 +01:00
if ( m_reportError ) {
wolv : : io : : File logFile ( m_logFilePath , wolv : : io : : File : : Mode : : Read ) ;
2023-12-07 11:18:49 +01:00
if ( logFile . isValid ( ) ) {
// Read current log file data
auto data = logFile . readString ( ) ;
// Anonymize the log file
{
2024-06-22 10:44:55 +02:00
for ( const auto & paths : paths : : All ) {
for ( auto & folder : paths - > all ( ) ) {
2023-12-07 11:18:49 +01:00
auto parent = wolv : : util : : toUTF8String ( folder . parent_path ( ) ) ;
data = wolv : : util : : replaceStrings ( data , parent , " <*****> " ) ;
}
2023-06-20 11:55:56 +02:00
}
}
2025-01-31 20:23:47 +01:00
TaskManager : : createBackgroundTask ( " hex.builtin.task.uploading_crash " , [ path = m_logFilePath , data ] ( auto & ) {
2023-12-07 11:18:49 +01:00
HttpRequest request ( " POST " , ImHexApiURL + std : : string ( " /crash_upload " ) ) ;
request . uploadFile ( std : : vector < u8 > ( data . begin ( ) , data . end ( ) ) , " file " , path . filename ( ) ) . wait ( ) ;
} ) ;
}
2023-06-20 11:55:56 +02:00
}
2024-02-03 12:16:36 +01:00
ContentRegistry : : Settings : : write < int > ( " hex.builtin.setting.general " , " hex.builtin.setting.general.upload_crash_logs " , m_reportError ) ;
2023-06-20 11:55:56 +02:00
2023-12-07 11:18:49 +01:00
this - > close ( ) ;
}
ImGui : : SameLine ( ) ;
ImGui : : SetCursorPosX ( width / 9 * 5 ) ;
2024-04-12 22:56:10 +02:00
if ( ImGui : : Button ( " hex.builtin.popup.safety_backup.delete " _lang , ImVec2 ( width / 3 , 0 ) ) | | ImGui : : IsKeyPressed ( ImGuiKey_Escape ) ) {
2023-12-19 13:10:25 +01:00
m_deleteCallback ( ) ;
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
this - > close ( ) ;
}
2023-04-08 00:58:53 +02:00
}
2023-12-07 11:18:49 +01:00
} ;
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
class PopupTipOfTheDay : public Popup < PopupTipOfTheDay > {
public :
PopupTipOfTheDay ( ) : Popup ( " hex.builtin.popup.tip_of_the_day.title " , true , false ) { }
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
void drawContent ( ) override {
ImGuiExt : : Header ( " hex.builtin.welcome.tip_of_the_day " _lang , true ) ;
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
ImGuiExt : : TextFormattedWrapped ( " {} " , s_tipOfTheDay . c_str ( ) ) ;
ImGui : : NewLine ( ) ;
static bool dontShowAgain = false ;
2023-12-23 21:09:41 +01:00
if ( ImGui : : Checkbox ( " hex.ui.common.dont_show_again " _lang , & dontShowAgain ) ) {
2024-02-03 12:16:36 +01:00
ContentRegistry : : Settings : : write < bool > ( " hex.builtin.setting.general " , " hex.builtin.setting.general.show_tips " , ! dontShowAgain ) ;
2023-12-07 11:18:49 +01:00
}
2023-12-23 21:09:41 +01:00
ImGui : : SameLine ( ( ImGui : : GetMainViewport ( ) - > Size / 3 - ImGui : : CalcTextSize ( " hex.ui.common.close " _lang ) - ImGui : : GetStyle ( ) . FramePadding ) . x ) ;
2023-04-08 00:58:53 +02:00
2024-04-12 22:56:10 +02:00
if ( ImGui : : Button ( " hex.ui.common.close " _lang ) | | ImGui : : IsKeyPressed ( ImGuiKey_Escape ) )
2023-12-07 11:18:49 +01:00
Popup : : close ( ) ;
2023-04-08 00:58:53 +02:00
}
2023-12-07 11:18:49 +01:00
} ;
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
void loadDefaultLayout ( ) {
2023-12-11 15:54:22 +01:00
LayoutManager : : loadFromString ( std : : string ( romfs : : get ( " layouts/default.hexlyt " ) . string ( ) ) ) ;
2023-12-07 11:18:49 +01:00
}
2023-04-08 00:58:53 +02:00
2023-12-07 11:18:49 +01:00
bool isAnyViewOpen ( ) {
const auto & views = ContentRegistry : : Views : : impl : : getEntries ( ) ;
return std : : any_of ( views . begin ( ) , views . end ( ) ,
[ ] ( const auto & entry ) {
return entry . second - > getWindowOpenState ( ) ;
} ) ;
2023-04-08 00:58:53 +02:00
}
2025-02-07 16:24:57 +01:00
void drawTiles ( ImDrawList * drawList , ImVec2 position , ImVec2 size , float lineDistance ) {
const auto tileCount = size / lineDistance ;
struct Segment {
i32 x , y ;
bool operator = = ( const Segment & ) const = default ;
} ;
static std : : list < Segment > segments ;
static std : : optional < Segment > colTile ;
static ImGuiDir direction ;
static auto rng = std : : mt19937 ( std : : random_device { } ( ) ) ;
static bool over = true ;
static i32 overCounter = 0 ;
static u32 spaceCount = 0 ;
if ( ImGui : : IsKeyPressed ( ImGuiKey_Space , false ) ) {
spaceCount + = 1 ;
if ( spaceCount > = 5 ) {
spaceCount = 0 ;
segments = { { 10 , 10 } , { 10 , 11 } , { 10 , 12 } } ;
direction = ImGuiDir_Right ;
colTile . reset ( ) ;
over = false ;
}
}
if ( over )
return ;
const auto drawTile = [ & ] ( u32 x , u32 y ) {
drawList - > AddRectFilled (
{
position . x + ( float ( x ) * lineDistance ) ,
position . y + ( float ( y ) * lineDistance )
} ,
{
position . x + ( float ( x + 1 ) * lineDistance ) - 1 _scaled ,
position . y + ( float ( y + 1 ) * lineDistance ) - 1 _scaled
} , ImGui : : GetColorU32 ( ImGuiCol_Text , 0.1F )
) ;
} ;
if ( colTile . has_value ( ) ) {
drawTile ( colTile - > x , colTile - > y ) ;
}
for ( const auto & [ x , y ] : segments ) {
drawTile ( x , y ) ;
}
if ( overCounter ! = 0 ) {
for ( u32 x = 0 ; x < u32 ( tileCount . x ) ; x + = 1 ) {
for ( u32 y = 0 ; y < u32 ( tileCount . y ) ; y + = 1 ) {
if ( ( x + y ) % 2 = = u32 ( overCounter % 2 ) )
drawTile ( x , y ) ;
}
}
}
static double lastTick = 0 ;
double tick = ImGui : : GetTime ( ) ;
if ( lastTick + 0.2 < tick ) {
Segment nextSegment = segments . front ( ) ;
switch ( direction ) {
case ImGuiDir_Up : nextSegment . y - = 1 ; break ;
case ImGuiDir_Down : nextSegment . y + = 1 ; break ;
case ImGuiDir_Left : nextSegment . x - = 1 ; break ;
case ImGuiDir_Right : nextSegment . x + = 1 ; break ;
default : break ;
}
if ( overCounter = = 0 ) {
for ( const auto & segment : segments ) {
if ( segment = = nextSegment ) overCounter = 5 ;
if ( segment . x < 0 | | segment . y < 0 ) overCounter = 5 ;
if ( segment . x > i32 ( tileCount . x ) | | segment . y > i32 ( tileCount . y ) ) overCounter = 5 ;
}
segments . push_front ( nextSegment ) ;
if ( colTile . has_value ( ) & & nextSegment ! = * colTile ) {
segments . pop_back ( ) ;
} else {
colTile = { i32 ( rng ( ) % u32 ( tileCount . x ) ) , i32 ( rng ( ) % u32 ( tileCount . x ) ) } ;
}
} else {
overCounter - = 1 ;
if ( overCounter < = 0 )
over = true ;
}
lastTick = tick ;
}
if ( ImGui : : IsKeyDown ( ImGuiKey_UpArrow ) & & direction ! = ImGuiDir_Down ) direction = ImGuiDir_Up ;
if ( ImGui : : IsKeyDown ( ImGuiKey_DownArrow ) & & direction ! = ImGuiDir_Up ) direction = ImGuiDir_Down ;
if ( ImGui : : IsKeyDown ( ImGuiKey_LeftArrow ) & & direction ! = ImGuiDir_Right ) direction = ImGuiDir_Left ;
if ( ImGui : : IsKeyDown ( ImGuiKey_RightArrow ) & & direction ! = ImGuiDir_Left ) direction = ImGuiDir_Right ;
}
2024-03-01 20:57:07 +01:00
void drawWelcomeScreenBackground ( ) {
const auto position = ImGui : : GetWindowPos ( ) ;
const auto size = ImGui : : GetWindowSize ( ) ;
auto drawList = ImGui : : GetWindowDrawList ( ) ;
const auto lineDistance = 20 _scaled ;
const auto lineColor = ImGui : : GetColorU32 ( ImGuiCol_Text , 0.03F ) ;
for ( auto x = position . x ; x < position . x + size . x + lineDistance ; x + = lineDistance ) {
drawList - > AddLine ( { x , position . y } , { x , position . y + size . y } , lineColor ) ;
}
for ( auto y = position . y ; y < position . y + size . y + lineDistance ; y + = lineDistance ) {
drawList - > AddLine ( { position . x , y } , { position . x + size . x , y } , lineColor ) ;
}
2025-02-07 16:24:57 +01:00
drawTiles ( drawList , position , size , lineDistance ) ;
2024-03-01 20:57:07 +01:00
}
2023-12-07 11:18:49 +01:00
void drawWelcomeScreenContentSimplified ( ) {
const ImVec2 backdropSize = scaled ( { 350 , 350 } ) ;
ImGui : : SetCursorPos ( ( ImGui : : GetContentRegionAvail ( ) - backdropSize ) / 2 ) ;
2025-01-27 22:10:30 +01:00
ImGui : : Image ( * s_backdropTexture , backdropSize ) ;
2022-02-01 18:09:40 +01:00
2023-12-07 11:53:31 +01:00
ImGuiExt : : TextFormattedCentered ( " hex.builtin.welcome.drop_file " _lang ) ;
2023-12-07 11:18:49 +01:00
}
2022-07-06 10:21:02 +02:00
2023-12-07 11:18:49 +01:00
void drawWelcomeScreenContentFull ( ) {
2023-12-18 11:03:19 +01:00
const ImVec2 margin = scaled ( { 30 , 20 } ) ;
2022-02-01 18:09:40 +01:00
2023-12-18 11:03:19 +01:00
ImGui : : SetCursorPos ( margin ) ;
if ( ImGui : : BeginTable ( " Welcome Outer " , 1 , ImGuiTableFlags_None , ImGui : : GetContentRegionAvail ( ) - margin ) ) {
ImGui : : TableNextRow ( ) ;
2023-12-07 11:18:49 +01:00
ImGui : : TableNextColumn ( ) ;
2022-02-01 18:09:40 +01:00
2025-01-15 17:54:35 +01:00
ImGui : : PushStyleColor ( ImGuiCol_ChildBg , ImGui : : GetStyleColorVec4 ( ImGuiCol_PopupBg ) ) ;
ON_SCOPE_EXIT { ImGui : : PopStyleColor ( ) ; } ;
2024-06-05 22:23:43 +02:00
2025-01-15 17:54:35 +01:00
const auto availableSpace = ImGui : : GetContentRegionAvail ( ) ;
if ( ImGui : : BeginTable ( " Welcome Left " , 1 , ImGuiTableFlags_NoBordersInBody , ImVec2 ( availableSpace . x / 2 , 0 ) ) ) {
ImGui : : TableNextRow ( ) ;
ImGui : : TableNextColumn ( ) ;
2025-01-27 22:10:30 +01:00
ImGui : : Image ( * s_bannerTexture , s_bannerTexture - > getSize ( ) ) ;
2024-06-26 20:37:39 +02:00
2025-01-15 17:54:35 +01:00
if ( ImHexApi : : System : : isNightlyBuild ( ) ) {
auto cursor = ImGui : : GetCursorPos ( ) ;
2024-06-05 22:23:43 +02:00
2025-01-15 17:54:35 +01:00
ImGui : : SameLine ( 0 ) ;
ImGui : : SetCursorPosX ( ImGui : : GetCursorPosX ( ) - 15 _scaled ) ;
ImGui : : SetCursorPosY ( ImGui : : GetCursorPosY ( ) + 5 _scaled ) ;
2024-06-05 22:23:43 +02:00
2025-01-27 22:10:30 +01:00
ImGui : : Image ( * s_nightlyTexture , s_nightlyTexture - > getSize ( ) ) ;
2025-01-15 17:54:35 +01:00
ImGuiExt : : InfoTooltip ( hex : : format ( " {0} \n \n Commit: {1}@{2} " , " hex.builtin.welcome.nightly_build " _lang , ImHexApi : : System : : getCommitBranch ( ) , ImHexApi : : System : : getCommitHash ( true ) ) . c_str ( ) ) ;
2023-12-18 11:03:19 +01:00
2025-01-15 17:54:35 +01:00
ImGui : : SetCursorPos ( cursor ) ;
}
ImGui : : NewLine ( ) ;
2023-12-18 11:03:19 +01:00
ImGui : : TableNextRow ( ImGuiTableRowFlags_None , ImGui : : GetTextLineHeightWithSpacing ( ) * 6 ) ;
ImGui : : TableNextColumn ( ) ;
2022-02-01 18:09:40 +01:00
2023-12-18 11:03:19 +01:00
static bool otherProvidersVisible = false ;
ImGui : : SetCursorPosY ( ImGui : : GetCursorPosY ( ) + 5 _scaled ) ;
2023-11-16 13:23:28 +01:00
2023-12-07 11:18:49 +01:00
{
2023-12-18 11:03:19 +01:00
auto startPos = ImGui : : GetCursorPos ( ) ;
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.start " _lang , nullptr , ImVec2 ( ) , ImGuiChildFlags_AutoResizeX ) ) {
2023-12-18 11:03:19 +01:00
if ( ImGuiExt : : IconHyperlink ( ICON_VS_NEW_FILE , " hex.builtin.welcome.start.create_file " _lang ) ) {
auto newProvider = hex : : ImHexApi : : Provider : : createProvider ( " hex.builtin.provider.mem_file " , true ) ;
if ( newProvider ! = nullptr & & ! newProvider - > open ( ) )
hex : : ImHexApi : : Provider : : remove ( newProvider ) ;
else
EventProviderOpened : : post ( newProvider ) ;
2023-12-07 11:18:49 +01:00
}
2023-12-18 11:03:19 +01:00
if ( ImGuiExt : : IconHyperlink ( ICON_VS_GO_TO_FILE , " hex.builtin.welcome.start.open_file " _lang ) )
RequestOpenWindow : : post ( " Open File " ) ;
if ( ImGuiExt : : IconHyperlink ( ICON_VS_NOTEBOOK , " hex.builtin.welcome.start.open_project " _lang ) )
RequestOpenWindow : : post ( " Open Project " ) ;
if ( ImGuiExt : : IconHyperlink ( ICON_VS_TELESCOPE , " hex.builtin.welcome.start.open_other " _lang ) )
otherProvidersVisible = ! otherProvidersVisible ;
2023-12-07 11:18:49 +01:00
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-12-18 11:03:19 +01:00
auto endPos = ImGui : : GetCursorPos ( ) ;
if ( otherProvidersVisible ) {
ImGui : : SameLine ( 0 , 2 _scaled ) ;
ImGui : : SetCursorPos ( ImGui : : GetCursorPos ( ) + ImVec2 ( 0 , ( endPos - startPos ) . y / 2 ) ) ;
ImGui : : TextUnformatted ( ICON_VS_ARROW_RIGHT ) ;
ImGui : : SameLine ( 0 , 2 _scaled ) ;
2025-02-02 21:15:56 +01:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.start.open_other " _lang , nullptr , ImVec2 ( 200 _scaled , ImGui : : GetTextLineHeightWithSpacing ( ) * 5.8 ) , ImGuiChildFlags_AutoResizeX ) ) {
2024-06-25 13:54:29 +02:00
for ( const auto & unlocalizedProviderName : ContentRegistry : : Provider : : impl : : getEntries ( ) ) {
if ( ImGuiExt : : Hyperlink ( Lang ( unlocalizedProviderName ) ) ) {
ImHexApi : : Provider : : createProvider ( unlocalizedProviderName ) ;
otherProvidersVisible = false ;
}
2023-12-07 11:18:49 +01:00
}
2024-06-25 13:54:29 +02:00
2022-02-01 18:09:40 +01:00
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-12-18 11:03:19 +01:00
}
}
2022-02-01 18:09:40 +01:00
2023-12-18 11:03:19 +01:00
// Draw recent entries
ImGui : : Dummy ( { } ) ;
2025-01-01 17:08:31 +01:00
# if !defined(OS_WEB)
recent : : draw ( ) ;
# endif
2023-12-18 11:03:19 +01:00
ImGui : : TableNextRow ( ImGuiTableRowFlags_None , ImGui : : GetTextLineHeightWithSpacing ( ) * 6 ) ;
ImGui : : TableNextColumn ( ) ;
2023-11-16 09:32:24 +01:00
2023-12-18 11:03:19 +01:00
ImGui : : SetCursorPosY ( ImGui : : GetCursorPosY ( ) + 5 _scaled ) ;
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.help " _lang , nullptr , ImVec2 ( ) , ImGuiChildFlags_AutoResizeX ) ) {
2023-12-18 11:03:19 +01:00
if ( ImGuiExt : : IconHyperlink ( ICON_VS_GITHUB , " hex.builtin.welcome.help.repo " _lang ) ) hex : : openWebpage ( " hex.builtin.welcome.help.repo.link " _lang ) ;
if ( ImGuiExt : : IconHyperlink ( ICON_VS_ORGANIZATION , " hex.builtin.welcome.help.gethelp " _lang ) ) hex : : openWebpage ( " hex.builtin.welcome.help.gethelp.link " _lang ) ;
if ( ImGuiExt : : IconHyperlink ( ICON_VS_COMMENT_DISCUSSION , " hex.builtin.welcome.help.discord " _lang ) ) hex : : openWebpage ( " hex.builtin.welcome.help.discord.link " _lang ) ;
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-11-16 09:32:24 +01:00
2023-12-28 20:26:58 +01:00
if ( ImHexApi : : System : : getInitArguments ( ) . contains ( " update-available " ) ) {
ImGui : : TableNextRow ( ) ;
ImGui : : TableNextColumn ( ) ;
2024-02-10 23:31:05 +01:00
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.update.title " _lang , hex : : format ( " hex.builtin.welcome.update.desc " _lang , ImHexApi : : System : : getInitArgument ( " update-available " ) ) . c_str ( ) , ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x * 0.8F , 0 ) ) )
2023-12-28 20:26:58 +01:00
ImHexApi : : System : : updateImHex ( ImHexApi : : System : : UpdateType : : Stable ) ;
}
2023-12-18 11:03:19 +01:00
ImGui : : EndTable ( ) ;
2023-11-16 13:23:28 +01:00
}
2023-12-18 11:03:19 +01:00
ImGui : : SameLine ( ) ;
if ( ImGui : : BeginTable ( " Welcome Right " , 1 , ImGuiTableFlags_NoBordersInBody , ImVec2 ( availableSpace . x / 2 , 0 ) ) ) {
ImGui : : TableNextRow ( ImGuiTableRowFlags_None , ImGui : : GetTextLineHeightWithSpacing ( ) * 5 ) ;
ImGui : : TableNextColumn ( ) ;
2023-11-16 22:24:06 +01:00
2025-01-15 17:54:35 +01:00
ImGui : : NewLine ( ) ;
ImGui : : NewLine ( ) ;
ImGui : : NewLine ( ) ;
ImGui : : NewLine ( ) ;
ImGui : : NewLine ( ) ;
2023-12-18 11:03:19 +01:00
auto windowPadding = ImGui : : GetStyle ( ) . WindowPadding . x * 3 ;
2022-02-01 18:09:40 +01:00
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.customize " _lang , nullptr , ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x - windowPadding , 0 ) , ImGuiChildFlags_AutoResizeX ) ) {
2023-12-18 11:03:19 +01:00
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.customize.settings.title " _lang , " hex.builtin.welcome.customize.settings.desc " _lang , ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x , 0 ) ) )
RequestOpenWindow : : post ( " Settings " ) ;
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-12-07 11:18:49 +01:00
ImGui : : TableNextRow ( ImGuiTableRowFlags_None , ImGui : : GetTextLineHeightWithSpacing ( ) * 5 ) ;
ImGui : : TableNextColumn ( ) ;
2023-11-17 15:54:38 +01:00
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.learn " _lang , nullptr , ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x - windowPadding , 0 ) , ImGuiChildFlags_AutoResizeX ) ) {
2023-12-18 11:03:19 +01:00
const auto size = ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x , 0 ) ;
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.learn.latest.title " _lang , " hex.builtin.welcome.learn.latest.desc " _lang , size ) )
hex : : openWebpage ( " hex.builtin.welcome.learn.latest.link " _lang ) ;
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.learn.imhex.title " _lang , " hex.builtin.welcome.learn.imhex.desc " _lang , size ) ) {
AchievementManager : : unlockAchievement ( " hex.builtin.achievement.starting_out " , " hex.builtin.achievement.starting_out.docs.name " ) ;
hex : : openWebpage ( " hex.builtin.welcome.learn.imhex.link " _lang ) ;
}
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.learn.pattern.title " _lang , " hex.builtin.welcome.learn.pattern.desc " _lang , size ) )
hex : : openWebpage ( " hex.builtin.welcome.learn.pattern.link " _lang ) ;
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.learn.plugins.title " _lang , " hex.builtin.welcome.learn.plugins.desc " _lang , size ) )
hex : : openWebpage ( " hex.builtin.welcome.learn.plugins.link " _lang ) ;
2024-02-24 16:10:05 +01:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Horizontal , 3 _scaled ) ;
if ( ImGuiExt : : DescriptionButton ( " hex.builtin.welcome.learn.interactive_tutorial.title " _lang , " hex.builtin.welcome.learn.interactive_tutorial.desc " _lang , size ) ) {
RequestOpenWindow : : post ( " Tutorials " ) ;
}
2023-12-18 11:03:19 +01:00
if ( auto [ unlocked , total ] = AchievementManager : : getProgress ( ) ; unlocked ! = total ) {
if ( ImGuiExt : : DescriptionButtonProgress ( " hex.builtin.welcome.learn.achievements.title " _lang , " hex.builtin.welcome.learn.achievements.desc " _lang , float ( unlocked ) / float ( total ) , size ) ) {
RequestOpenWindow : : post ( " Achievements " ) ;
}
}
2023-12-07 11:18:49 +01:00
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-11-17 15:54:38 +01:00
2023-12-18 11:03:19 +01:00
auto extraWelcomeScreenEntries = ContentRegistry : : Interface : : impl : : getWelcomeScreenEntries ( ) ;
if ( ! extraWelcomeScreenEntries . empty ( ) ) {
ImGui : : TableNextRow ( ImGuiTableRowFlags_None , ImGui : : GetTextLineHeightWithSpacing ( ) * 5 ) ;
ImGui : : TableNextColumn ( ) ;
2022-02-01 18:09:40 +01:00
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.various " _lang , nullptr , ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x - windowPadding , 0 ) ) ) {
2023-12-18 11:03:19 +01:00
for ( const auto & callback : extraWelcomeScreenEntries )
callback ( ) ;
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-12-18 11:03:19 +01:00
}
2023-12-08 14:46:32 +01:00
2025-01-27 22:10:30 +01:00
if ( s_infoBannerTexture - > isValid ( ) ) {
2023-12-18 11:03:19 +01:00
static bool hovered = false ;
ImGui : : PushStyleColor ( ImGuiCol_Border , ImGui : : GetStyleColorVec4 ( hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Border ) ) ;
ImGui : : PushStyleVar ( ImGuiStyleVar_WindowPadding , ImVec2 ( 0 , 0 ) ) ;
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.info " _lang , nullptr , ImVec2 ( ) , ImGuiChildFlags_AutoResizeX ) ) {
2023-12-18 11:03:19 +01:00
const auto height = 80 _scaled ;
2025-01-27 22:10:30 +01:00
ImGui : : Image ( * s_infoBannerTexture , ImVec2 ( height * s_infoBannerTexture - > getAspectRatio ( ) , height ) ) ;
2023-12-18 11:03:19 +01:00
hovered = ImGui : : IsItemHovered ( ) ;
if ( ImGui : : IsItemClicked ( ) ) {
hex : : openWebpage ( ImHexApiURL + hex : : format ( " /info/{}/link " , hex : : toLower ( ImHexApi : : System : : getOSName ( ) ) ) ) ;
}
2023-12-08 14:46:32 +01:00
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-12-18 11:03:19 +01:00
ImGui : : PopStyleVar ( ) ;
ImGui : : PopStyleColor ( ) ;
2023-12-07 11:18:49 +01:00
}
2023-12-09 15:56:26 +01:00
2023-12-07 11:18:49 +01:00
2023-12-18 11:03:19 +01:00
ImGui : : EndTable ( ) ;
}
2023-12-07 11:18:49 +01:00
ImGui : : EndTable ( ) ;
}
2022-08-10 10:28:40 +02:00
2023-12-18 11:30:09 +01:00
ImGui : : SetCursorPos ( ImVec2 ( ImGui : : GetContentRegionAvail ( ) . x - ImGui : : GetStyle ( ) . FramePadding . x * 2 , ImGui : : GetStyle ( ) . FramePadding . y * 2 - 1 ) ) ;
2023-12-07 11:18:49 +01:00
if ( ImGuiExt : : DimmedIconButton ( ICON_VS_CLOSE , ImGuiExt : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarRed ) ) ) {
auto provider = ImHexApi : : Provider : : createProvider ( " hex.builtin.provider.null " ) ;
if ( provider ! = nullptr )
2024-12-02 21:16:20 +01:00
std : : ignore = provider - > open ( ) ;
2023-12-07 11:18:49 +01:00
}
2022-08-10 10:28:40 +02:00
}
2022-02-01 18:09:40 +01:00
2023-12-07 11:18:49 +01:00
void drawWelcomeScreen ( ) {
2024-05-17 22:05:32 +02:00
ImGui : : PushStyleVar ( ImGuiStyleVar_FrameBorderSize , 0 ) ;
2023-12-07 11:18:49 +01:00
ImGui : : PushStyleColor ( ImGuiCol_WindowShadow , 0x00 ) ;
2024-07-03 22:32:48 +02:00
if ( ImGui : : Begin ( " ImHexDockSpace " , nullptr , ImGuiWindowFlags_NoBringToFrontOnFocus ) ) {
2023-12-07 11:18:49 +01:00
if ( ! ImHexApi : : Provider : : isValid ( ) ) {
2024-06-25 13:54:29 +02:00
static auto title = [ ] {
2025-02-10 09:42:35 +01:00
std : : array < char , 256 > result = { } ;
ImFormatString ( result . data ( ) , result . size ( ) , " %s/DockSpace_%08X " , ImGui : : GetCurrentWindowRead ( ) - > Name , ImGui : : GetID ( " ImHexMainDock " ) ) ;
return result ;
2024-06-25 13:54:29 +02:00
} ( ) ;
2023-12-07 11:18:49 +01:00
if ( ImGui : : Begin ( title . data ( ) , nullptr , ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus ) ) {
ImGui : : Dummy ( { } ) ;
ImGui : : PushStyleVar ( ImGuiStyleVar_WindowPadding , scaled ( { 10 , 10 } ) ) ;
ImGui : : SetNextWindowScroll ( { 0.0F , - 1.0F } ) ;
ImGui : : SetNextWindowSize ( ImGui : : GetContentRegionAvail ( ) + scaled ( { 0 , 10 } ) ) ;
ImGui : : SetNextWindowPos ( ImGui : : GetCursorScreenPos ( ) - ImVec2 ( 0 , ImGui : : GetStyle ( ) . FramePadding . y + 2 _scaled ) ) ;
2023-12-24 14:35:44 +01:00
ImGui : : SetNextWindowViewport ( ImGui : : GetMainViewport ( ) - > ID ) ;
2024-07-03 22:32:48 +02:00
if ( ImGui : : Begin ( " Welcome Screen " , nullptr , ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove ) ) {
2023-12-18 10:14:07 +01:00
ImGui : : BringWindowToDisplayBack ( ImGui : : GetCurrentWindowRead ( ) ) ;
2024-03-01 20:57:07 +01:00
drawWelcomeScreenBackground ( ) ;
2023-12-07 11:18:49 +01:00
if ( s_simplifiedWelcomeScreen )
drawWelcomeScreenContentSimplified ( ) ;
else
drawWelcomeScreenContentFull ( ) ;
static bool hovered = false ;
ImGui : : PushStyleVar ( ImGuiStyleVar_Alpha , hovered ? 1.0F : 0.3F ) ;
{
2025-01-13 22:17:14 +01:00
const ImVec2 windowSize = { 150 _scaled , ImGui : : GetTextLineHeightWithSpacing ( ) * 3.5F } ;
2023-12-18 14:57:37 +01:00
ImGui : : SetCursorScreenPos ( ImGui : : GetWindowPos ( ) + ImGui : : GetWindowSize ( ) - windowSize - ImGui : : GetStyle ( ) . WindowPadding ) ;
2023-12-09 15:56:26 +01:00
ImGui : : PushStyleColor ( ImGuiCol_ChildBg , ImGui : : GetStyleColorVec4 ( ImGuiCol_WindowBg ) ) ;
2024-06-25 13:54:29 +02:00
if ( ImGuiExt : : BeginSubWindow ( " hex.builtin.welcome.header.quick_settings " _lang , nullptr , windowSize , ImGuiChildFlags_AutoResizeY ) ) {
2023-12-11 15:54:22 +01:00
if ( ImGuiExt : : ToggleSwitch ( " hex.builtin.welcome.quick_settings.simplified " _lang , & s_simplifiedWelcomeScreen ) ) {
2024-02-03 12:16:36 +01:00
ContentRegistry : : Settings : : write < bool > ( " hex.builtin.setting.interface " , " hex.builtin.setting.interface.simplified_welcome_screen " , s_simplifiedWelcomeScreen ) ;
2023-12-11 15:54:22 +01:00
WorkspaceManager : : switchWorkspace ( s_simplifiedWelcomeScreen ? " Minimal " : " Default " ) ;
}
2024-06-25 13:54:29 +02:00
2023-12-07 11:18:49 +01:00
}
2024-06-26 19:11:31 +02:00
ImGuiExt : : EndSubWindow ( ) ;
2023-12-09 15:56:26 +01:00
ImGui : : PopStyleColor ( ) ;
2023-12-07 11:18:49 +01:00
hovered = ImGui : : IsItemHovered ( ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem ) ;
}
ImGui : : PopStyleVar ( ) ;
}
ImGui : : End ( ) ;
ImGui : : PopStyleVar ( ) ;
}
ImGui : : End ( ) ;
2023-12-18 10:14:07 +01:00
ImGui : : BringWindowToDisplayBack ( ImGui : : GetCurrentWindowRead ( ) ) ;
2023-12-07 11:18:49 +01:00
}
}
ImGui : : End ( ) ;
ImGui : : PopStyleColor ( ) ;
2024-05-17 22:05:32 +02:00
ImGui : : PopStyleVar ( ) ;
2023-12-07 11:18:49 +01:00
}
/**
* @ brief Draw some default background if there are no views available in the current layout
*/
void drawNoViewsBackground ( ) {
2024-05-17 22:05:32 +02:00
ImGui : : PushStyleVar ( ImGuiStyleVar_FrameBorderSize , 0 ) ;
2023-12-24 14:52:14 +01:00
ImGui : : PushStyleColor ( ImGuiCol_WindowShadow , 0x00 ) ;
2024-07-03 22:32:48 +02:00
if ( ImGui : : Begin ( " ImHexDockSpace " , nullptr , ImGuiWindowFlags_NoBringToFrontOnFocus ) ) {
2023-05-22 10:37:30 +02:00
static std : : array < char , 256 > title ;
2023-12-18 10:14:07 +01:00
ImFormatString ( title . data ( ) , title . size ( ) , " %s/DockSpace_%08X " , ImGui : : GetCurrentWindowRead ( ) - > Name , ImGui : : GetID ( " ImHexMainDock " ) ) ;
2023-12-07 11:18:49 +01:00
if ( ImGui : : Begin ( title . data ( ) ) ) {
2023-11-02 08:54:02 +01:00
ImGui : : Dummy ( { } ) ;
2023-11-05 21:17:37 +01:00
ImGui : : PushStyleVar ( ImGuiStyleVar_WindowPadding , scaled ( { 10 , 10 } ) ) ;
2023-05-22 10:37:30 +02:00
2023-11-05 21:17:37 +01:00
ImGui : : SetNextWindowScroll ( { 0.0F , - 1.0F } ) ;
2023-11-02 08:54:02 +01:00
ImGui : : SetNextWindowSize ( ImGui : : GetContentRegionAvail ( ) + scaled ( { 0 , 10 } ) ) ;
2023-11-05 21:17:37 +01:00
ImGui : : SetNextWindowPos ( ImGui : : GetCursorScreenPos ( ) - ImVec2 ( 0 , ImGui : : GetStyle ( ) . FramePadding . y + 2 _scaled ) ) ;
2023-12-24 14:35:44 +01:00
ImGui : : SetNextWindowViewport ( ImGui : : GetMainViewport ( ) - > ID ) ;
2024-07-03 22:32:48 +02:00
if ( ImGui : : Begin ( " Welcome Screen " , nullptr , ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove ) ) {
2023-12-07 11:18:49 +01:00
auto imageSize = scaled ( ImVec2 ( 350 , 350 ) ) ;
auto imagePos = ( ImGui : : GetContentRegionAvail ( ) - imageSize ) / 2 ;
2022-07-06 10:21:02 +02:00
2023-12-07 11:18:49 +01:00
ImGui : : SetCursorPos ( imagePos ) ;
2025-01-27 22:10:30 +01:00
ImGui : : Image ( * s_backdropTexture , imageSize ) ;
2023-05-05 22:00:17 +02:00
2023-12-07 11:18:49 +01:00
auto loadDefaultText = " hex.builtin.layouts.none.restore_default " _lang ;
auto textSize = ImGui : : CalcTextSize ( loadDefaultText ) ;
2023-05-05 22:00:17 +02:00
2023-12-07 11:18:49 +01:00
auto textPos = ImVec2 (
( ImGui : : GetContentRegionAvail ( ) . x - textSize . x ) / 2 ,
imagePos . y + imageSize . y
) ;
2023-05-05 22:00:17 +02:00
2023-12-07 11:18:49 +01:00
ImGui : : SetCursorPos ( textPos ) ;
if ( ImGuiExt : : DimmedButton ( loadDefaultText ) ) {
loadDefaultLayout ( ) ;
}
2023-05-05 22:00:17 +02:00
}
2023-11-07 16:40:37 +01:00
2023-12-07 11:18:49 +01:00
ImGui : : End ( ) ;
ImGui : : PopStyleVar ( ) ;
}
2023-11-02 08:54:02 +01:00
ImGui : : End ( ) ;
2022-07-06 10:21:02 +02:00
}
ImGui : : End ( ) ;
2023-12-24 14:52:14 +01:00
ImGui : : PopStyleColor ( ) ;
2024-05-17 22:05:32 +02:00
ImGui : : PopStyleVar ( ) ;
2022-07-06 10:21:02 +02:00
}
}
2023-05-05 22:02:18 +02:00
/**
* @ brief Registers the event handlers related to the welcome screen
* should only be called once , at startup
*/
2022-02-01 18:09:40 +01:00
void createWelcomeScreen ( ) {
2023-05-27 16:59:30 +02:00
recent : : registerEventHandlers ( ) ;
recent : : updateRecentEntries ( ) ;
2022-08-14 10:07:45 +02:00
2024-02-18 11:29:18 +01:00
EventFrameBegin : : subscribe ( drawWelcomeScreen ) ;
2023-05-05 22:02:18 +02:00
// Sets a background when they are no views
2024-02-18 11:29:18 +01:00
EventFrameBegin : : subscribe ( [ ] {
2022-09-05 14:16:31 +02:00
if ( ImHexApi : : Provider : : isValid ( ) & & ! isAnyViewOpen ( ) )
drawNoViewsBackground ( ) ;
} ) ;
2022-02-01 18:09:40 +01:00
2024-02-18 11:29:18 +01:00
ContentRegistry : : Settings : : onChange ( " hex.builtin.setting.interface " , " hex.builtin.setting.interface.color " , [ ] ( const ContentRegistry : : Settings : : SettingsValue & value ) {
auto theme = value . get < std : : string > ( " Dark " ) ;
if ( theme ! = ThemeManager : : NativeTheme ) {
static std : : string lastTheme ;
2022-02-01 18:09:40 +01:00
2024-02-18 11:29:18 +01:00
if ( theme ! = lastTheme ) {
RequestChangeTheme : : post ( theme ) ;
lastTheme = theme ;
2022-02-19 00:35:07 +01:00
}
2022-02-01 18:09:40 +01:00
}
} ) ;
2024-02-18 11:29:18 +01:00
ContentRegistry : : Settings : : onChange ( " hex.builtin.setting.interface " , " hex.builtin.setting.interface.simplified_welcome_screen " , [ ] ( const ContentRegistry : : Settings : : SettingsValue & value ) {
s_simplifiedWelcomeScreen = value . get < bool > ( false ) ;
} ) ;
ContentRegistry : : Settings : : onChange ( " hex.builtin.setting.interface " , " hex.builtin.setting.interface.language " , [ ] ( const ContentRegistry : : Settings : : SettingsValue & value ) {
auto language = value . get < std : : string > ( " en-US " ) ;
if ( language ! = LocalizationManager : : getSelectedLanguage ( ) )
LocalizationManager : : loadLanguage ( language ) ;
} ) ;
ContentRegistry : : Settings : : onChange ( " hex.builtin.setting.interface " , " hex.builtin.setting.interface.fps " , [ ] ( const ContentRegistry : : Settings : : SettingsValue & value ) {
ImHexApi : : System : : setTargetFPS ( static_cast < float > ( value . get < int > ( 14 ) ) ) ;
} ) ;
2022-02-01 18:09:40 +01:00
2024-02-18 11:29:18 +01:00
RequestChangeTheme : : subscribe ( [ ] ( const std : : string & theme ) {
2022-09-18 20:38:45 +02:00
auto changeTexture = [ & ] ( const std : : string & path ) {
2024-05-17 22:18:44 +02:00
return ImGuiExt : : Texture : : fromImage ( romfs : : get ( path ) . span ( ) , ImGuiExt : : Texture : : Filter : : Nearest ) ;
2024-05-01 20:36:10 +02:00
} ;
2024-06-05 22:23:43 +02:00
auto changeTextureSvg = [ & ] ( const std : : string & path , float width ) {
return ImGuiExt : : Texture : : fromSVG ( romfs : : get ( path ) . span ( ) , width , 0 , ImGuiExt : : Texture : : Filter : : Linear ) ;
2022-02-19 00:35:07 +01:00
} ;
2022-02-01 18:09:40 +01:00
2023-03-23 20:35:16 +01:00
ThemeManager : : changeTheme ( theme ) ;
2025-01-15 17:54:35 +01:00
s_bannerTexture = changeTextureSvg ( hex : : format ( " assets/{}/banner.svg " , ThemeManager : : getImageTheme ( ) ) , 300 _scaled ) ;
2024-06-05 22:23:43 +02:00
s_nightlyTexture = changeTextureSvg ( hex : : format ( " assets/{}/nightly.svg " , " common " ) , 35 _scaled ) ;
2023-07-04 08:42:33 +02:00
s_backdropTexture = changeTexture ( hex : : format ( " assets/{}/backdrop.png " , ThemeManager : : getImageTheme ( ) ) ) ;
2022-02-01 18:09:40 +01:00
2025-01-27 22:10:30 +01:00
if ( ! s_bannerTexture - > isValid ( ) ) {
2022-02-01 18:09:40 +01:00
log : : error ( " Failed to load banner texture! " ) ;
}
} ) ;
2023-04-08 00:58:53 +02:00
// Clear project context if we go back to the welcome screen
2023-12-08 10:29:44 +01:00
EventProviderChanged : : subscribe ( [ ] ( const hex : : prv : : Provider * oldProvider , const hex : : prv : : Provider * newProvider ) {
2024-12-14 21:35:54 +01:00
std : : ignore = oldProvider ;
2023-01-07 17:31:22 +01:00
if ( newProvider = = nullptr ) {
ProjectFile : : clearPath ( ) ;
2023-12-08 10:29:44 +01:00
RequestUpdateWindowTitle : : post ( ) ;
2023-01-07 17:31:22 +01:00
}
} ) ;
2022-08-14 10:07:45 +02:00
2023-11-17 15:54:38 +01:00
recent : : addMenuItems ( ) ;
2022-02-01 18:09:40 +01:00
2023-04-08 00:58:53 +02:00
// Check for crash backup
2023-05-22 13:24:48 +02:00
constexpr static auto CrashFileName = " crash.json " ;
constexpr static auto BackupFileName = " crash_backup.hexproj " ;
bool hasCrashed = false ;
2024-06-22 10:44:55 +02:00
for ( const auto & path : paths : : Config . read ( ) ) {
2023-05-22 13:24:48 +02:00
if ( auto crashFilePath = std : : fs : : path ( path ) / CrashFileName ; wolv : : io : : fs : : exists ( crashFilePath ) ) {
hasCrashed = true ;
log : : info ( " Found crash.json file at {} " , wolv : : util : : toUTF8String ( crashFilePath ) ) ;
wolv : : io : : File crashFile ( crashFilePath , wolv : : io : : File : : Mode : : Read ) ;
2023-05-28 11:35:51 +02:00
nlohmann : : json crashFileData ;
try {
crashFileData = nlohmann : : json : : parse ( crashFile . readString ( ) ) ;
} catch ( nlohmann : : json : : exception & e ) {
log : : error ( " Failed to parse crash.json file: {} " , e . what ( ) ) ;
crashFile . remove ( ) ;
continue ;
}
2023-05-22 13:24:48 +02:00
bool hasProject = ! crashFileData . value ( " project " , " " ) . empty ( ) ;
auto backupFilePath = path / BackupFileName ;
2024-05-19 23:13:43 +02:00
auto backupFilePathOld = path / BackupFileName ;
backupFilePathOld . replace_extension ( " .hexproj.old " ) ;
2023-05-22 13:24:48 +02:00
bool hasBackupFile = wolv : : io : : fs : : exists ( backupFilePath ) ;
2023-06-20 11:55:56 +02:00
2024-02-24 00:37:17 +01:00
if ( ! hasProject & & ! hasBackupFile ) {
log : : warn ( " No project file or backup file found in crash.json file " ) ;
crashFile . close ( ) ;
2024-05-19 23:13:43 +02:00
// Delete crash.json file
2024-02-24 00:37:17 +01:00
wolv : : io : : fs : : remove ( crashFilePath ) ;
2024-05-19 23:13:43 +02:00
// Delete old backup file
wolv : : io : : fs : : remove ( backupFilePathOld ) ;
// Try to move current backup file to the old backup location
if ( wolv : : io : : fs : : copyFile ( backupFilePath , backupFilePathOld ) ) {
wolv : : io : : fs : : remove ( backupFilePath ) ;
}
2024-02-24 00:37:17 +01:00
continue ;
}
2023-05-22 13:24:48 +02:00
PopupRestoreBackup : : open (
2023-10-04 12:00:32 +02:00
// Path of log file
2023-05-22 13:24:48 +02:00
crashFileData . value ( " logFile " , " " ) ,
2023-10-04 12:00:32 +02:00
// Restore callback
2023-12-27 16:33:49 +01:00
[ crashFileData , backupFilePath , hasProject , hasBackupFile ] {
2023-05-22 13:24:48 +02:00
if ( hasBackupFile ) {
2024-02-26 21:48:56 +01:00
if ( ProjectFile : : load ( backupFilePath ) ) {
if ( hasProject ) {
ProjectFile : : setPath ( crashFileData [ " project " ] . get < std : : string > ( ) ) ;
} else {
ProjectFile : : setPath ( " " ) ;
}
RequestUpdateWindowTitle : : post ( ) ;
2023-05-22 13:24:48 +02:00
} else {
2024-02-26 21:48:56 +01:00
ui : : ToastError : : open ( hex : : format ( " hex.builtin.popup.error.project.load " _lang , wolv : : util : : toUTF8String ( backupFilePath ) ) ) ;
2023-05-22 13:24:48 +02:00
}
2024-02-26 21:48:56 +01:00
} else {
2023-05-22 13:24:48 +02:00
if ( hasProject ) {
ProjectFile : : setPath ( crashFileData [ " project " ] . get < std : : string > ( ) ) ;
}
}
} ,
2023-10-04 12:00:32 +02:00
// Delete callback (also executed after restore)
[ crashFilePath , backupFilePath ] {
2023-05-22 13:24:48 +02:00
wolv : : io : : fs : : remove ( crashFilePath ) ;
wolv : : io : : fs : : remove ( backupFilePath ) ;
}
) ;
2022-02-01 18:09:40 +01:00
}
}
2023-04-08 00:58:53 +02:00
// Tip of the day
2022-10-27 13:21:54 +02:00
auto tipsData = romfs : : get ( " tips.json " ) ;
2023-05-22 13:24:48 +02:00
if ( ! hasCrashed & & tipsData . valid ( ) ) {
2022-10-27 13:21:54 +02:00
auto tipsCategories = nlohmann : : json : : parse ( tipsData . string ( ) ) ;
auto now = std : : chrono : : system_clock : : now ( ) ;
2023-12-27 16:33:49 +01:00
auto daysSinceEpoch = std : : chrono : : duration_cast < std : : chrono : : days > ( now . time_since_epoch ( ) ) ;
std : : mt19937 random ( daysSinceEpoch . count ( ) ) ;
2022-10-27 13:21:54 +02:00
2023-05-16 11:33:00 +02:00
auto chosenCategory = tipsCategories [ random ( ) % tipsCategories . size ( ) ] . at ( " tips " ) ;
2022-10-27 13:21:54 +02:00
auto chosenTip = chosenCategory [ random ( ) % chosenCategory . size ( ) ] ;
s_tipOfTheDay = chosenTip . get < std : : string > ( ) ;
2022-02-01 18:09:40 +01:00
2024-02-03 12:16:36 +01:00
bool showTipOfTheDay = ContentRegistry : : Settings : : read < bool > ( " hex.builtin.setting.general " , " hex.builtin.setting.general.show_tips " , false ) ;
2022-02-01 18:09:40 +01:00
if ( showTipOfTheDay )
2023-04-08 00:58:53 +02:00
PopupTipOfTheDay : : open ( ) ;
2022-02-01 18:09:40 +01:00
}
2023-08-06 21:33:15 +02:00
if ( hasCrashed ) {
2023-11-08 21:40:27 +01:00
TaskManager : : doLater ( [ ] {
AchievementManager : : unlockAchievement ( " hex.builtin.achievement.starting_out " , " hex.builtin.achievement.starting_out.crash.name " ) ;
} ) ;
2025-02-15 11:15:56 +01:00
} else {
std : : random_device rd ;
if ( ! ImHexApi : : System : : isCorporateEnvironment ( ) ) {
if ( rd ( ) % 25 = = 0 ) {
ui : : BannerButton : : open ( ICON_VS_HEART , " Using ImHex for professional work? Ask your boss to sponsor us! " , ImColor ( 0x68 , 0xA7 , 0x70 ) , " Donate Now! " , [ ] {
hex : : openWebpage ( " https://imhex.werwolv.net/donate_work " ) ;
} ) ;
}
} else {
if ( rd ( ) % 75 = = 0 ) {
ui : : BannerButton : : open ( ICON_VS_HEART , " ImHex needs your help to stay alive! Donate now to fund infrastructure and further development " , ImColor ( 0x68 , 0xA7 , 0x70 ) , " Donate Now! " , [ ] {
hex : : openWebpage ( " https://github.com/sponsors/WerWolv " ) ;
} ) ;
}
}
2023-08-06 21:33:15 +02:00
}
2023-11-17 15:54:38 +01:00
// Load info banner texture either locally or from the server
TaskManager : : doLater ( [ ] {
2024-06-22 10:44:55 +02:00
for ( const auto & defaultPath : paths : : Resources . read ( ) ) {
2023-11-17 15:54:38 +01:00
const auto infoBannerPath = defaultPath / " info_banner.png " ;
if ( wolv : : io : : fs : : exists ( infoBannerPath ) ) {
2024-05-01 20:36:10 +02:00
s_infoBannerTexture = ImGuiExt : : Texture : : fromImage ( infoBannerPath , ImGuiExt : : Texture : : Filter : : Linear ) ;
2023-11-28 00:47:03 +01:00
2025-01-27 22:10:30 +01:00
if ( s_infoBannerTexture - > isValid ( ) )
2023-11-28 00:47:03 +01:00
break ;
2023-11-17 15:54:38 +01:00
}
}
2023-11-28 00:47:03 +01:00
2024-07-21 13:14:02 -05:00
auto allowNetworking = ContentRegistry : : Settings : : read < bool > ( " hex.builtin.setting.general " , " hex.builtin.setting.general.network_interface " , false )
& & ContentRegistry : : Settings : : read < int > ( " hex.builtin.setting.general " , " hex.builtin.setting.general.server_contact " , 0 ) ! = 0 ;
2025-01-27 22:10:30 +01:00
if ( ! s_infoBannerTexture - > isValid ( ) & & allowNetworking ) {
2025-01-31 20:23:47 +01:00
TaskManager : : createBackgroundTask ( " hex.builtin.task.loading_banner " , [ ] ( auto & ) {
2023-12-08 14:43:59 +01:00
HttpRequest request ( " GET " ,
2023-12-08 16:22:36 +01:00
ImHexApiURL + hex : : format ( " /info/{}/image " , hex : : toLower ( ImHexApi : : System : : getOSName ( ) ) ) ) ;
2023-12-08 14:43:59 +01:00
2023-11-28 00:47:03 +01:00
auto response = request . downloadFile ( ) . get ( ) ;
if ( response . isSuccess ( ) ) {
const auto & data = response . getData ( ) ;
2024-01-11 20:11:52 +01:00
if ( ! data . empty ( ) ) {
TaskManager : : doLater ( [ data ] {
2024-05-01 20:36:10 +02:00
s_infoBannerTexture = ImGuiExt : : Texture : : fromImage ( data . data ( ) , data . size ( ) , ImGuiExt : : Texture : : Filter : : Linear ) ;
2024-01-11 20:11:52 +01:00
} ) ;
}
2023-11-28 00:47:03 +01:00
}
} ) ;
}
2023-11-17 15:54:38 +01:00
} ) ;
2022-02-01 18:09:40 +01:00
}
2022-02-19 00:35:07 +01:00
}