feat(modulo): progress in the Wizard implementation

This commit is contained in:
Federico Terzi 2021-06-13 14:17:28 +02:00
parent 9bfe515c87
commit da67a46e2b
8 changed files with 1757 additions and 169 deletions

View File

@ -88,3 +88,27 @@ typedef struct SearchMetadata {
const char *windowTitle; const char *windowTitle;
const char *iconPath; const char *iconPath;
} SearchMetadata; } SearchMetadata;
// WIZARD
typedef struct WizardMetadata {
const char *version;
const int is_welcome_page_enabled;
const int is_move_bundle_page_enabled;
const int is_legacy_version_page_enabled;
const int is_migrate_page_enabled;
const int is_add_path_page_enabled;
const int is_accessibility_page_enabled;
const char *window_icon_path;
const char *accessibility_image_1_path;
const char *accessibility_image_2_path;
// METHODS
int (*is_legacy_version_running)();
int (*backup_and_migrate)();
int (*add_to_path)();
int (*enable_accessibility)();
int (*is_accessibility_enabled)();
} WizardMetadata;

View File

@ -18,6 +18,7 @@
*/ */
use std::ffi::c_void; use std::ffi::c_void;
use std::os::raw::{c_char, c_int};
pub(crate) trait Interoperable { pub(crate) trait Interoperable {
fn as_ptr(&self) -> *const c_void; fn as_ptr(&self) -> *const c_void;
@ -106,7 +107,28 @@ pub struct SearchMetadata {
pub iconPath: *const ::std::os::raw::c_char, pub iconPath: *const ::std::os::raw::c_char,
} }
use std::os::raw::{c_char, c_int}; #[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct WizardMetadata {
pub version: *const c_char,
pub is_welcome_page_enabled: c_int,
pub is_move_bundle_page_enabled: c_int,
pub is_legacy_version_page_enabled: c_int,
pub is_migrate_page_enabled: c_int,
pub is_add_path_page_enabled: c_int,
pub is_accessibility_page_enabled: c_int,
pub window_icon_path: *const c_char,
pub accessibility_image_1_path: *const c_char,
pub accessibility_image_2_path: *const c_char,
pub is_legacy_version_running: extern fn() -> c_int,
pub backup_and_migrate: extern fn() -> c_int,
pub add_to_path: extern fn() -> c_int,
pub enable_accessibility: extern fn() -> c_int,
pub is_accessibility_enabled: extern fn() -> c_int,
}
// Native bindings // Native bindings
@ -132,5 +154,5 @@ extern "C" {
pub(crate) fn update_items(app: *const c_void, items: *const SearchItem, itemCount: c_int); pub(crate) fn update_items(app: *const c_void, items: *const SearchItem, itemCount: c_int);
// WIZARD // WIZARD
pub(crate) fn interop_show_wizard(); pub(crate) fn interop_show_wizard(metadata: *const WizardMetadata);
} }

View File

@ -17,156 +17,175 @@
* along with modulo. If not, see <https://www.gnu.org/licenses/>. * along with modulo. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::ffi::CStr; use std::os::raw::{c_char, c_int};
use std::os::raw::{c_char, c_int, c_void}; use std::{ffi::CString, sync::Mutex};
pub mod types { use crate::{
// #[derive(Debug)] sys::interop::WizardMetadata,
// pub struct Search { wizard::{WizardHandlers, WizardOptions},
// pub title: String, };
// pub icon: Option<String>,
// pub items: Vec<SearchItem>, lazy_static! {
// } static ref HANDLERS: Mutex<Option<WizardHandlers>> = Mutex::new(None);
} }
#[allow(dead_code)] pub fn show(options: WizardOptions) {
mod interop { let c_version = CString::new(options.version).expect("unable to convert version to CString");
use super::types;
use super::super::interop::*;
use std::ffi::{c_void, CString};
// pub(crate) struct OwnedSearch { let (_c_window_icon_path, c_window_icon_path_ptr) =
// title: CString, convert_to_cstring_or_null(options.window_icon_path);
// icon_path: CString, let (_c_accessibility_image_1_path, c_accessibility_image_1_path_ptr) =
// items: Vec<OwnedSearchItem>, convert_to_cstring_or_null(options.accessibility_image_1_path);
// pub(crate) interop_items: Vec<SearchItem>, let (_c_accessibility_image_2_path, c_accessibility_image_2_path_ptr) =
// _interop: Box<SearchMetadata>, convert_to_cstring_or_null(options.accessibility_image_2_path);
// }
// impl Interoperable for OwnedSearch { extern "C" fn is_legacy_version_running() -> c_int {
// fn as_ptr(&self) -> *const c_void { let lock = HANDLERS
// &(*self._interop) as *const SearchMetadata as *const c_void .lock()
// } .expect("unable to acquire lock in is_legacy_version_running method");
// } let handlers_ref = (*lock).as_ref().expect("unable to unwrap handlers");
if let Some(handler_ref) = handlers_ref.is_legacy_version_running.as_ref() {
// impl From<&types::Search> for OwnedSearch { if (*handler_ref)() {
// fn from(search: &types::Search) -> Self { 1
// let title = } else {
// CString::new(search.title.clone()).expect("unable to convert search title to CString"); 0
}
// let items: Vec<OwnedSearchItem> = search.items.iter().map(|item| item.into()).collect(); } else {
-1
// let interop_items: Vec<SearchItem> = items.iter().map(|item| item.to_search_item()).collect(); }
// let icon_path = if let Some(icon_path) = search.icon.as_ref() {
// icon_path.clone()
// } else {
// "".to_owned()
// };
// let icon_path = CString::new(icon_path).expect("unable to convert search icon to CString");
// let icon_path_ptr = if search.icon.is_some() {
// icon_path.as_ptr()
// } else {
// std::ptr::null()
// };
// let _interop = Box::new(SearchMetadata {
// iconPath: icon_path_ptr,
// windowTitle: title.as_ptr(),
// });
// Self {
// title,
// items,
// icon_path,
// interop_items,
// _interop,
// }
// }
// }
// pub(crate) struct OwnedSearchItem {
// id: CString,
// label: CString,
// trigger: CString,
// }
// impl OwnedSearchItem {
// fn to_search_item(&self) -> SearchItem {
// SearchItem {
// id: self.id.as_ptr(),
// label: self.label.as_ptr(),
// trigger: self.trigger.as_ptr(),
// }
// }
// }
// impl From<&types::SearchItem> for OwnedSearchItem {
// fn from(item: &types::SearchItem) -> Self {
// let id = CString::new(item.id.clone()).expect("unable to convert item id to CString");
// let label =
// CString::new(item.label.clone()).expect("unable to convert item label to CString");
// let trigger = if let Some(trigger) = item.trigger.as_deref() {
// CString::new(trigger.to_string()).expect("unable to convert item trigger to CString")
// } else {
// CString::new("".to_string()).expect("unable to convert item trigger to CString")
// };
// Self { id, label, trigger }
// }
// }
} }
// struct SearchData { extern "C" fn backup_and_migrate() -> c_int {
// owned_search: interop::OwnedSearch, let lock = HANDLERS
// items: Vec<types::SearchItem>, .lock()
// algorithm: Box<dyn Fn(&str, &[types::SearchItem]) -> Vec<usize>>, .expect("unable to acquire lock in backup_and_migrate method");
// } let handlers_ref = (*lock).as_ref().expect("unable to unwrap handlers");
// TODO:
pub fn show() { // if let Some(handler_ref) = handlers_ref.backup_and_migrate.as_ref() {
use super::interop::*; // if (*handler_ref)() {
// 1
// extern "C" fn search_callback(query: *const c_char, app: *const c_void, data: *const c_void) { // } else {
// let query = unsafe { CStr::from_ptr(query) }; // 0
// let query = query.to_string_lossy().to_string();
// let search_data = data as *const SearchData;
// let search_data = unsafe { &*search_data };
// let indexes = (*search_data.algorithm)(&query, &search_data.items);
// let items: Vec<SearchItem> = indexes
// .into_iter()
// .map(|index| search_data.owned_search.interop_items[index])
// .collect();
// unsafe {
// update_items(app, items.as_ptr(), items.len() as c_int);
// } // }
// } else {
// -1
// } // }
0
}
// let mut result: Option<String> = None; extern "C" fn add_to_path() -> c_int {
let lock = HANDLERS
.lock()
.expect("unable to acquire lock in add_to_path method");
let handlers_ref = (*lock).as_ref().expect("unable to unwrap handlers");
// TODO:
// if let Some(handler_ref) = handlers_ref.add_to_path.as_ref() {
// if (*handler_ref)() {
// 1
// } else {
// 0
// }
// } else {
// -1
// }
0
}
// extern "C" fn result_callback(id: *const c_char, result: *mut c_void) { extern "C" fn enable_accessibility() -> c_int {
// let id = unsafe { CStr::from_ptr(id) }; let lock = HANDLERS
// let id = id.to_string_lossy().to_string(); .lock()
// let result: *mut Option<String> = result as *mut Option<String>; .expect("unable to acquire lock in enable_accessibility method");
// unsafe { let handlers_ref = (*lock).as_ref().expect("unable to unwrap handlers");
// *result = Some(id); // TODO:
// if let Some(handler_ref) = handlers_ref.add_to_path.as_ref() {
// if (*handler_ref)() {
// 1
// } else {
// 0
// } // }
// } else {
// -1
// } // }
0
}
extern "C" fn is_accessibility_enabled() -> c_int {
let lock = HANDLERS
.lock()
.expect("unable to acquire lock in is_accessibility_enabled method");
let handlers_ref = (*lock).as_ref().expect("unable to unwrap handlers");
// TODO:
// if let Some(handler_ref) = handlers_ref.add_to_path.as_ref() {
// if (*handler_ref)() {
// 1
// } else {
// 0
// }
// } else {
// -1
// }
0
}
{
let mut lock = HANDLERS.lock().expect("unable to acquire handlers lock");
*lock = Some(options.handlers)
}
let wizard_metadata = WizardMetadata {
version: c_version.as_ptr(),
is_welcome_page_enabled: if options.is_welcome_page_enabled {
1
} else {
0
},
is_move_bundle_page_enabled: if options.is_move_bundle_page_enabled {
1
} else {
0
},
is_legacy_version_page_enabled: if options.is_legacy_version_page_enabled {
1
} else {
0
},
is_migrate_page_enabled: if options.is_migrate_page_enabled {
1
} else {
0
},
is_add_path_page_enabled: if options.is_add_path_page_enabled {
1
} else {
0
},
is_accessibility_page_enabled: if options.is_accessibility_page_enabled {
1
} else {
0
},
window_icon_path: c_window_icon_path_ptr,
accessibility_image_1_path: c_accessibility_image_1_path_ptr,
accessibility_image_2_path: c_accessibility_image_2_path_ptr,
is_legacy_version_running,
backup_and_migrate,
add_to_path,
enable_accessibility,
is_accessibility_enabled,
};
unsafe { unsafe {
interop_show_wizard( super::interop::interop_show_wizard(&wizard_metadata);
// metadata, }
// search_callback,
// &search_data as *const SearchData as *const c_void,
// result_callback,
// &mut result as *mut Option<String> as *mut c_void,
);
} }
//result fn convert_to_cstring_or_null(str: Option<String>) -> (Option<CString>, *const c_char) {
let c_string =
str.map(|str| CString::new(str).expect("unable to convert Option<String> to CString"));
let c_ptr = c_string.as_ref().map_or(std::ptr::null(), |path| path.as_ptr());
(c_string, c_ptr)
} }

View File

@ -27,6 +27,16 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
const int WELCOME_PAGE_INDEX = 0;
const int MOVE_BUNDLE_PAGE_INDEX = WELCOME_PAGE_INDEX + 1;
const int LEGACY_VERSION_PAGE_INDEX = MOVE_BUNDLE_PAGE_INDEX + 1;
const int MIGRATE_PAGE_INDEX = LEGACY_VERSION_PAGE_INDEX + 1;
const int ADD_PATH_PAGE_INDEX = MIGRATE_PAGE_INDEX + 1;
const int ACCESSIBILITY_PAGE_INDEX = ADD_PATH_PAGE_INDEX + 1;
const int MAX_PAGE_INDEX = ACCESSIBILITY_PAGE_INDEX + 1; // Update if a new page is added at the end
WizardMetadata *metadata= nullptr;
// App Code // App Code
class WizardApp : public wxApp class WizardApp : public wxApp
@ -35,11 +45,49 @@ public:
virtual bool OnInit(); virtual bool OnInit();
}; };
int find_next_page(int current_index) {
int next_index = current_index + 1;
if (next_index >= MAX_PAGE_INDEX) {
return -1;
}
switch (next_index) {
case WELCOME_PAGE_INDEX:
if (metadata->is_welcome_page_enabled) {
return WELCOME_PAGE_INDEX;
}
case MOVE_BUNDLE_PAGE_INDEX:
if (metadata->is_move_bundle_page_enabled) {
return MOVE_BUNDLE_PAGE_INDEX;
}
case LEGACY_VERSION_PAGE_INDEX:
if (metadata->is_legacy_version_page_enabled) {
return LEGACY_VERSION_PAGE_INDEX;
}
case MIGRATE_PAGE_INDEX:
if (metadata->is_migrate_page_enabled) {
return MIGRATE_PAGE_INDEX;
}
case ADD_PATH_PAGE_INDEX:
if (metadata->is_add_path_page_enabled) {
return ADD_PATH_PAGE_INDEX;
}
case ACCESSIBILITY_PAGE_INDEX:
if (metadata->is_accessibility_page_enabled) {
return ACCESSIBILITY_PAGE_INDEX;
}
}
return find_next_page(next_index);
}
class DerivedFrame : public WizardFrame class DerivedFrame : public WizardFrame
{ {
protected: protected:
void check_timer_tick( wxTimerEvent& event );
void welcome_start_clicked(wxCommandEvent &event); void welcome_start_clicked(wxCommandEvent &event);
void navigate_to_next_page_or_close();
public: public:
DerivedFrame(wxWindow *parent); DerivedFrame(wxWindow *parent);
}; };
@ -47,17 +95,54 @@ public:
DerivedFrame::DerivedFrame(wxWindow *parent) DerivedFrame::DerivedFrame(wxWindow *parent)
: WizardFrame(parent) : WizardFrame(parent)
{ {
// TODO: load images for accessibility page if on macOS
this->welcome_version_text->SetLabel(wxString::Format("( version %s )", metadata->version));
// Load the first page
int page = find_next_page(-1);
if (page >= 0) {
this->m_simplebook->ChangeSelection(page);
} else {
Close(true);
}
}
void DerivedFrame::navigate_to_next_page_or_close() {
int current_page = this->m_simplebook->GetSelection();
int page = find_next_page(current_page);
if (page >= 0) {
this->m_simplebook->ChangeSelection(page);
} else {
Close(true);
}
} }
void DerivedFrame::welcome_start_clicked(wxCommandEvent &event) void DerivedFrame::welcome_start_clicked(wxCommandEvent &event)
{ {
this->m_simplebook->ChangeSelection(2); this->navigate_to_next_page_or_close();
} }
void DerivedFrame::check_timer_tick( wxTimerEvent& event ) {
if (this->m_simplebook->GetSelection() == LEGACY_VERSION_PAGE_INDEX) {
if (metadata->is_legacy_version_running) {
if (metadata->is_legacy_version_running() == 0) {
this->navigate_to_next_page_or_close();
}
}
}
}
bool WizardApp::OnInit() bool WizardApp::OnInit()
{ {
wxInitAllImageHandlers();
DerivedFrame *frame = new DerivedFrame(NULL); DerivedFrame *frame = new DerivedFrame(NULL);
//setFrameIcon(formMetadata->iconPath, frame);
if (metadata->window_icon_path) {
setFrameIcon(metadata->window_icon_path, frame);
}
frame->Show(true); frame->Show(true);
Activate(frame); Activate(frame);
@ -65,13 +150,15 @@ bool WizardApp::OnInit()
return true; return true;
} }
extern "C" void interop_show_wizard() extern "C" void interop_show_wizard(WizardMetadata * _metadata)
{ {
// Setup high DPI support on Windows // Setup high DPI support on Windows
#ifdef __WXMSW__ #ifdef __WXMSW__
SetProcessDPIAware(); SetProcessDPIAware();
#endif #endif
metadata = _metadata;
wxApp::SetInstance(new WizardApp()); wxApp::SetInstance(new WizardApp());
int argc = 0; int argc = 0;
wxEntry(argc, (char **)nullptr); wxEntry(argc, (char **)nullptr);

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,9 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
this->SetSizeHints( wxDefaultSize, wxDefaultSize ); this->SetSizeHints( wxDefaultSize, wxDefaultSize );
this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
check_timer.SetOwner( this, wxID_ANY );
check_timer.Start( 500 );
wxBoxSizer* bSizer1; wxBoxSizer* bSizer1;
bSizer1 = new wxBoxSizer( wxVERTICAL ); bSizer1 = new wxBoxSizer( wxVERTICAL );
@ -36,9 +39,12 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
welcome_version_text->Wrap( -1 ); welcome_version_text->Wrap( -1 );
bSizer2->Add( welcome_version_text, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); bSizer2->Add( welcome_version_text, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
bSizer2->Add( 0, 20, 0, 0, 5 );
welcome_description_text = new wxStaticText( welcome_panel, wxID_ANY, wxT("This wizard will help you to quickly get started with espanso. \n\nClick \"Start\" when you are ready"), wxDefaultPosition, wxDefaultSize, 0 ); welcome_description_text = new wxStaticText( welcome_panel, wxID_ANY, wxT("This wizard will help you to quickly get started with espanso. \n\nClick \"Start\" when you are ready"), wxDefaultPosition, wxDefaultSize, 0 );
welcome_description_text->Wrap( -1 ); welcome_description_text->Wrap( -1 );
bSizer2->Add( welcome_description_text, 0, wxALL, 20 ); bSizer2->Add( welcome_description_text, 0, wxALL, 10 );
bSizer2->Add( 0, 0, 1, wxEXPAND, 5 ); bSizer2->Add( 0, 0, 1, wxEXPAND, 5 );
@ -53,6 +59,38 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
welcome_panel->Layout(); welcome_panel->Layout();
bSizer2->Fit( welcome_panel ); bSizer2->Fit( welcome_panel );
m_simplebook->AddPage( welcome_panel, wxT("a page"), false ); m_simplebook->AddPage( welcome_panel, wxT("a page"), false );
move_bundle_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
move_bundle_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer22;
bSizer22 = new wxBoxSizer( wxVERTICAL );
move_bundle_title = new wxStaticText( move_bundle_panel, wxID_ANY, wxT("Move to /Applications folder"), wxDefaultPosition, wxDefaultSize, 0 );
move_bundle_title->Wrap( -1 );
move_bundle_title->SetFont( wxFont( 18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer22->Add( move_bundle_title, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP, 20 );
bSizer22->Add( 0, 20, 0, 0, 5 );
move_bundle_description = new wxStaticText( move_bundle_panel, wxID_ANY, wxT("Espanso is being run from outside the Applications directory, which prevents it from working correctly.\n\nPlease move the Espanso.app bundle inside your Applications folder and start it again.\n"), wxDefaultPosition, wxDefaultSize, 0 );
move_bundle_description->Wrap( -1 );
bSizer22->Add( move_bundle_description, 0, wxALL, 10 );
bSizer22->Add( 0, 20, 1, wxEXPAND, 5 );
move_bundle_quit_button = new wxButton( move_bundle_panel, wxID_ANY, wxT("Start"), wxDefaultPosition, wxDefaultSize, 0 );
move_bundle_quit_button->SetDefault();
bSizer22->Add( move_bundle_quit_button, 0, wxALIGN_RIGHT|wxALL, 10 );
move_bundle_panel->SetSizer( bSizer22 );
move_bundle_panel->Layout();
bSizer22->Fit( move_bundle_panel );
m_simplebook->AddPage( move_bundle_panel, wxT("a page"), false );
legacy_version_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); legacy_version_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
legacy_version_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); legacy_version_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@ -65,12 +103,15 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
bSizer21->Add( legacy_version_title, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxTOP, 20 ); bSizer21->Add( legacy_version_title, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxTOP, 20 );
legacy_version_description = new wxStaticText( legacy_version_panel, wxID_ANY, wxT("A legacy espanso process has been detected and prevents the new version from working correctly.\n\nPlease terminate and uninstall the old espanso version to proceed.\n\nFor more information, see: \n"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer21->Add( 0, 20, 0, 0, 5 );
legacy_version_description = new wxStaticText( legacy_version_panel, wxID_ANY, wxT("A legacy espanso process has been detected and prevents the new version from working correctly.\n\nPlease terminate and uninstall the old espanso version to proceed.\n\nFor more information, see: "), wxDefaultPosition, wxDefaultSize, 0 );
legacy_version_description->Wrap( 500 ); legacy_version_description->Wrap( 500 );
bSizer21->Add( legacy_version_description, 0, wxLEFT|wxRIGHT|wxTOP, 20 ); bSizer21->Add( legacy_version_description, 0, wxLEFT|wxRIGHT|wxTOP, 10 );
legacy_version_docs_link = new wxHyperlinkCtrl( legacy_version_panel, wxID_ANY, wxT("https://espanso.org/migration#uninstall"), wxT("https://espanso.org/migration#uninstall"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); legacy_version_docs_link = new wxHyperlinkCtrl( legacy_version_panel, wxID_ANY, wxT("https://espanso.org/migration#uninstall"), wxT("https://espanso.org/migration#uninstall"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
bSizer21->Add( legacy_version_docs_link, 0, wxLEFT|wxRIGHT, 20 ); bSizer21->Add( legacy_version_docs_link, 0, wxLEFT|wxRIGHT, 10 );
bSizer21->Add( 0, 0, 1, wxEXPAND, 5 ); bSizer21->Add( 0, 0, 1, wxEXPAND, 5 );
@ -99,12 +140,15 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
bSizer211->Add( migrate_title, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxTOP, 20 ); bSizer211->Add( migrate_title, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxTOP, 20 );
migrate_description = new wxStaticText( migrate_panel, wxID_ANY, wxT("The new version uses a slightly different configuration format that powers some exciting features.\n\nTo ease the transition, espanso offers two possible choices: \n\n - Automatically backup the old configuration in the Documents folder and migrate to the new format (recommended). \n - Use compatibility mode without changing the configs. \n\nKeep in mind that: \n\n - Compatibility mode does not support all new espanso features \n - You can always migrate the configs later \n\nFor more information, see: \n"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer211->Add( 0, 20, 0, 0, 5 );
migrate_description = new wxStaticText( migrate_panel, wxID_ANY, wxT("The new version uses a slightly different configuration format that powers some exciting new features.\n\nTo ease the transition, espanso offers two possible choices: \n\n - Automatically backup the old configuration in the Documents folder and migrate to the new format (recommended). \n - Use compatibility mode without changing the configs. \n\nKeep in mind that: \n\n - Compatibility mode does not support all new espanso features \n - You can always migrate the configs later \n\nFor more information, see: "), wxDefaultPosition, wxDefaultSize, 0 );
migrate_description->Wrap( 500 ); migrate_description->Wrap( 500 );
bSizer211->Add( migrate_description, 1, wxLEFT|wxRIGHT|wxTOP, 20 ); bSizer211->Add( migrate_description, 1, wxLEFT|wxRIGHT|wxTOP, 10 );
migrate_link = new wxHyperlinkCtrl( migrate_panel, wxID_ANY, wxT("https://espanso.org/migration"), wxT("https://espanso.org/migration"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); migrate_link = new wxHyperlinkCtrl( migrate_panel, wxID_ANY, wxT("https://espanso.org/migration"), wxT("https://espanso.org/migration"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
bSizer211->Add( migrate_link, 0, wxLEFT|wxRIGHT, 20 ); bSizer211->Add( migrate_link, 0, wxLEFT|wxRIGHT, 10 );
bSizer211->Add( 0, 0, 10, wxEXPAND, 5 ); bSizer211->Add( 0, 0, 10, wxEXPAND, 5 );
@ -131,6 +175,96 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
migrate_panel->Layout(); migrate_panel->Layout();
bSizer211->Fit( migrate_panel ); bSizer211->Fit( migrate_panel );
m_simplebook->AddPage( migrate_panel, wxT("a page"), false ); m_simplebook->AddPage( migrate_panel, wxT("a page"), false );
add_path_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
add_path_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer212;
bSizer212 = new wxBoxSizer( wxVERTICAL );
add_path_title = new wxStaticText( add_path_panel, wxID_ANY, wxT("Add to PATH"), wxDefaultPosition, wxDefaultSize, 0 );
add_path_title->Wrap( -1 );
add_path_title->SetFont( wxFont( 18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer212->Add( add_path_title, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxTOP, 20 );
bSizer212->Add( 0, 20, 0, 0, 5 );
add_path_description = new wxStaticText( add_path_panel, wxID_ANY, wxT("Espanso offers a rich CLI interface that enables some powerful features and comes handy when debugging configuration problems.\n\nTo be easily accessed, espanso can be added to the PATH environment variable automatically. Do you want to proceed?\n"), wxDefaultPosition, wxDefaultSize, 0 );
add_path_description->Wrap( 500 );
bSizer212->Add( add_path_description, 0, wxLEFT|wxRIGHT|wxTOP, 10 );
add_path_checkbox = new wxCheckBox( add_path_panel, wxID_ANY, wxT("Yes, add espanso to PATH"), wxDefaultPosition, wxDefaultSize, 0 );
add_path_checkbox->SetValue(true);
bSizer212->Add( add_path_checkbox, 0, wxALL, 20 );
add_path_note = new wxStaticText( add_path_panel, wxID_ANY, wxT("Note: if you don't know what the PATH env variable is, you should probably keep this checked."), wxDefaultPosition, wxDefaultSize, 0 );
add_path_note->Wrap( 500 );
bSizer212->Add( add_path_note, 0, wxALL, 10 );
bSizer212->Add( 0, 0, 1, wxEXPAND, 5 );
add_path_continue_button = new wxButton( add_path_panel, wxID_ANY, wxT("Continue"), wxDefaultPosition, wxDefaultSize, 0 );
add_path_continue_button->SetDefault();
bSizer212->Add( add_path_continue_button, 0, wxALIGN_RIGHT|wxALL, 10 );
add_path_panel->SetSizer( bSizer212 );
add_path_panel->Layout();
bSizer212->Fit( add_path_panel );
m_simplebook->AddPage( add_path_panel, wxT("a page"), false );
accessibility_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
accessibility_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer2121;
bSizer2121 = new wxBoxSizer( wxVERTICAL );
accessibility_title = new wxStaticText( accessibility_panel, wxID_ANY, wxT("Enable Accessibility"), wxDefaultPosition, wxDefaultSize, 0 );
accessibility_title->Wrap( -1 );
accessibility_title->SetFont( wxFont( 18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizer2121->Add( accessibility_title, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxTOP, 20 );
bSizer2121->Add( 0, 20, 0, 0, 5 );
m_scrolledWindow1 = new wxScrolledWindow( accessibility_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL );
m_scrolledWindow1->SetScrollRate( 5, 5 );
wxBoxSizer* bSizer81;
bSizer81 = new wxBoxSizer( wxVERTICAL );
accessibility_description = new wxStaticText( m_scrolledWindow1, wxID_ANY, wxT("Espanso needs Accessibility permissions to detect and insert snippets into applications. \n\nTo enable it, follow these steps:\n\n1. Click on \"Enable\" (at the bottom right)\n2. In the dialog that appears, click on \"Open System Preferences\"\n"), wxDefaultPosition, wxDefaultSize, 0 );
accessibility_description->Wrap( 500 );
bSizer81->Add( accessibility_description, 0, wxLEFT|wxRIGHT|wxTOP, 10 );
accessibility_image1 = new wxStaticBitmap( m_scrolledWindow1, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
bSizer81->Add( accessibility_image1, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
accessibility_description2 = new wxStaticText( m_scrolledWindow1, wxID_ANY, wxT("3. Then, under the \"Privacy\" panel click on the Lock icon (1) to enable edits and then check \"Espanso\" (2), as shown in the picture:"), wxDefaultPosition, wxDefaultSize, 0 );
accessibility_description2->Wrap( 500 );
bSizer81->Add( accessibility_description2, 0, wxALL, 10 );
accessibility_image2 = new wxStaticBitmap( m_scrolledWindow1, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
bSizer81->Add( accessibility_image2, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
m_scrolledWindow1->SetSizer( bSizer81 );
m_scrolledWindow1->Layout();
bSizer81->Fit( m_scrolledWindow1 );
bSizer2121->Add( m_scrolledWindow1, 1, wxEXPAND | wxALL, 0 );
accessibility_enable_button = new wxButton( accessibility_panel, wxID_ANY, wxT("Enable"), wxDefaultPosition, wxDefaultSize, 0 );
accessibility_enable_button->SetDefault();
bSizer2121->Add( accessibility_enable_button, 0, wxALIGN_RIGHT|wxALL, 10 );
accessibility_panel->SetSizer( bSizer2121 );
accessibility_panel->Layout();
bSizer2121->Fit( accessibility_panel );
m_simplebook->AddPage( accessibility_panel, wxT("a page"), false );
bSizer1->Add( m_simplebook, 1, wxEXPAND | wxALL, 5 ); bSizer1->Add( m_simplebook, 1, wxEXPAND | wxALL, 5 );
@ -141,12 +275,24 @@ WizardFrame::WizardFrame( wxWindow* parent, wxWindowID id, const wxString& title
this->Centre( wxBOTH ); this->Centre( wxBOTH );
// Connect Events // Connect Events
this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( WizardFrame::check_timer_tick ) );
welcome_start_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::welcome_start_clicked ), NULL, this ); welcome_start_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::welcome_start_clicked ), NULL, this );
move_bundle_quit_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::move_bundle_quit_clicked ), NULL, this );
migrate_compatibility_mode_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::migrate_compatibility_mode_clicked ), NULL, this );
migrate_backup_and_migrate_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::migrate_button_clicked ), NULL, this );
add_path_continue_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::add_path_continue_clicked ), NULL, this );
accessibility_enable_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::accessibility_enable_clicked ), NULL, this );
} }
WizardFrame::~WizardFrame() WizardFrame::~WizardFrame()
{ {
// Disconnect Events // Disconnect Events
this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( WizardFrame::check_timer_tick ) );
welcome_start_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::welcome_start_clicked ), NULL, this ); welcome_start_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::welcome_start_clicked ), NULL, this );
move_bundle_quit_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::move_bundle_quit_clicked ), NULL, this );
migrate_compatibility_mode_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::migrate_compatibility_mode_clicked ), NULL, this );
migrate_backup_and_migrate_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::migrate_button_clicked ), NULL, this );
add_path_continue_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::add_path_continue_clicked ), NULL, this );
accessibility_enable_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WizardFrame::accessibility_enable_clicked ), NULL, this );
} }

View File

@ -9,6 +9,7 @@
#include <wx/artprov.h> #include <wx/artprov.h>
#include <wx/xrc/xmlres.h> #include <wx/xrc/xmlres.h>
#include <wx/timer.h>
#include <wx/string.h> #include <wx/string.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
@ -22,6 +23,9 @@
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/hyperlink.h> #include <wx/hyperlink.h>
#include <wx/checkbox.h>
#include <wx/statbmp.h>
#include <wx/scrolwin.h>
#include <wx/simplebook.h> #include <wx/simplebook.h>
#include <wx/frame.h> #include <wx/frame.h>
@ -36,12 +40,17 @@ class WizardFrame : public wxFrame
private: private:
protected: protected:
wxTimer check_timer;
wxSimplebook* m_simplebook; wxSimplebook* m_simplebook;
wxPanel* welcome_panel; wxPanel* welcome_panel;
wxStaticText* welcome_title_text; wxStaticText* welcome_title_text;
wxStaticText* welcome_version_text; wxStaticText* welcome_version_text;
wxStaticText* welcome_description_text; wxStaticText* welcome_description_text;
wxButton* welcome_start_button; wxButton* welcome_start_button;
wxPanel* move_bundle_panel;
wxStaticText* move_bundle_title;
wxStaticText* move_bundle_description;
wxButton* move_bundle_quit_button;
wxPanel* legacy_version_panel; wxPanel* legacy_version_panel;
wxStaticText* legacy_version_title; wxStaticText* legacy_version_title;
wxStaticText* legacy_version_description; wxStaticText* legacy_version_description;
@ -53,14 +62,34 @@ class WizardFrame : public wxFrame
wxHyperlinkCtrl* migrate_link; wxHyperlinkCtrl* migrate_link;
wxButton* migrate_compatibility_mode_button; wxButton* migrate_compatibility_mode_button;
wxButton* migrate_backup_and_migrate_button; wxButton* migrate_backup_and_migrate_button;
wxPanel* add_path_panel;
wxStaticText* add_path_title;
wxStaticText* add_path_description;
wxCheckBox* add_path_checkbox;
wxStaticText* add_path_note;
wxButton* add_path_continue_button;
wxPanel* accessibility_panel;
wxStaticText* accessibility_title;
wxScrolledWindow* m_scrolledWindow1;
wxStaticText* accessibility_description;
wxStaticBitmap* accessibility_image1;
wxStaticText* accessibility_description2;
wxStaticBitmap* accessibility_image2;
wxButton* accessibility_enable_button;
// Virtual event handlers, overide them in your derived class // Virtual event handlers, overide them in your derived class
virtual void check_timer_tick( wxTimerEvent& event ) { event.Skip(); }
virtual void welcome_start_clicked( wxCommandEvent& event ) { event.Skip(); } virtual void welcome_start_clicked( wxCommandEvent& event ) { event.Skip(); }
virtual void move_bundle_quit_clicked( wxCommandEvent& event ) { event.Skip(); }
virtual void migrate_compatibility_mode_clicked( wxCommandEvent& event ) { event.Skip(); }
virtual void migrate_button_clicked( wxCommandEvent& event ) { event.Skip(); }
virtual void add_path_continue_clicked( wxCommandEvent& event ) { event.Skip(); }
virtual void accessibility_enable_clicked( wxCommandEvent& event ) { event.Skip(); }
public: public:
WizardFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Espanso"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 550,523 ), long style = wxCAPTION|wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); WizardFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Espanso"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 550,577 ), long style = wxCAPTION|wxCLOSE_BOX|wxSYSTEM_MENU|wxTAB_TRAVERSAL );
~WizardFrame(); ~WizardFrame();

View File

@ -18,3 +18,35 @@
*/ */
pub use crate::sys::wizard::show; pub use crate::sys::wizard::show;
pub struct WizardOptions {
pub version: String,
pub is_welcome_page_enabled: bool,
pub is_move_bundle_page_enabled: bool,
pub is_legacy_version_page_enabled: bool,
pub is_migrate_page_enabled: bool,
pub is_add_path_page_enabled: bool,
pub is_accessibility_page_enabled: bool,
pub window_icon_path: Option<String>,
pub accessibility_image_1_path: Option<String>,
pub accessibility_image_2_path: Option<String>,
pub handlers: WizardHandlers,
}
pub struct WizardHandlers {
pub is_legacy_version_running: Option<Box<dyn Fn() -> bool + Send>>,
pub backup_and_migrate: Option<Box<dyn Fn() -> MigrationResult + Send>>,
pub add_to_path: Option<Box<dyn Fn() -> bool + Send>>,
pub enable_accessibility: Option<Box<dyn Fn() + Send>>,
pub is_accessibility_enabled: Option<Box<dyn Fn() -> bool + Send>>,
}
#[derive(Debug)]
pub enum MigrationResult {
Success,
CleanFailure,
DirtyFailure,
}