1/*
2* Copyright 2016 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
8#include "tools/sk_app/CommandSet.h"
9
10#include "include/core/SkCanvas.h"
11#include "include/core/SkFont.h"
12
13namespace sk_app {
14
15CommandSet::CommandSet()
16    : fHelpMode(kNone_HelpMode) {
17    this->addCommand('h', "Overlays", "Show help screen", [this]() {
18        switch (this->fHelpMode) {
19            case kNone_HelpMode:
20                this->fHelpMode = kGrouped_HelpMode;
21                break;
22            case kGrouped_HelpMode:
23                this->fHelpMode = kAlphabetical_HelpMode;
24                break;
25            case kAlphabetical_HelpMode:
26                this->fHelpMode = kNone_HelpMode;
27                break;
28        }
29        fWindow->inval();
30    });
31}
32
33void CommandSet::attach(Window* window) {
34    fWindow = window;
35}
36
37bool CommandSet::onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) {
38    if (skui::InputState::kDown == state) {
39        for (Command& cmd : fCommands) {
40            if (Command::kKey_CommandType == cmd.fType && key == cmd.fKey) {
41                cmd.fFunction();
42                return true;
43            }
44        }
45    }
46
47    return false;
48}
49
50bool CommandSet::onChar(SkUnichar c, skui::ModifierKey modifiers) {
51    for (Command& cmd : fCommands) {
52        if (Command::kChar_CommandType == cmd.fType && c == cmd.fChar) {
53            cmd.fFunction();
54            return true;
55        }
56    }
57
58    return false;
59}
60
61bool CommandSet::onSoftkey(const SkString& softkey) {
62    for (const Command& cmd : fCommands) {
63        if (cmd.getSoftkeyString().equals(softkey)) {
64            cmd.fFunction();
65            return true;
66        }
67    }
68    return false;
69}
70
71void CommandSet::addCommand(SkUnichar c, const char* group, const char* description,
72                            std::function<void(void)> function) {
73    fCommands.push_back(Command(c, group, description, function));
74}
75
76void CommandSet::addCommand(skui::Key k, const char* keyName, const char* group,
77                            const char* description, std::function<void(void)> function) {
78    fCommands.push_back(Command(k, keyName, group, description, function));
79}
80
81#if defined(SK_BUILD_FOR_WIN)
82    #define SK_strcasecmp   _stricmp
83#else
84    #define SK_strcasecmp   strcasecmp
85#endif
86
87bool CommandSet::compareCommandKey(const Command& first, const Command& second) {
88    return SK_strcasecmp(first.fKeyName.c_str(), second.fKeyName.c_str()) < 0;
89}
90
91bool CommandSet::compareCommandGroup(const Command& first, const Command& second) {
92    return SK_strcasecmp(first.fGroup.c_str(), second.fGroup.c_str()) < 0;
93}
94
95void CommandSet::drawHelp(SkCanvas* canvas) {
96    if (kNone_HelpMode == fHelpMode) {
97        return;
98    }
99
100    // Sort commands for current mode:
101    std::stable_sort(fCommands.begin(), fCommands.end(),
102                     kAlphabetical_HelpMode == fHelpMode ? compareCommandKey : compareCommandGroup);
103
104    SkFont font;
105    font.setSize(16);
106
107    SkFont groupFont;
108    groupFont.setSize(18);
109
110    SkPaint bgPaint;
111    bgPaint.setColor(0xC0000000);
112    canvas->drawPaint(bgPaint);
113
114    SkPaint paint;
115    paint.setAntiAlias(true);
116    paint.setColor(0xFFFFFFFF);
117
118    SkPaint groupPaint;
119    groupPaint.setAntiAlias(true);
120    groupPaint.setColor(0xFFFFFFFF);
121
122    SkScalar x = SkIntToScalar(10);
123    SkScalar y = SkIntToScalar(10);
124
125    // Measure all key strings:
126    SkScalar keyWidth = 0;
127    for (Command& cmd : fCommands) {
128        keyWidth = std::max(keyWidth,
129                               font.measureText(cmd.fKeyName.c_str(), cmd.fKeyName.size(),
130                                                SkTextEncoding::kUTF8));
131    }
132    keyWidth += font.measureText(" ", 1, SkTextEncoding::kUTF8);
133
134    // If we're grouping by category, we'll be adding text height on every new group (including the
135    // first), so no need to do that here. Otherwise, skip down so the first line is where we want.
136    if (kGrouped_HelpMode != fHelpMode) {
137        y += font.getSize();
138    }
139
140    // Print everything:
141    SkString lastGroup;
142    for (Command& cmd : fCommands) {
143        if (kGrouped_HelpMode == fHelpMode && lastGroup != cmd.fGroup) {
144            // Group change. Advance and print header:
145            y += font.getSize();
146            canvas->drawSimpleText(cmd.fGroup.c_str(), cmd.fGroup.size(), SkTextEncoding::kUTF8,
147                                   x, y, groupFont, groupPaint);
148            y += groupFont.getSize() + 2;
149            lastGroup = cmd.fGroup;
150        }
151
152        canvas->drawSimpleText(cmd.fKeyName.c_str(), cmd.fKeyName.size(), SkTextEncoding::kUTF8,
153                               x, y, font, paint);
154        SkString text = SkStringPrintf(": %s", cmd.fDescription.c_str());
155        canvas->drawString(text, x + keyWidth, y, font, paint);
156        y += font.getSize() + 2;
157    }
158}
159
160std::vector<SkString> CommandSet::getCommandsAsSoftkeys() const {
161    std::vector<SkString> result;
162    for(const Command& command : fCommands) {
163        result.push_back(command.getSoftkeyString());
164    }
165    return result;
166}
167
168}   // namespace sk_app
169