ui/winui/toolkit.cpp

changeset 431
bb7da585debc
parent 388
473c03f85197
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/winui/toolkit.cpp	Sat Jan 04 16:38:48 2025 +0100
@@ -0,0 +1,382 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+
+#include "toolkit.h"
+
+#include <cx/allocator.h>
+#include <cx/mempool.h>
+
+#include "../common/context.h"
+#include "../common/document.h"
+#include "../common/toolbar.h"
+#include "../common/properties.h"
+
+#include "icons.h"
+
+#include "MainWindow.xaml.h"
+
+#include "App.xaml.h"
+
+using namespace winrt;
+using namespace Microsoft::UI::Xaml;
+using namespace Microsoft::UI::Xaml::Controls;
+using namespace Microsoft::UI::Xaml::XamlTypeInfo;
+using namespace Microsoft::UI::Xaml::Markup;
+using namespace Windows::UI::Xaml::Interop;
+using namespace winrt::Windows::Foundation;
+using namespace Windows::UI::Core;
+
+static const char* application_name;
+
+static ui_callback   startup_func;
+static void* startup_data;
+
+static ui_callback   open_func;
+void* open_data;
+
+static ui_callback   exit_func;
+void* exit_data;
+
+static ui_callback   appclose_fnc;
+
+static void* appclose_udata;
+
+
+static UiObject* active_window;
+
+static winrt::Microsoft::UI::Dispatching::DispatcherQueue uiDispatcherQueue = { nullptr };
+
+void ui_app_run_startup() {
+	uiDispatcherQueue = winrt::Microsoft::UI::Dispatching::DispatcherQueue::GetForCurrentThread();
+	
+	if (startup_func) {
+		startup_func(NULL, startup_data);
+	}
+}
+
+class App : public ApplicationT<App, IXamlMetadataProvider> {
+public:
+	void OnLaunched(LaunchActivatedEventArgs const&) {
+		Resources().MergedDictionaries().Append(XamlControlsResources());
+		if (startup_func) {
+			startup_func(NULL, startup_data);
+		}
+
+		//auto window = make<winui::implementation::MainWindow>();
+		//window.Activate();
+	}
+	IXamlType GetXamlType(TypeName const& type) {
+		return provider.GetXamlType(type);
+	}
+	IXamlType GetXamlType(hstring const& fullname) {
+		return provider.GetXamlType(fullname);
+	}
+	com_array<XmlnsDefinition> GetXmlnsDefinitions() {
+		return provider.GetXmlnsDefinitions();
+	}
+private:
+	XamlControlsXamlMetaDataProvider provider;
+};
+
+UiWidget::UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm) : uielement(elm) {}
+
+extern "C" void destroy_ui_window_wrapper(void* ptr) {
+	UiWindow* win = (UiWindow*)ptr;
+	delete win;
+}
+
+extern "C" void destroy_ui_widget_wrapper(void* ptr) {
+	UiWidget* widget = (UiWidget*)ptr;
+	delete widget;
+}
+
+extern "C" void destroy_ui_container_wrapper(void* ptr) {
+	UiContainer* ctn = (UiContainer*)ptr;
+	delete ctn;
+}
+
+void ui_context_add_window_destructor(UiContext* ctx, UiWindow* win) {
+	cxMempoolRegister(ctx->mp, win, destroy_ui_window_wrapper);
+}
+
+void ui_context_add_widget_destructor(UiContext* ctx, UiWidget* widget) {
+	cxMempoolRegister(ctx->mp, widget, destroy_ui_widget_wrapper);
+}
+
+void ui_context_add_container_destructor(UiContext* ctx, UiContainer *container) {
+	cxMempoolRegister(ctx->mp, container, destroy_ui_container_wrapper);
+}
+
+
+UiEvent ui_create_int_event(UiObject* obj, int64_t i) {
+	UiEvent evt;
+	evt.obj = obj;
+	evt.window = obj->window;
+	evt.document = obj->ctx->document;
+	evt.eventdata = nullptr;
+	evt.intval = i;
+	return evt;
+}
+
+
+#include <MddBootstrap.h>
+
+void ui_appsdk_bootstrap(void) {
+	const UINT32 majorMinorVersion{ 0x00010002 };
+	PCWSTR versionTag{ L"" };
+	const PACKAGE_VERSION minVersion{};
+
+	const HRESULT hr = MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion);
+	if (FAILED(hr)) {
+		exit(102);
+	}
+}
+
+void ui_init(const char* appname, int argc, char** argv) {
+	application_name = appname;
+
+	//ui_appsdk_bootstrap();
+
+	uic_init_global_context();
+	uic_docmgr_init();
+        uic_menu_init();
+	uic_toolbar_init();
+	
+	uic_load_app_properties();
+}
+
+const char* ui_appname() {
+	return application_name;
+}
+
+void ui_onstartup(ui_callback f, void* userdata) {
+	startup_func = f;
+	startup_data = userdata;
+}
+
+void ui_onopen(ui_callback f, void* userdata) {
+	open_func = f;
+	open_data = userdata;
+}
+
+void ui_onexit(ui_callback f, void* userdata) {
+	exit_func = f;
+	exit_data = userdata;
+}
+
+void ui_main() {
+	/*
+	init_apartment();
+	//Application::Start([](auto&&) {make<App>(); });
+
+	::winrt::Microsoft::UI::Xaml::Application::Start(
+		[](auto&&)
+		{
+			::winrt::make<::winrt::winui::implementation::App>();
+		});
+		*/
+	{
+		void (WINAPI * pfnXamlCheckProcessRequirements)();
+		auto module = ::LoadLibrary(L"Microsoft.ui.xaml.dll");
+		if (module)
+		{
+			pfnXamlCheckProcessRequirements = reinterpret_cast<decltype(pfnXamlCheckProcessRequirements)>(GetProcAddress(module, "XamlCheckProcessRequirements"));
+			if (pfnXamlCheckProcessRequirements)
+			{
+				(*pfnXamlCheckProcessRequirements)();
+			}
+
+			::FreeLibrary(module);
+		}
+	}
+
+	winrt::init_apartment(winrt::apartment_type::single_threaded);
+	::winrt::Microsoft::UI::Xaml::Application::Start(
+		[](auto&&)
+		{
+			::winrt::make<::winrt::winui::implementation::App>();
+		});
+}
+
+class UiWin {
+public:
+	Window window;
+};
+
+void ui_show(UiObject* obj) {
+	if (obj->wobj) {
+		obj->wobj->window.Activate();
+	} else if(obj->widget && obj->widget->Show) {
+		obj->widget->Show();
+	}
+}
+
+void ui_close(UiObject* obj) {
+	if (obj->wobj) {
+		obj->wobj->window.Close();
+	}
+}
+
+static void ui_job_thread(UiJob* job) {
+	if (!job->job_func(job->job_data) && job->finish_callback) {
+		bool isQueued = uiDispatcherQueue.TryEnqueue([job]()
+		{
+			UiEvent event;
+			event.obj = job->obj;
+			event.window = job->obj->window;
+			event.document = job->obj->ctx->document;
+			event.intval = 0;
+			event.eventdata = NULL;
+			job->finish_callback(&event, job->finish_data);
+			delete job;
+		});
+		if (!isQueued) {
+			// TODO: error or try again?
+			exit(-1);
+		}
+	}
+	else {
+		delete job;
+	}
+}
+
+UIEXPORT void ui_job(UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) {
+	UiJob* job = new UiJob;
+	job->obj = obj;
+	job->job_func = tf;
+	job->job_data = td;
+	job->finish_callback = f;
+	job->finish_data = fd;
+
+	std::thread jobThread(ui_job_thread, job);
+	jobThread.detach();
+}
+
+UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td) {
+	bool isQueued = uiDispatcherQueue.TryEnqueue([tf, td]()
+	{
+		(void)tf(td);
+	});
+	if (!isQueued) {
+		// TODO: error or try again?
+		exit(-1);
+	}
+}
+
+static UiJob kill_job; // &kill_job indicates to stop the thread
+
+static void ui_threadpool_run(UiThreadpool* pool) {
+	for (;;) {
+		UiJob* job = pool->GetJob();
+		if (job == &kill_job) {
+			return;
+		}
+		else if (job) {
+			ui_job_thread(job);
+		}
+	}
+}
+
+UiThreadpool::UiThreadpool(int nthreads) {
+	for (int i = 0; i < nthreads; i++) {
+		std::thread thread(ui_threadpool_run, this);
+		thread.detach();
+	}
+}
+
+void UiThreadpool::EnqueueJob(UiJob* job)
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	queue.push(job);
+	lock.unlock();
+	condition.notify_one();
+}
+
+UiJob* UiThreadpool::GetJob() {
+	std::unique_lock<std::mutex> lock(mutex);
+
+	UiJob* job = nullptr;
+	while (!job) {
+		if (queue.empty()) {
+			condition.wait(lock);
+			continue;
+		}
+		else
+		{
+			job = queue.front();
+			queue.pop();
+		}
+	}
+
+	return job;
+}
+
+UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads) {
+	return new UiThreadpool(nthreads);
+}
+
+UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool) {
+	// TODO
+}
+
+UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) {
+	UiJob* job = new UiJob;
+	job->obj = obj;
+	job->job_func = tf;
+	job->job_data = td;
+	job->finish_callback = f;
+	job->finish_data = fd;
+	pool->EnqueueJob(job);
+}
+
+
+
+void ui_set_widget_groups(UiContext *ctx, UIWIDGET widget, const int *groups) {
+	if(!groups) {
+		return;
+	}
+	size_t ngroups = uic_group_array_size(groups);
+	ui_set_widget_ngroups(ctx, widget, groups, ngroups);
+}
+
+void ui_set_widget_ngroups(UiContext *ctx, UIWIDGET widget, const int *groups, size_t ngroups) {
+	if(ngroups > 0) {
+		uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
+		ui_set_enabled(widget, FALSE);
+	}
+}
+
+
+UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled) {
+	Control ctrl = widget->uielement.as<Control>();
+	if (ctrl) {
+		ctrl.IsEnabled(enabled);
+	}
+}

mercurial