// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz // http://chaosforge.org/ // // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/gui/gui_environment.hh" #include "nv/gui/gui_renderer.hh" /* TODO: parse a lua stylesheet as per Trac wiki IDEA: Store everything in std::unordered_maps, with lua_value's? A lua_value is a variant stores strings as const char* that deletes them on destructor? Question is that there might not be much gained on speed anyway, due to Lua's speed. Special function field allows delayed per parse execution? */ #include "nv/gfx/sliced_buffer.hh" #include "nv/gfx/texture_atlas.hh" nv::gui::environment::environment( window* w, const std::string& shader_path ) : m_renderer( nullptr ), m_window( w ), m_first_free(-1), m_last_free(-1) { m_area.dim( dimension( w->get_width(), w->get_height() ) ); m_screen = create_handle(); element* screen = get_element( m_screen ); screen->m_absolute = m_area; screen->m_relative = m_area; m_renderer = new renderer( w, shader_path ); } void nv::gui::environment::load_style( const std::string& filename ) { m_renderer->load_style( filename ); } nv::gui::handle nv::gui::environment::create_element( const rectangle& r ) { return create_element( m_screen, r ); } nv::gui::handle nv::gui::environment::create_element( handle parent, const rectangle& r ) { if ( parent.is_nil() ) parent = m_screen; handle result = create_handle(); element* e = get_element( result ); e->m_absolute = r; e->m_relative = r; add_child( parent, result ); return result; } nv::gui::handle nv::gui::environment::create_handle() { m_elements.emplace_back(); m_elements.back().m_this = create_index( uint16( m_elements.size() - 1 ) ); return m_elements.back().m_this; } nv::gui::handle nv::gui::environment::create_index( sint16 element_id ) { uint16 i = get_free_index(); index& idx = m_indices[i]; idx.element_id = element_id; idx.counter++; return handle( i, idx.counter ); } nv::uint16 nv::gui::environment::get_free_index() { if ( m_first_free != -1 ) { uint16 result = (uint16)m_first_free; m_first_free = m_indices[result].next_free; m_indices[result].next_free = -1; if ( m_first_free == -1 ) m_last_free = -1; return result; } m_indices.emplace_back(); return uint16( m_indices.size() - 1 ); } void nv::gui::environment::free_index( uint16 idx_index ) { // TODO: reuse index& idx = m_indices[ idx_index ]; idx.element_id = -1; idx.next_free = -1; if ( m_last_free == -1 ) { m_first_free = m_last_free = idx_index; } else { m_indices[ m_last_free ].next_free = idx_index; m_last_free = idx_index; } } void nv::gui::environment::destroy_element( handle e ) { element* dead_element = get_element( e ); if ( dead_element == nullptr ) return; destroy_children( e ); remove_child( dead_element->m_parent, e ); delete dead_element->m_render_data; dead_element->m_render_data = nullptr; dead_element->m_parent = handle(); uint16 dead_index = m_indices[ e.m_index ].element_id; if ( dead_index != m_elements.size()-1 ) { m_elements[ dead_index ] = m_elements.back(); m_elements.pop_back(); m_indices[ m_elements[ dead_index ].m_this.m_index ].element_id = dead_index; } else m_elements.pop_back(); free_index( e.m_index ); } void nv::gui::environment::update( handle e, uint32 elapsed ) { element* el = get_element( e ); if ( !el ) return; // el->on_update( elapsed ); if ( el->m_visible ) { for ( handle i : el->m_children ) { update( i, elapsed ); } } if ( el->m_dirty || el->m_render_data == nullptr ) { m_renderer->redraw( el, elapsed ); el->m_dirty = false; } } void nv::gui::environment::draw( handle e ) { element* el = get_element( e ); if ( !el ) return; if ( el->m_visible ) { // el->on_draw(); m_renderer->draw( el ); for ( handle i : el->m_children ) { draw(i); } } } void nv::gui::environment::update() { update( m_screen, 0 ); } void nv::gui::environment::draw() { draw( m_screen ); m_renderer->draw(); } void nv::gui::environment::add_child( handle child ) { add_child( m_screen, child ); } void nv::gui::environment::add_child( handle parent, handle child ) { element* e = get_element( child ); element* p = get_element( parent ); if ( e && p ) { remove_child( e->m_parent, child ); e->m_parent = parent; p->m_children.push_back( child ); p->m_child_count++; } } void nv::gui::environment::destroy_children( handle e ) { element* parent = get_element(e); if ( parent ) { while ( !parent->m_children.empty() ) { destroy_element( parent->m_children.front() ); } } } nv::gui::environment::~environment() { destroy_children( handle() ); delete m_renderer; } bool nv::gui::environment::process_io_event( const io_event& ) { return false; } bool nv::gui::environment::process_io_event( handle e, const io_event& ev ) { element* el = get_element( e ); return el && el->m_parent.m_index ? process_io_event( el->m_parent, ev ) : false; } nv::gui::handle nv::gui::environment::get_element( const position& p ) { return get_deepest_child( m_screen, p ); } nv::gui::element* nv::gui::environment::get_element( handle h ) { if ( h.is_nil() ) return nullptr; const index& idx = m_indices[ h.m_index ]; return idx.counter == h.m_counter && idx.element_id >= 0 ? &m_elements[ idx.element_id ] : nullptr; } nv::gui::handle nv::gui::environment::get_deepest_child( handle e, const position& p ) { element* el = get_element(e); if ( !el && !el->m_visible ) return handle(); handle result; element::list::reverse_iterator it = el->m_children.rbegin(); while ( it != el->m_children.rend() ) { result = get_deepest_child( *it, p ); if ( result.m_index ) return result; ++it; } if ( el->m_absolute.contains(p) ) return e; return handle(); } void nv::gui::environment::move_to_top( handle child ) { element* e = get_element( child ); element* parent = get_element( e->m_parent ); if ( e && parent ) { auto it = std::find( parent->m_children.begin(), parent->m_children.end(), child ); if ( it != parent->m_children.end() ) { parent->m_children.erase( it ); parent->m_children.push_back( child ); parent->m_dirty = true; } } } void nv::gui::environment::move_to_bottom( handle child ) { element* e = get_element( child ); element* parent = get_element( e->m_parent ); if ( e && parent ) { auto it = std::find( parent->m_children.begin(), parent->m_children.end(), child ); if ( it != parent->m_children.end() ) { parent->m_children.erase( it ); parent->m_children.push_front( child ); parent->m_dirty = true; } } } void nv::gui::environment::set_relative( handle e, const rectangle& r ) { element* el = get_element(e); if ( el ) { el->m_dirty = true; el->m_relative = r; recalculate_absolute( e ); } } void nv::gui::environment::set_relative( handle e, const position& p ) { element* el = get_element(e); if ( el ) { set_relative( e, rectangle( p, p + el->m_relative.get_size() ) ); } } void nv::gui::environment::recalculate_absolute( handle e ) { element* el = get_element(e); rectangle pabsolute = get_element( el->m_parent )->m_absolute; el->m_absolute = el->m_relative + pabsolute.ul; for ( handle o : el->m_children ) { recalculate_absolute( o ); } } void nv::gui::environment::set_class( handle e, const string& text ) { element* ep = get_element(e); if ( ep != nullptr ) { ep->m_class = text; ep->m_dirty = true; } } void nv::gui::environment::set_text( handle e, const string& text ) { element* ep = get_element(e); if ( ep != nullptr ) { ep->m_text = text; ep->m_dirty = true; } } void nv::gui::environment::remove_child( handle parent, handle child ) { element* p = get_element( parent ); if ( p ) { auto it = std::find( p->m_children.begin(), p->m_children.end(), child ); if ( it != p->m_children.end() ) { element* e = get_element( *it ); e->m_parent = handle(); p->m_children.erase(it); } } }