UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2023 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "pch.h" 30 31 #include "text.h" 32 33 #include "../common/context.h" 34 #include "../common/object.h" 35 36 #include <cx/string.h> 37 #include <cx/allocator.h> 38 39 #include "util.h" 40 #include "container.h" 41 42 43 44 using namespace winrt; 45 using namespace Microsoft::UI::Xaml; 46 using namespace Microsoft::UI::Xaml::Controls; 47 using namespace Windows::UI::Xaml::Interop; 48 using namespace winrt::Windows::Foundation; 49 using namespace Microsoft::UI::Xaml::Markup; 50 using namespace Microsoft::UI::Xaml::Media; 51 using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; 52 using namespace winrt::Windows::UI::Xaml::Input; 53 54 55 UIEXPORT UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { 56 UiObject* current = uic_current_obj(obj); 57 58 // create textarea and toolkit wrapper 59 TextBox textarea = TextBox(); 60 textarea.AcceptsReturn(true); 61 ScrollViewer::SetVerticalScrollBarVisibility(textarea, ScrollBarVisibility::Auto); 62 UIElement elm = textarea; 63 UiWidget* widget = new UiWidget(elm); 64 ui_context_add_widget_destructor(current->ctx, widget); 65 ui_set_widget_groups(current->ctx, widget, args.groups); 66 67 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_TEXT); 68 if (var) { 69 UiText* value = (UiText*)var->value; 70 value->obj = widget; 71 value->undomgr = NULL; 72 value->set = ui_textarea_set; 73 value->get = ui_textarea_get; 74 value->getsubstr = ui_textarea_getsubstr; 75 value->insert = ui_textarea_insert; 76 value->setposition = ui_textarea_setposition; 77 value->position = ui_textarea_position; 78 value->selection = ui_textarea_selection; 79 value->length = ui_textarea_length; 80 value->remove = ui_textarea_remove; 81 } 82 83 // add textarea to current container 84 UI_APPLY_LAYOUT1(current, args); 85 86 current->container->Add(textarea, true); 87 88 return widget; 89 } 90 91 UIEXPORT UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) { 92 return textarea; 93 } 94 95 UIEXPORT void ui_text_undo(UiText *value) { 96 97 } 98 99 UIEXPORT void ui_text_redo(UiText *value) { 100 101 } 102 103 // -------------------------- getter/setter for textarea UiText -------------------------- 104 105 char* ui_wtext_get(UiText *text, std::wstring &value) { 106 if (text->value.ptr) { 107 text->value.free(text->value.ptr); 108 } 109 110 text->value.ptr = wchar2utf8(value.c_str(), value.length()); 111 text->value.free = free; 112 113 return text->value.ptr; 114 } 115 116 std::wstring ui_wtext_set(UiText *text, const char* value) { 117 if (text->value.ptr) { 118 text->value.free(text->value.ptr); 119 } 120 121 text->value.ptr = _strdup(value); 122 text->value.free = free; 123 124 int len; 125 wchar_t* wstr = str2wstr(value, &len); 126 std::wstring s(wstr); 127 free(wstr); 128 129 return s; 130 } 131 132 extern "C" char* ui_textarea_get(UiText *text) { 133 UiWidget* widget = (UiWidget*)text->obj; 134 TextBox box = widget->uielement.as<TextBox>(); 135 std::wstring wstr(box.Text()); 136 return ui_wtext_get(text, wstr); 137 } 138 139 extern "C" void ui_textarea_set(UiText *text, const char *newvalue) { 140 UiWidget* widget = (UiWidget*)text->obj; 141 TextBox box = widget->uielement.as<TextBox>(); 142 box.Text(ui_wtext_set(text, newvalue)); 143 } 144 145 extern "C" char* ui_textarea_getsubstr(UiText *text, int begin, int end) { 146 return NULL; 147 } 148 149 extern "C" void ui_textarea_insert(UiText *text, int pos, char *str) { 150 151 } 152 153 extern "C" void ui_textarea_setposition(UiText *text, int pos) { 154 155 } 156 157 extern "C" int ui_textarea_position(UiText *text) { 158 return 0; 159 } 160 161 extern "C" void ui_textarea_selection(UiText *text, int *begin, int *end) { 162 163 } 164 165 extern "C" int ui_textarea_length(UiText *text) { 166 return 0; 167 } 168 169 extern "C" void ui_textarea_remove(UiText *text, int begin, int end) { 170 171 } 172 173 174 175 176 UIWIDGET ui_textfield_create(UiObject* obj, UiTextFieldArgs args) { 177 UiObject* current = uic_current_obj(obj); 178 179 // create textbox and toolkit wrapper 180 TextBox textfield = TextBox(); 181 UIElement elm = textfield; 182 UiWidget* widget = new UiWidget(elm); 183 ui_context_add_widget_destructor(current->ctx, widget); 184 ui_set_widget_groups(current->ctx, widget, args.groups); 185 186 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); 187 if (var) { 188 UiString* value = (UiString*)var->value; 189 value->obj = widget; 190 value->get = ui_textfield_get; 191 value->set = ui_textfield_set; 192 193 // listener for notifying observers 194 // TODO: 195 } 196 197 // add textfield to current container 198 UI_APPLY_LAYOUT1(current, args); 199 200 current->container->Add(textfield, false); 201 202 return widget; 203 } 204 205 UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) { 206 return ui_textfield_create(obj, args); 207 } 208 209 UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) { 210 UiObject* current = uic_current_obj(obj); 211 212 // create textbox and toolkit wrapper 213 PasswordBox textfield = PasswordBox(); 214 UIElement elm = textfield; 215 UiWidget* widget = new UiWidget(elm); 216 ui_context_add_widget_destructor(current->ctx, widget); 217 ui_set_widget_groups(current->ctx, widget, args.groups); 218 219 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); 220 if (var) { 221 UiString* value = (UiString*)var->value; 222 value->obj = widget; 223 value->get = ui_passwordfield_get; 224 value->set = ui_passwordfield_set; 225 226 // listener for notifying observers 227 // TODO: 228 } 229 230 // add textfield to current container 231 UI_APPLY_LAYOUT1(current, args); 232 233 current->container->Add(textfield, false); 234 235 return widget; 236 } 237 238 239 // -------------------------- getter/setter for textfield UiString -------------------------- 240 241 char* ui_wstring_get(UiString* str, std::wstring &value) { 242 if (str->value.ptr) { 243 str->value.free(str->value.ptr); 244 } 245 246 str->value.ptr = wchar2utf8(value.c_str(), value.length()); 247 str->value.free = free; 248 249 return str->value.ptr; 250 } 251 252 std::wstring ui_wstring_set(UiString* str, const char* value) { 253 if (str->value.ptr) { 254 str->value.free(str->value.ptr); 255 } 256 257 str->value.ptr = _strdup(value); 258 str->value.free = free; 259 260 int len; 261 wchar_t* wstr = str2wstr(value, &len); 262 std::wstring s(wstr); 263 free(wstr); 264 265 return s; 266 } 267 268 char* ui_textfield_get(UiString * str) { 269 UiWidget* widget = (UiWidget*)str->obj; 270 TextBox box = widget->uielement.as<TextBox>(); 271 std::wstring wstr(box.Text()); 272 return ui_wstring_get(str, wstr); 273 } 274 275 void ui_textfield_set(UiString * str, const char* newvalue) { 276 UiWidget* widget = (UiWidget*)str->obj; 277 TextBox box = widget->uielement.as<TextBox>(); 278 box.Text(ui_wstring_set(str, newvalue)); 279 } 280 281 282 char* ui_passwordfield_get(UiString * str) { 283 UiWidget* widget = (UiWidget*)str->obj; 284 PasswordBox box = widget->uielement.as<PasswordBox>(); 285 std::wstring wstr(box.Password()); 286 return ui_wstring_get(str, wstr); 287 } 288 289 void ui_passwordfield_set(UiString * str, const char* newvalue) { 290 UiWidget* widget = (UiWidget*)str->obj; 291 PasswordBox box = widget->uielement.as<PasswordBox>(); 292 box.Password(ui_wstring_set(str, newvalue)); 293 } 294 295 296 // ------------------------ path textfield -------------------------------------- 297 298 extern "C" static void destroy_ui_pathtextfield(void* ptr) { 299 UiPathTextField* pb = (UiPathTextField*)ptr; 300 delete pb; 301 } 302 303 static void ui_context_add_pathtextfield_destructor(UiContext* ctx, UiPathTextField* pb) { 304 cxMempoolRegister(ctx->mp, pb, destroy_ui_pathtextfield); 305 } 306 307 static void ui_pathtextfield_clear(StackPanel& buttons) { 308 for (int i = buttons.Children().Size() - 1; i >= 0; i--) { 309 buttons.Children().RemoveAt(i); 310 } 311 } 312 313 static void ui_pathfield_free_pathelms(UiPathElm* elms, size_t nelm) { 314 if (!elms) { 315 return; 316 } 317 for (int i = 0; i < nelm; i++) { 318 UiPathElm e = elms[i]; 319 free(e.name); 320 free(e.path); 321 } 322 free(elms); 323 } 324 325 UiPathTextField::~UiPathTextField() { 326 ui_pathfield_free_pathelms(this->current_path, this->current_path_nelms); 327 } 328 329 static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { 330 cxstring *pathelms; 331 size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); 332 333 if (nelm == 0) { 334 *ret_nelm = 0; 335 return nullptr; 336 } 337 338 UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); 339 size_t n = nelm; 340 int j = 0; 341 for (int i = 0; i < nelm; i++) { 342 cxstring c = pathelms[i]; 343 if (c.length == 0) { 344 if (i == 0) { 345 c.length = 1; 346 } 347 else { 348 n--; 349 continue; 350 } 351 } 352 353 cxmutstr m = cx_strdup(c); 354 elms[j].name = m.ptr; 355 elms[j].name_len = m.length; 356 357 size_t elm_path_len = c.ptr + c.length - full_path; 358 cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); 359 elms[j].path = elm_path.ptr; 360 elms[j].path_len = elm_path.length; 361 362 j++; 363 } 364 *ret_nelm = n; 365 366 return elms; 367 } 368 369 int ui_pathtextfield_update(UiPathTextField* pb, const char *full_path) { 370 Grid grid = pb->grid; 371 372 ui_pathelm_func getpathelm = pb->getpathelm; 373 void* getpathelmdata = pb->getpathelmdata; 374 375 size_t full_path_len = full_path ? strlen(full_path) : 0; 376 377 size_t nelm = 0; 378 UiPathElm* path_elm = getpathelm(full_path, full_path_len, &nelm, getpathelmdata); 379 if (!path_elm) { 380 return 1; 381 } 382 383 // hide textbox, show button panel 384 pb->textbox.Visibility(Visibility::Collapsed); 385 pb->buttons.Visibility(Visibility::Visible); 386 387 // clear old buttons 388 ui_pathtextfield_clear(pb->buttons); 389 390 ui_pathfield_free_pathelms(pb->current_path, pb->current_path_nelms); 391 pb->current_path = path_elm; 392 pb->current_path_nelms = nelm; 393 394 // add new buttons 395 int j = 0; 396 for (int i = 0; i < nelm;i++) { 397 UiPathElm elm = path_elm[i]; 398 wchar_t* wstr = str2wstr_len(elm.name, elm.name_len, nullptr); 399 Button button = Button(); 400 button.Content(box_value(wstr)); 401 free(wstr); 402 403 if (pb->onactivate) { 404 button.Click([pb, j, elm](IInspectable const& sender, RoutedEventArgs) { 405 // copy elm.path because it could be a non-terminated string 406 cxmutstr elmpath = cx_strdup(cx_strn(elm.path, elm.path_len)); 407 408 UiEvent evt; 409 evt.obj = pb->obj; 410 evt.window = evt.obj->window; 411 evt.document = evt.obj->ctx->document; 412 evt.eventdata = elmpath.ptr; 413 evt.intval = j; 414 pb->onactivate(&evt, pb->onactivatedata); 415 416 free(elmpath.ptr); 417 }); 418 } 419 420 Thickness t = { 0, 0, 1, 0 }; 421 CornerRadius c = { 0 ,0, 0, 0 }; 422 button.BorderThickness(t); 423 button.CornerRadius(c); 424 425 pb->buttons.Children().Append(button); 426 427 j++; 428 } 429 430 return 0; 431 } 432 433 char* ui_path_textfield_get(UiString * str) { 434 UiPathTextField* widget = (UiPathTextField*)str->obj; 435 TextBox box = widget->textbox; 436 std::wstring wstr(box.Text()); 437 return ui_wstring_get(str, wstr); 438 } 439 440 void ui_path_textfield_set(UiString* str, const char* newvalue) { 441 UiPathTextField* widget = (UiPathTextField*)str->obj; 442 TextBox box = widget->textbox; 443 box.Text(ui_wstring_set(str, newvalue)); 444 ui_pathtextfield_update(widget, newvalue); 445 } 446 447 UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { 448 UiObject* current = uic_current_obj(obj); 449 450 // create view and toolkit wrapper 451 Border pathbar = Border(); 452 453 IInspectable bgRes = Application::Current().Resources().Lookup(box_value("TextControlBackground"L)); 454 IInspectable borderThicknessRes = Application::Current().Resources().Lookup(box_value("TextControlBorderThemeThickness"L)); 455 IInspectable borderBrushRes = Application::Current().Resources().Lookup(box_value("TextControlBorderBrush"L)); 456 // IInspectable cornerRes = Application::Current().Resources().Lookup(box_value(L"TextControlCornerRadius")); 457 458 Brush bgBrush = unbox_value<Brush>(bgRes); 459 Thickness border = unbox_value<Thickness>(borderThicknessRes); 460 Brush borderBrush = unbox_value<Brush>(borderBrushRes); 461 CornerRadius cornerRadius = { 4, 4, 4, 4 }; //unbox_value<CornerRadius>(cornerRes); 462 463 pathbar.Background(bgBrush); 464 pathbar.BorderBrush(borderBrush); 465 pathbar.BorderThickness(border); 466 pathbar.CornerRadius(cornerRadius); 467 468 Grid content = Grid(); 469 pathbar.Child(content); 470 471 GridLength gl; 472 gl.Value = 0; 473 gl.GridUnitType = GridUnitType::Auto; 474 475 ColumnDefinition coldef = ColumnDefinition(); 476 coldef.Width(gl); 477 content.ColumnDefinitions().Append(coldef); 478 479 gl.Value = 1; 480 gl.GridUnitType = GridUnitType::Star; 481 482 ColumnDefinition coldef2 = ColumnDefinition(); 483 coldef2.Width(gl); 484 content.ColumnDefinitions().Append(coldef2); 485 486 TextBox pathTextBox = TextBox(); 487 Thickness t = { 0, 0, 0, 0 }; 488 CornerRadius c = { 0 ,0, 0, 0 }; 489 pathTextBox.BorderThickness(t); 490 //pathTextBox.CornerRadius(c); 491 492 493 pathTextBox.HorizontalAlignment(HorizontalAlignment::Stretch); 494 content.SetColumn(pathTextBox, 0); 495 content.SetColumnSpan(pathTextBox, 2); 496 497 content.Children().Append(pathTextBox); 498 499 // stackpanel for buttons 500 StackPanel buttons = StackPanel(); 501 buttons.Orientation(Orientation::Horizontal); 502 buttons.Visibility(Visibility::Collapsed); 503 content.SetColumn(buttons, 0); 504 content.Children().Append(buttons); 505 506 TextBlock filler = TextBlock(); 507 filler.VerticalAlignment(VerticalAlignment::Stretch); 508 //filler.Text(winrt::hstring(L"hello filler")); 509 510 filler.HorizontalAlignment(HorizontalAlignment::Stretch); 511 filler.VerticalAlignment(VerticalAlignment::Stretch); 512 content.SetColumn(filler, 1); 513 content.Children().Append(filler); 514 515 filler.PointerPressed( 516 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( 517 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { 518 pathTextBox.Visibility(Visibility::Visible); 519 buttons.Visibility(Visibility::Collapsed); 520 filler.Visibility(Visibility::Collapsed); 521 pathTextBox.SelectionStart(pathTextBox.Text().size()); 522 pathTextBox.SelectionLength(0); 523 pathTextBox.Focus(FocusState::Keyboard); 524 }) 525 ); 526 527 //pathTextBox.Visibility(Visibility::Collapsed); 528 529 UiPathTextField* uipathbar = new UiPathTextField; 530 ui_context_add_pathtextfield_destructor(current->ctx, uipathbar); 531 uipathbar->grid = content; 532 uipathbar->buttons = buttons; 533 uipathbar->textbox = pathTextBox; 534 uipathbar->filler = filler; 535 uipathbar->obj = obj; 536 uipathbar->getpathelm = args.getpathelm ? args.getpathelm : default_pathelm_func; 537 uipathbar->getpathelmdata = args.getpathelmdata; 538 uipathbar->onactivate = args.onactivate; 539 uipathbar->onactivatedata = args.onactivatedata; 540 uipathbar->ondragstart = args.ondragstart; 541 uipathbar->ondragstartdata = args.ondragstartdata; 542 uipathbar->ondragcomplete = args.ondragcomplete; 543 uipathbar->ondragcompletedata = args.ondragcompletedata; 544 uipathbar->ondrop = args.ondrop; 545 uipathbar->ondropdata = args.ondropsdata; 546 547 548 pathTextBox.KeyDown( 549 winrt::Microsoft::UI::Xaml::Input::KeyEventHandler( 550 [=](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) { 551 auto key = e.Key(); 552 bool showButtons = false; 553 bool update = false; 554 if (key == Windows::System::VirtualKey::Escape) { 555 showButtons = true; 556 } 557 else if (key == Windows::System::VirtualKey::Enter) { 558 showButtons = true; 559 update = true; 560 } 561 562 if (showButtons) { 563 pathTextBox.Visibility(Visibility::Collapsed); 564 buttons.Visibility(Visibility::Visible); 565 filler.Visibility(Visibility::Visible); 566 if (update) { 567 std::wstring value(pathTextBox.Text()); 568 char* full_path = wchar2utf8(value.c_str(), value.length()); 569 570 if (!ui_pathtextfield_update(uipathbar, full_path)) { 571 UiEvent evt; 572 evt.obj = obj; 573 evt.window = obj->window; 574 evt.document = obj->ctx->document; 575 evt.eventdata = full_path; 576 evt.intval = -1; 577 args.onactivate(&evt, args.onactivatedata); 578 } 579 580 free(full_path); 581 } 582 583 //buttons.Focus(FocusState::Keyboard); 584 } 585 }) 586 ); 587 588 589 UIElement elm = pathbar; 590 UiWidget* widget = new UiWidget(elm); 591 widget->data1 = uipathbar; 592 ui_context_add_widget_destructor(current->ctx, widget); 593 594 // bind var 595 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); 596 if (var) { 597 UiString* value = (UiString*)var->value; 598 value->obj = uipathbar; 599 value->get = ui_path_textfield_get; 600 value->set = ui_path_textfield_set; 601 } 602 603 // add listview to current container 604 UI_APPLY_LAYOUT1(current, args); 605 606 current->container->Add(pathbar, false); 607 608 return widget; 609 } 610