Fri, 16 Feb 2024 19:37:51 +0100
port textfield to new API (GTK)
/* * 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 "text.h" #include "../common/context.h" #include "../common/object.h" #include <cx/string.h> #include <cx/allocator.h> #include "util.h" #include "container.h" using namespace winrt; using namespace Microsoft::UI::Xaml; using namespace Microsoft::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Interop; using namespace winrt::Windows::Foundation; using namespace Microsoft::UI::Xaml::Markup; using namespace Microsoft::UI::Xaml::Media; using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; using namespace winrt::Windows::UI::Xaml::Input; UIWIDGET ui_textfield_create(UiObject* obj, UiTextFieldArgs args) { UiObject* current = uic_current_obj(obj); // create textbox and toolkit wrapper TextBox textfield = TextBox(); UIElement elm = textfield; UiWidget* widget = new UiWidget(elm); ui_context_add_widget_destructor(current->ctx, widget); UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); if (var) { UiString* value = (UiString*)var->value; value->obj = widget; value->get = ui_textfield_get; value->set = ui_textfield_set; // listener for notifying observers // TODO: } // add button to current container UI_APPLY_LAYOUT1(current, args); current->container->Add(textfield, false); return widget; } UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) { return ui_textfield_create(obj, args); } UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) { UiObject* current = uic_current_obj(obj); // create textbox and toolkit wrapper PasswordBox textfield = PasswordBox(); UIElement elm = textfield; UiWidget* widget = new UiWidget(elm); ui_context_add_widget_destructor(current->ctx, widget); UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); if (var) { UiString* value = (UiString*)var->value; value->obj = widget; value->get = ui_passwordfield_get; value->set = ui_passwordfield_set; // listener for notifying observers // TODO: } // add button to current container UI_APPLY_LAYOUT1(current, args); current->container->Add(textfield, false); return widget; } // -------------------------- getter/setter for textfield UiString -------------------------- char* ui_wstring_get(UiString* str, std::wstring &value) { if (str->value.ptr) { str->value.free(str->value.ptr); } str->value.ptr = wchar2utf8(value.c_str(), value.length()); str->value.free = free; return str->value.ptr; } std::wstring ui_wstring_set(UiString* str, const char* value) { if (str->value.ptr) { str->value.free(str->value.ptr); } str->value.ptr = _strdup(value); str->value.free = free; int len; wchar_t* wstr = str2wstr(value, &len); std::wstring s(wstr); free(wstr); return s; } char* ui_textfield_get(UiString * str) { UiWidget* widget = (UiWidget*)str->obj; TextBox box = widget->uielement.as<TextBox>(); std::wstring wstr(box.Text()); return ui_wstring_get(str, wstr); } void ui_textfield_set(UiString * str, const char* newvalue) { UiWidget* widget = (UiWidget*)str->obj; TextBox box = widget->uielement.as<TextBox>(); box.Text(ui_wstring_set(str, newvalue)); } char* ui_passwordfield_get(UiString * str) { UiWidget* widget = (UiWidget*)str->obj; PasswordBox box = widget->uielement.as<PasswordBox>(); std::wstring wstr(box.Password()); return ui_wstring_get(str, wstr); } void ui_passwordfield_set(UiString * str, const char* newvalue) { UiWidget* widget = (UiWidget*)str->obj; PasswordBox box = widget->uielement.as<PasswordBox>(); box.Password(ui_wstring_set(str, newvalue)); } // ------------------------ path textfield -------------------------------------- extern "C" static void destroy_ui_pathtextfield(void* ptr) { UiPathTextField* pb = (UiPathTextField*)ptr; delete pb; } static void ui_context_add_pathtextfield_destructor(UiContext* ctx, UiPathTextField* pb) { cxMempoolRegister(ctx->mp, pb, destroy_ui_pathtextfield); } static void ui_pathtextfield_clear(StackPanel& buttons) { for (int i = buttons.Children().Size() - 1; i >= 0; i--) { buttons.Children().RemoveAt(i); } } static void ui_pathfield_free_pathelms(UiPathElm* elms, size_t nelm) { if (!elms) { return; } for (int i = 0; i < nelm; i++) { UiPathElm e = elms[i]; free(e.name); free(e.path); } free(elms); } UiPathTextField::~UiPathTextField() { ui_pathfield_free_pathelms(this->current_path, this->current_path_nelms); } static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { cxstring *pathelms; size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); if (nelm == 0) { *ret_nelm = 0; return nullptr; } UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); size_t n = nelm; int j = 0; for (int i = 0; i < nelm; i++) { cxstring c = pathelms[i]; if (c.length == 0) { if (i == 0) { c.length = 1; } else { n--; continue; } } cxmutstr m = cx_strdup(c); elms[j].name = m.ptr; elms[j].name_len = m.length; size_t elm_path_len = c.ptr + c.length - full_path; cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); elms[j].path = elm_path.ptr; elms[j].path_len = elm_path.length; j++; } *ret_nelm = n; return elms; } int ui_pathtextfield_update(UiPathTextField* pb, const char *full_path) { Grid grid = pb->grid; ui_pathelm_func getpathelm = pb->getpathelm; void* getpathelmdata = pb->getpathelmdata; size_t full_path_len = full_path ? strlen(full_path) : 0; size_t nelm = 0; UiPathElm* path_elm = getpathelm(full_path, full_path_len, &nelm, getpathelmdata); if (!path_elm) { return 1; } // hide textbox, show button panel pb->textbox.Visibility(Visibility::Collapsed); pb->buttons.Visibility(Visibility::Visible); // clear old buttons ui_pathtextfield_clear(pb->buttons); ui_pathfield_free_pathelms(pb->current_path, pb->current_path_nelms); pb->current_path = path_elm; pb->current_path_nelms = nelm; // add new buttons int j = 0; for (int i = 0; i < nelm;i++) { UiPathElm elm = path_elm[i]; wchar_t* wstr = str2wstr_len(elm.name, elm.name_len, nullptr); Button button = Button(); button.Content(box_value(wstr)); free(wstr); if (pb->onactivate) { button.Click([pb, j, elm](IInspectable const& sender, RoutedEventArgs) { // copy elm.path because it could be a non-terminated string cxmutstr elmpath = cx_strdup(cx_strn(elm.path, elm.path_len)); UiEvent evt; evt.obj = pb->obj; evt.window = evt.obj->window; evt.document = evt.obj->ctx->document; evt.eventdata = elmpath.ptr; evt.intval = j; pb->onactivate(&evt, pb->onactivatedata); free(elmpath.ptr); }); } Thickness t = { 0, 0, 1, 0 }; CornerRadius c = { 0 ,0, 0, 0 }; button.BorderThickness(t); button.CornerRadius(c); pb->buttons.Children().Append(button); j++; } return 0; } char* ui_path_textfield_get(UiString * str) { UiPathTextField* widget = (UiPathTextField*)str->obj; TextBox box = widget->textbox; std::wstring wstr(box.Text()); return ui_wstring_get(str, wstr); } void ui_path_textfield_set(UiString* str, const char* newvalue) { UiPathTextField* widget = (UiPathTextField*)str->obj; TextBox box = widget->textbox; box.Text(ui_wstring_set(str, newvalue)); ui_pathtextfield_update(widget, newvalue); } UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { UiObject* current = uic_current_obj(obj); // create view and toolkit wrapper Border pathbar = Border(); IInspectable bgRes = Application::Current().Resources().Lookup(box_value(L"TextControlBackground")); IInspectable borderThicknessRes = Application::Current().Resources().Lookup(box_value(L"TextControlBorderThemeThickness")); IInspectable borderBrushRes = Application::Current().Resources().Lookup(box_value(L"TextControlBorderBrush")); // IInspectable cornerRes = Application::Current().Resources().Lookup(box_value(L"TextControlCornerRadius")); Brush bgBrush = unbox_value<Brush>(bgRes); Thickness border = unbox_value<Thickness>(borderThicknessRes); Brush borderBrush = unbox_value<Brush>(borderBrushRes); CornerRadius cornerRadius = { 4, 4, 4, 4 }; //unbox_value<CornerRadius>(cornerRes); pathbar.Background(bgBrush); pathbar.BorderBrush(borderBrush); pathbar.BorderThickness(border); pathbar.CornerRadius(cornerRadius); Grid content = Grid(); pathbar.Child(content); GridLength gl; gl.Value = 0; gl.GridUnitType = GridUnitType::Auto; ColumnDefinition coldef = ColumnDefinition(); coldef.Width(gl); content.ColumnDefinitions().Append(coldef); gl.Value = 1; gl.GridUnitType = GridUnitType::Star; ColumnDefinition coldef2 = ColumnDefinition(); coldef2.Width(gl); content.ColumnDefinitions().Append(coldef2); TextBox pathTextBox = TextBox(); Thickness t = { 0, 0, 0, 0 }; CornerRadius c = { 0 ,0, 0, 0 }; pathTextBox.BorderThickness(t); //pathTextBox.CornerRadius(c); pathTextBox.HorizontalAlignment(HorizontalAlignment::Stretch); content.SetColumn(pathTextBox, 0); content.SetColumnSpan(pathTextBox, 2); content.Children().Append(pathTextBox); // stackpanel for buttons StackPanel buttons = StackPanel(); buttons.Orientation(Orientation::Horizontal); buttons.Visibility(Visibility::Collapsed); content.SetColumn(buttons, 0); content.Children().Append(buttons); TextBlock filler = TextBlock(); filler.VerticalAlignment(VerticalAlignment::Stretch); //filler.Text(winrt::hstring(L"hello filler")); filler.HorizontalAlignment(HorizontalAlignment::Stretch); filler.VerticalAlignment(VerticalAlignment::Stretch); content.SetColumn(filler, 1); content.Children().Append(filler); filler.PointerPressed( winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { pathTextBox.Visibility(Visibility::Visible); buttons.Visibility(Visibility::Collapsed); filler.Visibility(Visibility::Collapsed); pathTextBox.SelectionStart(pathTextBox.Text().size()); pathTextBox.SelectionLength(0); pathTextBox.Focus(FocusState::Keyboard); }) ); //pathTextBox.Visibility(Visibility::Collapsed); UiPathTextField* uipathbar = new UiPathTextField; ui_context_add_pathtextfield_destructor(current->ctx, uipathbar); uipathbar->grid = content; uipathbar->buttons = buttons; uipathbar->textbox = pathTextBox; uipathbar->filler = filler; uipathbar->obj = obj; uipathbar->getpathelm = args.getpathelm ? args.getpathelm : default_pathelm_func; uipathbar->getpathelmdata = args.getpathelmdata; uipathbar->onactivate = args.onactivate; uipathbar->onactivatedata = args.onactivatedata; uipathbar->ondragstart = args.ondragstart; uipathbar->ondragstartdata = args.ondragstartdata; uipathbar->ondragcomplete = args.ondragcomplete; uipathbar->ondragcompletedata = args.ondragcompletedata; uipathbar->ondrop = args.ondrop; uipathbar->ondropdata = args.ondropsdata; pathTextBox.KeyDown( winrt::Microsoft::UI::Xaml::Input::KeyEventHandler( [=](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) { auto key = e.Key(); bool showButtons = false; bool update = false; if (key == Windows::System::VirtualKey::Escape) { showButtons = true; } else if (key == Windows::System::VirtualKey::Enter) { showButtons = true; update = true; } if (showButtons) { pathTextBox.Visibility(Visibility::Collapsed); buttons.Visibility(Visibility::Visible); filler.Visibility(Visibility::Visible); if (update) { std::wstring value(pathTextBox.Text()); char* full_path = wchar2utf8(value.c_str(), value.length()); if (!ui_pathtextfield_update(uipathbar, full_path)) { UiEvent evt; evt.obj = obj; evt.window = obj->window; evt.document = obj->ctx->document; evt.eventdata = full_path; evt.intval = -1; args.onactivate(&evt, args.onactivatedata); } free(full_path); } //buttons.Focus(FocusState::Keyboard); } }) ); UIElement elm = pathbar; UiWidget* widget = new UiWidget(elm); widget->data1 = uipathbar; ui_context_add_widget_destructor(current->ctx, widget); // bind var UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); if (var) { UiString* value = (UiString*)var->value; value->obj = uipathbar; value->get = ui_path_textfield_get; value->set = ui_path_textfield_set; } // add listview to current container UI_APPLY_LAYOUT1(current, args); current->container->Add(pathbar, false); return widget; }