#include "pch.h" #include "app.h" #include "log.h" #include "node_text_input.h" #include "node_border.h" Node* NodeTextInput::clone_instantiate() const { return new NodeTextInput(); } void NodeTextInput::on_tick(float dt) { timer += dt; bool focus = root()->current_key_capture.get() == this; if (!focus) m_cursor_visible = false; m_thinkness = focus; if (timer > 1.0) { timer = 0; if (focus) { m_cursor_visible = !m_cursor_visible; app_redraw(); } } } void NodeTextInput::clone_copy(Node* dest) const { NodeBorder::clone_copy(dest); NodeTextInput* n = static_cast(dest); n->m_text_mesh.max_width = m_text_mesh.max_width; n->m_text_mesh.create(); n->m_text_mesh.update(font_id, m_text); n->m_text = m_text; n->m_font = m_font; n->m_color = m_color; n->m_font_size = m_font_size; n->font_id = font_id; n->m_multiline = m_multiline; n->m_off = m_off; n->m_text_align_v = m_text_align_v; n->m_text_align_h = m_text_align_h; } kEventResult NodeTextInput::handle_event(Event* e) { KeyEvent* ke = (KeyEvent*)e; switch (e->m_type) { case kEventType::MouseDownL: App::I->showKeyboard(); break; case kEventType::MouseUpL: key_capture(); timer = 0; m_cursor_visible = true; break; case kEventType::KeyDown: if (!has_focus()) return kEventResult::Available; //switch (ke->m_key) //{ //case VK_BACK: // m_string.erase(m_string.end() - 1); // m_text->set_text(m_string); // break; //default: // break; //} break; case kEventType::KeyUp: if (!has_focus()) return kEventResult::Available; if (ke->m_key == kKey::KeyEnter && on_return) on_return(this); if (ke->m_key == kKey::KeyBackspace) { if (!m_text.empty()) { m_text.erase(m_text.end() - 1); set_text(m_text); } } break; case kEventType::KeyChar: if (!has_focus()) return kEventResult::Available; timer = 0; m_cursor_visible = true; if (ke->m_char == '\b' // backspace || ke->m_char == 0x7f // DEL ) { if (!m_text.empty()) { m_text.erase(m_text.end() - 1); set_text(m_text); } } else if (ke->m_char == '\n' || ke->m_char == '\r') // enter/return { if (m_multiline) { m_text += '\n'; set_text(m_text); } else if (on_return) { on_return(this); } } else if ((uint8_t)ke->m_char >= 32 && (uint8_t)ke->m_char < 256) // visible ascii character { m_text += (char)ke->m_char; set_text(m_text); } break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } void NodeTextInput::set_text(const std::string& s) { m_text = s; m_text_mesh.update(font_id, s); update_layout(); } void NodeTextInput::set_text_format(const char* fmt, ...) { static char buffer[4096]; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); m_text = buffer; m_text_mesh.update(font_id, buffer); update_layout(); } void NodeTextInput::update_layout() { auto pad = GetPadding(); float h = GetHeight() - (pad[1] + pad[3]); float w = GetWidth() - (pad[0] + pad[2]); if (m_text_align_v == TextAlign::Begin) m_off.y = pad[0]; else if (m_text_align_v == TextAlign::Center) m_off.y = (h - m_text_mesh.bb.y) * 0.5f + pad[0]; else m_off.y = (h - m_text_mesh.bb.y) + pad[0]; if (m_text_align_h == TextAlign::Begin) m_off.x = pad[3]; else if (m_text_align_v == TextAlign::Center) m_off.x = (w - m_text_mesh.bb.x) * 0.5f + pad[3]; else m_off.x = (w - m_text_mesh.bb.x) + pad[3]; } bool NodeTextInput::has_focus() noexcept { return root()->current_key_capture.get() == this; } void NodeTextInput::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) { auto pad = GetPadding(); m_text_mesh.max_width = m_multiline ? new_size.x - (pad[1] + pad[3]) : 0; update_layout(); } void NodeTextInput::create() { NodeBorder::create(); if (!m_font.empty()) { font_id = fmt::format("{}-{}", m_font, m_font_size); m_text_mesh.create(); m_text_mesh.update(font_id, m_text); update_layout(); } } void NodeTextInput::draw() { NodeBorder::draw(); glm::mat4 pos = glm::translate(glm::vec3(glm::floor(m_pos + m_off), 0)); ShaderManager::use(kShader::Font); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * pos); ShaderManager::u_vec4(kShaderUniform::Col, m_color); glEnable(GL_BLEND); m_text_mesh.draw(); glDisable(GL_BLEND); if (m_cursor_visible) { glDisable(GL_BLEND); glm::mat4 cur_pos = glm::translate(glm::vec3(m_pos + m_off + xy(m_text_mesh.cur_box), 0)); glm::mat4 cur_scale = glm::scale(glm::vec3(zw(m_text_mesh.cur_box), 1)); glm::mat4 cur_pivot = glm::translate(glm::vec3(0.5, 0.5, 0)); ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * cur_pos * cur_scale * cur_pivot); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); m_plane.draw_fill(); } } void NodeTextInput::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) { switch (ka) { case kAttribute::Multiline: m_multiline = attr->BoolValue(); break; case kAttribute::TextAlign: if (strcmp(attr->Value(), "left") == 0) m_text_align_h = TextAlign::Begin; else if (strcmp(attr->Value(), "center") == 0) m_text_align_h = TextAlign::Center; else if (strcmp(attr->Value(), "right") == 0) m_text_align_h = TextAlign::End; break; case kAttribute::TextVerticalAlign: if (strcmp(attr->Value(), "top") == 0) m_text_align_v = TextAlign::Begin; else if (strcmp(attr->Value(), "center") == 0) m_text_align_v = TextAlign::Center; else if (strcmp(attr->Value(), "bottom") == 0) m_text_align_v = TextAlign::End; break; default: NodeBorder::parse_attributes(ka, attr); } } void NodeTextInput::destroy() { if (root()->current_key_capture.get() == this) App::I->hideKeyboard(); NodeBorder::destroy(); }