on_default_filter
()Filter and validation callbacks serve different purposes. Filter callbacks get invoked as part of the input field's editing process, and validation callbacks get invoked at the conclusion of the editing process. The input filter example in Chapter 8, Filtered input fields accepts only digits as input, but it has no means of blocking tabbing out of the input field after typing in fewer than the requisite ten digits.
/* ** Copyright 2019-2021 Double Precision, Inc. ** See COPYING for distribution information. */ #include "config.h" #include "close_flag.H" #include <x/exception.H> #include <x/destroy_callback.H> #include <x/appid.H> #include <x/w/main_window.H> #include <x/w/gridlayoutmanager.H> #include <x/w/gridfactory.H> #include <x/w/label.H> #include <x/w/text_param_literals.H> #include <x/w/input_field.H> #include <x/w/canvas.H> #include <x/w/container.H> #include <x/w/button.H> #include <string> #include <iostream> #include <sstream> #include <optional> #include <algorithm> std::string x::appid() noexcept { return "filteredinput2.examples.w.libcxx.com"; } x::w::validated_input_field<std::string> create_mainwindow(const x::w::main_window &main_window, const close_flag_ref &close_flag) { auto layout=main_window->gridlayout(); layout->row_alignment(0, x::w::valign::middle); x::w::gridfactory factory=layout->append_row(); factory->create_label("Phone #:"); x::w::input_field_config config{15}; config.maximum_size=14; config.autoselect=true; config.autodeselect=true; config.direction=x::w::bidi::left_to_right; auto field=factory->create_input_field("(###) ###-####", config); field->on_default_filter(// Valid input is digits 0-9. [] (char32_t c) { return c >= '0' && c <= '9'; }, // Immutable positions in the input field. {0, 4, 5, 9}, // Character that indicates no input. '#'); factory=layout->append_row(); factory->create_canvas(); factory->create_button("Ok", x::w::default_button() ) ->on_activate([close_flag] (ONLY IN_THREAD, const auto &trigger, const auto &mcguffin) { close_flag->close(); }); // An existing input field's set_validator() is equivalent to // using create_validated_input_field_contents() for // a new input field. set_validator() has the effect of converting // a regular input field into a validated input field. // // This is less efficient, but sometimes it's not convenient to // create the validator closures until after all widgets in the window // get created. // // set_validator() takes the same parameters as // create_validated_input_field_contents() and returns a // x::w::validated_input_field<type>. // // There's also set_string_validator() that's comparable to using // create_string_validated_input_field_contents(). return field->set_validator ([] (ONLY IN_THREAD, const std::string &value, x::w::input_lock &lock, const x::w::callback_trigger_t &trigger) -> std::optional<std::string> { std::string s; s.reserve(10); for (auto c:value) { if (c >= '0' && c <= '9') s.push_back(c); } if (s.size() > 0 && s.size() < 10) { lock.stop_message("Invalid phone number"); return std::nullopt; } return s; }, [] (const std::optional<std::string> &v) -> std::string { std::string s="(###) ###-####"; auto i=s.begin(); if (v && v->size() == 10) { for (char c:*v) { while (*i != '#') ++i; *i++ = c; } } return s; }); } void filteredinputfield() { x::destroy_callback::base::guard guard; auto close_flag=close_flag_ref::create(); x::w::validated_input_fieldptr<char> char_input; x::w::validated_input_fieldptr<int> int_input; x::w::validated_input_fieldptr<std::string> validated_phone_number; auto main_window=x::w::main_window::create ([&] (const auto &main_window) { validated_phone_number= create_mainwindow(main_window, close_flag); validated_phone_number->set("2125551212"); }); main_window->on_disconnect([] { _exit(1); }); guard(main_window->connection_mcguffin()); main_window->set_window_title("Prompt"); main_window->on_delete ([close_flag] (ONLY IN_THREAD, const x::w::busy &ignore) { close_flag->close(); }); main_window->show_all(); close_flag->wait(); auto phone_number=validated_phone_number->value(); if (phone_number) std::cout << "Phone number: " << *phone_number << std::endl; } int main(int argc, char **argv) { try { filteredinputfield(); } catch (const x::exception &e) { e->caught(); exit(1); } return 0; }
on_default_filter
() uses
on_filter
() to install an input filter with
behavior similar to
what filteredinput.C
implements.
filteredinput2.C
implements an input field
for entering a phone number in the US telephone number format, with
hashes serving as placeholders for the ten digits. A
validation callback checks that
all ten digits of the phone number got typed in, and provides a validated
phone number string containing just the ten digits, with the punctuation
stripped off.
on_default_filter
() requirements
on_default_filter
() takes the following
parameters:
A closure or a lambda that determines whether a single Unicode character is acceptable input.
A std::vector<size_t>
that specifies
the indexes of the immutable characters in the input field.
These immutable characters get skipped in the input field and
cannot be modified.
filteredinput2.C
passes in a vector that
lists the parenthesis, the space, and the dash characters' indexes.
The placeholder character for an empty character position. This defaults to a space character.
on_default_filter
() imposes additional
requirements on the input field:
The initial contents of the input field should be set to a text string with the same number of Unicode characters as the input field's defined maximum size.
All characters except the immutable ones should be set to the empty placeholder value.
An input field with an
on_default_filter
() is effectively a
fixed-sized field. New text gets added to the input field replacing
the empty placeholder characters, and removed text gets replaced
with placeholder characters.
filteredinput2.C
uses “#” as
empty placeholders where phone number digits go. They get replaced
by the typed-in digits, and deleting the phone number's digits
replaces them with “#”s.