aboutsummaryrefslogtreecommitdiff
path: root/src/core/frontend/applets/swkbd.h
blob: 82a1a2edb4026c6693408c8b30187a1d85a9ecb9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <utility>
#include <vector>
#include "common/assert.h"

namespace Core {
class System;
}

namespace Frontend {

enum class AcceptedInput {
    Anything,            /// All inputs are accepted.
    NotEmpty,            /// Empty inputs are not accepted.
    NotEmptyAndNotBlank, /// Empty or blank inputs (consisting solely of whitespace) are not
                         /// accepted.
    NotBlank,    /// Blank inputs (consisting solely of whitespace) are not accepted, but empty
                 /// inputs are.
    FixedLength, /// The input must have a fixed length (specified by maxTextLength in
                 /// swkbdInit).
};

enum class ButtonConfig {
    Single, /// Ok button
    Dual,   /// Cancel | Ok buttons
    Triple, /// Cancel | I Forgot | Ok buttons
    None,   /// No button (returned by swkbdInputText in special cases)
};

/// Default English button text mappings. Frontends may need to copy this to internationalize it.
constexpr char SWKBD_BUTTON_OKAY[] = "Ok";
constexpr char SWKBD_BUTTON_CANCEL[] = "Cancel";
constexpr char SWKBD_BUTTON_FORGOT[] = "I Forgot";

/// Configuration thats relevent to frontend implementation of applets. Anything missing that we
/// later learn is needed can be added here and filled in by the backend HLE applet
struct KeyboardConfig {
    ButtonConfig button_config;
    AcceptedInput accept_mode; /// What kinds of input are accepted (blank/empty/fixed width)
    bool multiline_mode;       /// True if the keyboard accepts multiple lines of input
    u16 max_text_length;       /// Maximum number of letters allowed if its a text input
    u16 max_digits;            /// Maximum number of numbers allowed if its a number input
    std::string hint_text;     /// Displayed in the field as a hint before
    std::vector<std::string> button_text; /// Contains the button text that the caller provides
    struct Filters {
        bool prevent_digit;     /// Limit maximum digit count to max_digits
        bool prevent_at;        /// Disallow the use of the @ sign.
        bool prevent_percent;   /// Disallow the use of the % sign.
        bool prevent_backslash; /// Disallow the use of the \ sign.
        bool prevent_profanity; /// Disallow profanity using Nintendo's profanity filter.
        bool enable_callback;   /// Use a callback in order to check the input.
    };
    Filters filters;
};

struct KeyboardData {
    std::string text;
    u8 button{};
};

enum class ValidationError {
    None,
    // Button Selection
    ButtonOutOfRange,
    // Configured Filters
    MaxDigitsExceeded,
    AtSignNotAllowed,
    PercentNotAllowed,
    BackslashNotAllowed,
    ProfanityNotAllowed,
    CallbackFailed,
    // Allowed Input Type
    FixedLengthRequired,
    MaxLengthExceeded,
    BlankInputNotAllowed,
    EmptyInputNotAllowed,
};

class SoftwareKeyboard {
public:
    virtual ~SoftwareKeyboard() = default;

    /**
     * Executes the software keyboard, configured with the given parameters.
     */
    virtual void Execute(const KeyboardConfig& config_) {
        config = config_;
    }

    /**
     * Whether the result data is ready to be received.
     */
    bool DataReady() const;

    /**
     * Receives the current result data stored in the applet, and clears the ready state.
     */
    const KeyboardData& ReceiveData();

    /**
     * Shows an error text returned by the callback.
     */
    virtual void ShowError(const std::string& error) = 0;

    /**
     * Validates if the provided string breaks any of the filter rules. This is meant to be called
     * whenever the user input changes to check to see if the new input is valid. Frontends can
     * decide if they want to check the input continuously or once before submission
     */
    ValidationError ValidateFilters(const std::string& input) const;

    /**
     * Validates the the provided string doesn't break any extra rules like "input must not be
     * empty". This will be called by Finalize but can be called earlier if the frontend needs
     */
    ValidationError ValidateInput(const std::string& input) const;

    /**
     * Verifies that the selected button is valid. This should be used as the last check before
     * closing.
     */
    ValidationError ValidateButton(u8 button) const;

    /**
     * Runs all validation phases. If successful, stores the data so that the HLE applet in core can
     * send this to the calling application
     */
    ValidationError Finalize(const std::string& text, u8 button);

protected:
    KeyboardConfig config;
    KeyboardData data;

    bool data_ready = false;
};

class DefaultKeyboard final : public SoftwareKeyboard {
public:
    explicit DefaultKeyboard(Core::System& system_);
    void Execute(const KeyboardConfig& config) override;
    void ShowError(const std::string& error) override;

private:
    Core::System& system;
};

} // namespace Frontend