This commit is contained in:
Garux
2022-10-27 09:10:31 +03:00
parent b1e5581c60
commit 9d3d204018
289 changed files with 13557 additions and 33481 deletions

View File

@@ -39,12 +39,23 @@
#include "stream/stringstream.h"
#include "generic/callback.h"
#include <gtk/gtk.h>
#include "gtkutil/glwidget.h"
#include "gtkutil/button.h"
#include "gtkutil/toolbar.h"
#include "gtkutil/cursor.h"
#include "gtkmisc.h"
#include "gtkutil/fbo.h"
#include "gtkutil/mousepresses.h"
#include "gtkutil/guisettings.h"
#include <QWidget>
#include <QToolBar>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSplitter>
#include <QTreeView>
#include <QStandardItemModel>
#include <QScrollBar>
#include <QOpenGLWidget>
#include "mainframe.h"
#include "camwindow.h"
@@ -511,8 +522,6 @@ public:
}
};
void ModelBrowser_scrollChanged( void* data, gdouble value );
class ModelBrowser : public scene::Instantiable::Observer
{
// track instances in the order of insertion
@@ -520,24 +529,22 @@ class ModelBrowser : public scene::Instantiable::Observer
public:
ModelFS m_modelFS;
CopiedString m_prefFoldersToLoad = "*models/99*";
ModelBrowser() : m_scrollAdjustment( ModelBrowser_scrollChanged, this ){
ModelBrowser() : m_scrollAdjustment( [this]( int value ){
//globalOutputStream() << "vertical scroll\n";
setOriginZ( -value );
} )
{
}
~ModelBrowser(){
}
FBO* m_fbo = nullptr;
FBO* fbo_get(){
return m_fbo = m_fbo? m_fbo : GlobalOpenGL().support_ARB_framebuffer_object? new FBO : new FBO_fallback;
}
const int m_MSAA = 8;
GtkWindow* m_parent = nullptr;
GtkWidget* m_gl_widget = nullptr;
GtkWidget* m_gl_scroll = nullptr;
GtkWidget* m_treeViewTree = nullptr;
QWidget* m_parent = nullptr;
QOpenGLWidget* m_gl_widget = nullptr;
QScrollBar* m_gl_scroll = nullptr;
QTreeView* m_treeView = nullptr;
guint m_sizeHandler;
guint m_exposeHandler;
int m_width;
int m_height;
@@ -563,16 +570,11 @@ private:
return constructCellPos().totalHeight( m_height, m_modelInstances.size() );
}
void updateScroll() const {
GtkAdjustment *vadjustment = gtk_range_get_adjustment( GTK_RANGE( m_gl_scroll ) );
gtk_adjustment_set_value( vadjustment, -m_originZ );
gtk_adjustment_set_page_size( vadjustment, m_height );
gtk_adjustment_set_page_increment( vadjustment, m_height / 2 );
gtk_adjustment_set_step_increment( vadjustment, 20 );
gtk_adjustment_set_lower( vadjustment, 0 );
gtk_adjustment_set_upper( vadjustment, totalHeight() );
g_signal_emit_by_name( G_OBJECT( vadjustment ), "changed" );
m_gl_scroll->setMinimum( 0 );
m_gl_scroll->setMaximum( totalHeight() - m_height );
m_gl_scroll->setValue( -m_originZ );
m_gl_scroll->setPageStep( m_height );
m_gl_scroll->setSingleStep( 20 );
}
public:
void setOriginZ( int origin ){
@@ -582,7 +584,7 @@ public:
}
void queueDraw() const {
if ( m_gl_widget != nullptr )
gtk_widget_queue_draw( m_gl_widget );
widget_queue_draw( *m_gl_widget );
}
bool m_originInvalid = true;
void validate(){
@@ -595,30 +597,29 @@ public:
}
private:
void trackingDelta( int x, int y, const QMouseEvent *event ){
m_move_amount += std::abs( x ) + std::abs( y );
if ( event->buttons() & Qt::MouseButton::RightButton && y != 0 ) { // scroll view
const int scale = event->modifiers().testFlag( Qt::KeyboardModifier::ShiftModifier )? 4 : 1;
setOriginZ( m_originZ + y * scale );
}
else if ( event->buttons() & Qt::MouseButton::LeftButton && ( x != 0 || y != 0 ) && m_currentModelId >= 0 ) { // rotate selected model
ASSERT_MESSAGE( m_currentModelId < static_cast<int>( m_modelInstances.size() ), "modelBrowser.m_currentModelId out of range" );
scene::Instance *instance = m_modelInstances[m_currentModelId];
if( TransformNode *transformNode = Node_getTransformNode( instance->path().parent() ) ){
Matrix4 rot( g_matrix4_identity );
matrix4_pivoted_rotate_by_euler_xyz_degrees( rot, Vector3( y, 0, x ) * ( 45.f / m_cellSize ), constructCellPos().getOrigin( m_currentModelId ) );
matrix4_premultiply_by_matrix4( const_cast<Matrix4&>( transformNode->localToParent() ), rot );
instance->parent()->transformChangedLocal();
instance->transformChangedLocal();
queueDraw();
}
}
}
FreezePointer m_freezePointer;
bool m_move_started = false;
public:
int m_move_amount;
static void trackingDelta( int x, int y, unsigned int state, void* data ){
ModelBrowser& modelBrowser = *reinterpret_cast<ModelBrowser*>( data );
modelBrowser.m_move_amount += std::abs( x ) + std::abs( y );
if ( ( state & GDK_BUTTON3_MASK ) && y != 0 ) { // scroll view
const int scale = ( state & GDK_SHIFT_MASK )? 4 : 1;
modelBrowser.setOriginZ( modelBrowser.m_originZ + y * scale );
}
else if ( ( state & GDK_BUTTON1_MASK ) && ( x != 0 || y != 0 ) && modelBrowser.m_currentModelId >= 0 ) { // rotate selected model
ASSERT_MESSAGE( modelBrowser.m_currentModelId < static_cast<int>( modelBrowser.m_modelInstances.size() ), "modelBrowser.m_currentModelId out of range" );
scene::Instance *instance = modelBrowser.m_modelInstances[modelBrowser.m_currentModelId];
if( TransformNode *transformNode = Node_getTransformNode( instance->path().parent() ) ){
Matrix4 rot( g_matrix4_identity );
matrix4_pivoted_rotate_by_euler_xyz_degrees( rot, Vector3( y, 0, x ) * ( 45.f / modelBrowser.m_cellSize ), modelBrowser.constructCellPos().getOrigin( modelBrowser.m_currentModelId ) );
matrix4_premultiply_by_matrix4( const_cast<Matrix4&>( transformNode->localToParent() ), rot );
instance->parent()->transformChangedLocal();
instance->transformChangedLocal();
modelBrowser.queueDraw();
}
}
}
void tracking_MouseUp(){
if( m_move_started ){
m_move_started = false;
@@ -629,7 +630,13 @@ public:
tracking_MouseUp();
m_move_started = true;
m_move_amount = 0;
m_freezePointer.freeze_pointer( m_parent, m_gl_widget, trackingDelta, this );
m_freezePointer.freeze_pointer( m_gl_widget,
[this]( int x, int y, const QMouseEvent *event ){
trackingDelta( x, y, event );
},
[this](){
tracking_MouseUp();
} );
}
void insert( scene::Instance* instance ) override {
@@ -749,18 +756,16 @@ origin -----> +x
void ModelBrowser_render(){
g_ModelBrowser.validate();
g_ModelBrowser.fbo_get()->start();
const int W = g_ModelBrowser.m_width;
const int H = g_ModelBrowser.m_height;
glViewport( 0, 0, W, H );
gl().glViewport( 0, 0, W, H );
// enable depth buffer writes
glDepthMask( GL_TRUE );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
gl().glDepthMask( GL_TRUE );
gl().glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glClearColor( .25f, .25f, .25f, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
gl().glClearColor( .25f, .25f, .25f, 0 );
gl().glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
const unsigned int globalstate = RENDER_DEPTHTEST
| RENDER_COLOURWRITE
@@ -769,8 +774,6 @@ void ModelBrowser_render(){
| RENDER_BLEND
| RENDER_CULLFACE
| RENDER_COLOURARRAY
| RENDER_POLYGONSMOOTH
| RENDER_LINESMOOTH
| RENDER_FOG
| RENDER_COLOURCHANGE
| RENDER_FILL
@@ -827,53 +830,51 @@ void ModelBrowser_render(){
m_view.Construct( m_projection, m_modelview, W, H );
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( reinterpret_cast<const float*>( &m_projection ) );
gl().glMatrixMode( GL_PROJECTION );
gl().glLoadMatrixf( reinterpret_cast<const float*>( &m_projection ) );
glMatrixMode( GL_MODELVIEW );
glLoadMatrixf( reinterpret_cast<const float*>( &m_modelview ) );
gl().glMatrixMode( GL_MODELVIEW );
gl().glLoadMatrixf( reinterpret_cast<const float*>( &m_modelview ) );
if( g_ModelBrowser.m_currentFolder != nullptr ){
{ // prepare for 2d stuff
glDisable( GL_BLEND );
gl().glDisable( GL_BLEND );
if ( GlobalOpenGL().GL_1_3() ) {
glClientActiveTexture( GL_TEXTURE0 );
glActiveTexture( GL_TEXTURE0 );
}
gl().glClientActiveTexture( GL_TEXTURE0 );
gl().glActiveTexture( GL_TEXTURE0 );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
gl().glDisableClientState( GL_TEXTURE_COORD_ARRAY );
gl().glDisableClientState( GL_NORMAL_ARRAY );
gl().glDisableClientState( GL_COLOR_ARRAY );
glDisable( GL_TEXTURE_2D );
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
glDisable( GL_DEPTH_TEST );
gl().glDisable( GL_TEXTURE_2D );
gl().glDisable( GL_LIGHTING );
gl().glDisable( GL_COLOR_MATERIAL );
gl().glDisable( GL_DEPTH_TEST );
}
{ // brighter background squares
glColor4f( 0.3f, 0.3f, 0.3f, 1.f );
glDepthMask( GL_FALSE );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glDisable( GL_CULL_FACE );
gl().glColor4f( 0.3f, 0.3f, 0.3f, 1.f );
gl().glDepthMask( GL_FALSE );
gl().glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
gl().glDisable( GL_CULL_FACE );
CellPos cellPos = g_ModelBrowser.constructCellPos();
glBegin( GL_QUADS );
gl().glBegin( GL_QUADS );
for( std::size_t i = g_ModelBrowser.m_currentFolder->m_files.size(); i != 0; --i ){
const Vector3 origin = cellPos.getOrigin();
const float minx = origin.x() - cellPos.getCellSize();
const float maxx = origin.x() + cellPos.getCellSize();
const float minz = origin.z() - cellPos.getCellSize();
const float maxz = origin.z() + cellPos.getCellSize();
glVertex3f( minx, 0, maxz );
glVertex3f( minx, 0, minz );
glVertex3f( maxx, 0, minz );
glVertex3f( maxx, 0, maxz );
gl().glVertex3f( minx, 0, maxz );
gl().glVertex3f( minx, 0, minz );
gl().glVertex3f( maxx, 0, minz );
gl().glVertex3f( maxx, 0, maxz );
++cellPos;
}
glEnd();
gl().glEnd();
}
// one directional light source directly behind the viewer
@@ -890,12 +891,12 @@ void ModelBrowser_render(){
inverse_cam_dir[2] = -m_view.getViewDir()[2];
inverse_cam_dir[3] = 0;
glLightfv( GL_LIGHT0, GL_POSITION, inverse_cam_dir );
gl().glLightfv( GL_LIGHT0, GL_POSITION, inverse_cam_dir );
glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
gl().glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
gl().glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
glEnable( GL_LIGHT0 );
gl().glEnable( GL_LIGHT0 );
}
{
@@ -910,30 +911,28 @@ void ModelBrowser_render(){
}
{ // prepare for 2d stuff
glColor4f( 1, 1, 1, 1 );
glDisable( GL_BLEND );
gl().glColor4f( 1, 1, 1, 1 );
gl().glDisable( GL_BLEND );
if ( GlobalOpenGL().GL_1_3() ) {
glClientActiveTexture( GL_TEXTURE0 );
glActiveTexture( GL_TEXTURE0 );
}
gl().glClientActiveTexture( GL_TEXTURE0 );
gl().glActiveTexture( GL_TEXTURE0 );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
gl().glDisableClientState( GL_TEXTURE_COORD_ARRAY );
gl().glDisableClientState( GL_NORMAL_ARRAY );
gl().glDisableClientState( GL_COLOR_ARRAY );
glDisable( GL_TEXTURE_2D );
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
glDisable( GL_DEPTH_TEST );
glLineWidth( 1 );
gl().glDisable( GL_TEXTURE_2D );
gl().glDisable( GL_LIGHTING );
gl().glDisable( GL_COLOR_MATERIAL );
gl().glDisable( GL_DEPTH_TEST );
gl().glLineWidth( 1 );
}
{ // render model file names
CellPos cellPos = g_ModelBrowser.constructCellPos();
for( const CopiedString& string : g_ModelBrowser.m_currentFolder->m_files ){
const Vector3 pos = cellPos.getTextPos();
if( m_view.TestPoint( pos ) ){
glRasterPos3f( pos.x(), pos.y(), pos.z() );
gl().glRasterPos3f( pos.x(), pos.y(), pos.z() );
GlobalOpenGL().drawString( string.c_str() );
}
++cellPos;
@@ -943,109 +942,102 @@ void ModelBrowser_render(){
// bind back to the default texture so that we don't have problems
// elsewhere using/modifying texture maps between contexts
glBindTexture( GL_TEXTURE_2D, 0 );
g_ModelBrowser.fbo_get()->save();
gl().glBindTexture( GL_TEXTURE_2D, 0 );
}
gboolean ModelBrowser_size_allocate( GtkWidget* widget, GtkAllocation* allocation, ModelBrowser* modelBrowser ){
modelBrowser->fbo_get()->reset( allocation->width, allocation->height, modelBrowser->m_MSAA, true );
modelBrowser->m_width = allocation->width;
modelBrowser->m_height = allocation->height;
modelBrowser->m_originInvalid = true;
modelBrowser->forEachModelInstance( models_set_transforms() );
modelBrowser->queueDraw();
return FALSE;
}
gboolean ModelBrowser_expose( GtkWidget* widget, GdkEventExpose* event, ModelBrowser* modelBrowser ){
if ( glwidget_make_current( modelBrowser->m_gl_widget ) ) {
GlobalOpenGL_debugAssertNoErrors();
ModelBrowser_render();
GlobalOpenGL_debugAssertNoErrors();
glwidget_swap_buffers( modelBrowser->m_gl_widget );
class ModelBrowserGLWidget : public QOpenGLWidget
{
ModelBrowser& m_modBro;
FBO *m_fbo{};
MousePresses m_mouse;
public:
ModelBrowserGLWidget( ModelBrowser& modelBrowser ) : QOpenGLWidget(), m_modBro( modelBrowser )
{
}
return FALSE;
}
gboolean ModelBrowser_mouseScroll( GtkWidget* widget, GdkEventScroll* event, ModelBrowser* modelBrowser ){
gtk_widget_grab_focus( widget );
if( !gtk_window_is_active( modelBrowser->m_parent ) )
gtk_window_present( modelBrowser->m_parent );
if ( event->direction == GDK_SCROLL_UP ) {
modelBrowser->setOriginZ( modelBrowser->m_originZ + 64 );
~ModelBrowserGLWidget() override {
delete m_fbo;
glwidget_context_destroyed();
}
else if ( event->direction == GDK_SCROLL_DOWN ) {
modelBrowser->setOriginZ( modelBrowser->m_originZ - 64 );
protected:
void initializeGL() override
{
glwidget_context_created( *this );
}
return FALSE;
}
void resizeGL( int w, int h ) override
{
delete m_fbo;
m_fbo = new FBO( w, h, true, m_modBro.m_MSAA );
void ModelBrowser_scrollChanged( void* data, gdouble value ){
//globalOutputStream() << "vertical scroll\n";
reinterpret_cast<ModelBrowser*>( data )->setOriginZ( -(int)value );
}
static void ModelBrowser_scrollbarScroll( GtkAdjustment *adjustment, ModelBrowser* modelBrowser ){
modelBrowser->m_scrollAdjustment.value_changed( gtk_adjustment_get_value( adjustment ) );
}
gboolean ModelBrowser_button_press( GtkWidget* widget, GdkEventButton* event, ModelBrowser* modelBrowser ){
if ( event->type == GDK_BUTTON_PRESS ) {
gtk_widget_grab_focus( widget );
if ( event->button == 1 || event->button == 3 ) {
modelBrowser->tracking_MouseDown();
}
if ( event->button == 1 ) {
modelBrowser->testSelect( static_cast<int>( event->x ), static_cast<int>( event->y ) );
m_modBro.m_width = w;
m_modBro.m_height = h;
m_modBro.m_originInvalid = true;
m_modBro.forEachModelInstance( models_set_transforms() );
}
void paintGL() override
{
if( ScreenUpdates_Enabled() && m_fbo->bind() ){
GlobalOpenGL_debugAssertNoErrors();
ModelBrowser_render();
GlobalOpenGL_debugAssertNoErrors();
m_fbo->blit();
m_fbo->release();
}
}
/* create misc_model */
else if ( event->type == GDK_2BUTTON_PRESS && event->button == 1 && modelBrowser->m_currentFolder != nullptr && modelBrowser->m_currentModelId >= 0 ) {
UndoableCommand undo( "insertModel" );
// todo
// GlobalEntityClassManager() search for "misc_model"
// otherwise search for entityClass->miscmodel_is
// otherwise go with GlobalEntityClassManager().findOrInsert( "misc_model", false );
EntityClass* entityClass = GlobalEntityClassManager().findOrInsert( "misc_model", false );
NodeSmartReference node( GlobalEntityCreator().createEntity( entityClass ) );
Node_getTraversable( GlobalSceneGraph().root() )->insert( node );
scene::Path entitypath( makeReference( GlobalSceneGraph().root() ) );
entitypath.push( makeReference( node.get() ) );
scene::Instance& instance = findInstance( entitypath );
if ( Transformable* transform = Instance_getTransformable( instance ) ) { // might be cool to consider model aabb here
transform->setType( TRANSFORM_PRIMITIVE );
transform->setTranslation( vector3_snapped( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) - Camera_getViewVector( *g_pParentWnd->GetCamWnd() ) * 128.f, GetSnapGridSize() ) );
transform->freezeTransform();
void mousePressEvent( QMouseEvent *event ) override {
setFocus();
const auto press = m_mouse.press( event );
if( press == MousePresses::Left2x ){
mouseDoubleClick();
}
else if ( press == MousePresses::Left || press == MousePresses::Right ) {
m_modBro.tracking_MouseDown();
if ( press == MousePresses::Left ) {
m_modBro.testSelect( event->x(), event->y() );
}
}
GlobalSelectionSystem().setSelectedAll( false );
Instance_setSelected( instance, true );
StringOutputStream sstream( 128 );
sstream << modelBrowser->m_currentFolderPath << std::next( modelBrowser->m_currentFolder->m_files.begin(), modelBrowser->m_currentModelId )->c_str();
Node_getEntity( node )->setKeyValue( entityClass->miscmodel_key(), sstream.c_str() );
}
return FALSE;
}
void mouseDoubleClick(){
/* create misc_model */
if ( m_modBro.m_currentFolder != nullptr && m_modBro.m_currentModelId >= 0 ) {
UndoableCommand undo( "insertModel" );
// todo
// GlobalEntityClassManager() search for "misc_model"
// otherwise search for entityClass->miscmodel_is
// otherwise go with GlobalEntityClassManager().findOrInsert( "misc_model", false );
EntityClass* entityClass = GlobalEntityClassManager().findOrInsert( "misc_model", false );
NodeSmartReference node( GlobalEntityCreator().createEntity( entityClass ) );
Node_getTraversable( GlobalSceneGraph().root() )->insert( node );
scene::Path entitypath( makeReference( GlobalSceneGraph().root() ) );
entitypath.push( makeReference( node.get() ) );
scene::Instance& instance = findInstance( entitypath );
if ( Transformable* transform = Instance_getTransformable( instance ) ) { // might be cool to consider model aabb here
transform->setType( TRANSFORM_PRIMITIVE );
transform->setTranslation( vector3_snapped( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) - Camera_getViewVector( *g_pParentWnd->GetCamWnd() ) * 128.f, GetSnapGridSize() ) );
transform->freezeTransform();
}
GlobalSelectionSystem().setSelectedAll( false );
Instance_setSelected( instance, true );
gboolean ModelBrowser_button_release( GtkWidget* widget, GdkEventButton* event, ModelBrowser* modelBrowser ){
if ( event->type == GDK_BUTTON_RELEASE ) {
if ( event->button == 1 || event->button == 3 ) {
modelBrowser->tracking_MouseUp();
}
if ( event->button == 1 && modelBrowser->m_move_amount < 16 && modelBrowser->m_currentFolder != nullptr && modelBrowser->m_currentModelId >= 0 ) { // assign model to selected entity nodes
StringOutputStream sstream( 128 );
sstream << modelBrowser->m_currentFolderPath << std::next( modelBrowser->m_currentFolder->m_files.begin(), modelBrowser->m_currentModelId )->c_str();
sstream << m_modBro.m_currentFolderPath << std::next( m_modBro.m_currentFolder->m_files.begin(), m_modBro.m_currentModelId )->c_str();
Node_getEntity( node )->setKeyValue( entityClass->miscmodel_key(), sstream.c_str() );
}
}
void mouseReleaseEvent( QMouseEvent *event ) override {
const auto release = m_mouse.release( event );
if ( release == MousePresses::Left || release == MousePresses::Right ) {
m_modBro.tracking_MouseUp();
}
if ( release == MousePresses::Left && m_modBro.m_move_amount < 16 && m_modBro.m_currentFolder != nullptr && m_modBro.m_currentModelId >= 0 ) { // assign model to selected entity nodes
StringOutputStream sstream( 128 );
sstream << m_modBro.m_currentFolderPath << std::next( m_modBro.m_currentFolder->m_files.begin(), m_modBro.m_currentModelId )->c_str();
class EntityVisitor : public SelectionSystem::Visitor
{
const char* m_filePath;
@@ -1062,58 +1054,61 @@ gboolean ModelBrowser_button_release( GtkWidget* widget, GdkEventButton* event,
GlobalSelectionSystem().foreachSelected( visitor );
}
}
return FALSE;
}
void wheelEvent( QWheelEvent *event ) override {
setFocus();
if( !m_modBro.m_parent->isActiveWindow() ){
m_modBro.m_parent->activateWindow();
m_modBro.m_parent->raise();
}
m_modBro.setOriginZ( m_modBro.m_originZ + std::copysign( 64, event->angleDelta().y() ) );
}
};
static void TreeView_onRowActivated( GtkTreeView* treeview, GtkTreePath* path, GtkTreeViewColumn* col, gpointer userdata ){
GtkTreeModel* model = gtk_tree_view_get_model( GTK_TREE_VIEW( treeview ) );
GtkTreeIter iter;
if ( gtk_tree_model_get_iter( model, &iter, path ) ) {
std::deque<GtkTreeIter> iters;
iters.push_front( iter );
GtkTreeIter parent;
while( gtk_tree_model_iter_parent( model, &parent, &iters.front() ) )
iters.push_front( parent );
StringOutputStream sstream( 64 );
const ModelFS *modelFS = &g_ModelBrowser.m_modelFS;
for( GtkTreeIter& i : iters ){
gchar* buffer;
gtk_tree_model_get( model, &i, 0, &buffer, -1 );
const auto found = modelFS->m_folders.find( ModelFS( StringRange( buffer, strlen( buffer ) ) ) );
static void TreeView_onRowActivated( const QModelIndex& index ){
StringOutputStream sstream( 64 );
const ModelFS *modelFS = &g_ModelBrowser.m_modelFS;
{
std::deque<QModelIndex> iters;
iters.push_front( index );
while( iters.front().parent().isValid() )
iters.push_front( iters.front().parent() );
for( const QModelIndex& i : iters ){
const auto dir = i.data( Qt::ItemDataRole::DisplayRole ).toByteArray();
const auto found = modelFS->m_folders.find( ModelFS( StringRange( dir.constData(), strlen( dir.constData() ) ) ) );
if( found != modelFS->m_folders.end() ){ // ok to not find, while loading root
modelFS = &( *found );
sstream << buffer << "/";
sstream << dir.constData() << "/";
}
g_free( buffer );
}
}
//% globalOutputStream() << sstream.c_str() << " sstream.c_str()\n";
ModelGraph_clear(); // this goes 1st: resets m_currentFolder
ModelGraph_clear(); // this goes 1st: resets m_currentFolder
g_ModelBrowser.m_currentFolder = modelFS;
g_ModelBrowser.m_currentFolderPath = sstream.c_str();
g_ModelBrowser.m_currentFolder = modelFS;
g_ModelBrowser.m_currentFolderPath = sstream.c_str();
{
ScopeDisableScreenUpdates disableScreenUpdates( g_ModelBrowser.m_currentFolderPath.c_str(), "Loading Models" );
{
for( const CopiedString& filename : g_ModelBrowser.m_currentFolder->m_files ){
sstream.clear();
sstream << g_ModelBrowser.m_currentFolderPath << filename;
ModelNode *modelNode = new ModelNode;
modelNode->setModel( sstream.c_str() );
NodeSmartReference node( modelNode->node() );
Node_getTraversable( g_modelGraph->root() )->insert( node );
}
g_ModelBrowser.forEachModelInstance( models_set_transforms() );
for( const CopiedString& filename : g_ModelBrowser.m_currentFolder->m_files ){
sstream( g_ModelBrowser.m_currentFolderPath, filename );
ModelNode *modelNode = new ModelNode;
modelNode->setModel( sstream.c_str() );
NodeSmartReference node( modelNode->node() );
Node_getTraversable( g_modelGraph->root() )->insert( node );
}
g_ModelBrowser.queueDraw();
//deactivate, so SPACE and RETURN wont be broken for 2d
gtk_window_set_focus( GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( treeview ) ) ), NULL );
g_ModelBrowser.forEachModelInstance( models_set_transforms() );
}
g_ModelBrowser.queueDraw();
//deactivate, so SPACE and RETURN wont be broken for 2d
g_ModelBrowser.m_treeView->clearFocus();
}
@@ -1132,12 +1127,11 @@ void modelFS_traverse( const ModelFS& modelFS ){
--depth;
}
#endif
void ModelBrowser_constructTreeModel( const ModelFS& modelFS, GtkTreeStore* store, GtkTreeIter* parent ){
GtkTreeIter iter;
gtk_tree_store_append( store, &iter, parent );
gtk_tree_store_set( store, &iter, 0, modelFS.m_folderName.c_str(), -1 );
void ModelBrowser_constructTreeModel( const ModelFS& modelFS, QStandardItemModel* model, QStandardItem* parent ){
auto item = new QStandardItem( modelFS.m_folderName.c_str() );
parent->appendRow( item );
for( const ModelFS& m : modelFS.m_folders )
ModelBrowser_constructTreeModel( m, store, &iter ); //recursion
ModelBrowser_constructTreeModel( m, model, item ); //recursion
}
typedef std::map<CopiedString, std::size_t> ModelFoldersMap;
@@ -1241,107 +1235,90 @@ void ModelBrowser_constructTree(){
//% modelFS_traverse( g_ModelBrowser.m_modelFS );
GtkTreeStore* store = gtk_tree_store_new( 1, G_TYPE_STRING );
auto model = new QStandardItemModel( g_ModelBrowser.m_treeView ); //. ? delete old or clear() & reuse
{
if( !g_ModelBrowser.m_modelFS.m_files.empty() ){ // models in the root
GtkTreeIter iter;
gtk_tree_store_append( store, &iter, nullptr );
gtk_tree_store_set( store, &iter, 0, "", -1 );
if( !g_ModelBrowser.m_modelFS.m_files.empty() ){ // models in the root: add blank item for access
model->invisibleRootItem()->appendRow( new QStandardItem( "" ) );
}
for( const ModelFS& m : g_ModelBrowser.m_modelFS.m_folders )
ModelBrowser_constructTreeModel( m, store, nullptr );
ModelBrowser_constructTreeModel( m, model, model->invisibleRootItem() );
}
gtk_tree_view_set_model( GTK_TREE_VIEW( g_ModelBrowser.m_treeViewTree ), GTK_TREE_MODEL( store ) );
g_object_unref( G_OBJECT( store ) );
g_ModelBrowser.m_treeView->setModel( model );
}
GtkWidget* ModelBrowser_constructWindow( GtkWindow* toplevel ){
class TexBro_QTreeView : public QTreeView
{
protected:
bool event( QEvent *event ) override {
if( event->type() == QEvent::ShortcutOverride ){
event->accept();
return true;
}
return QTreeView::event( event );
}
};
QWidget* ModelBrowser_constructWindow( QWidget* toplevel ){
g_ModelBrowser.m_parent = toplevel;
GtkWidget* table = gtk_table_new( 1, 3, FALSE );
GtkWidget* vbox = gtk_vbox_new( FALSE, 0 );
gtk_table_attach( GTK_TABLE( table ), vbox, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0 );
gtk_widget_show( vbox );
QSplitter *splitter = new QSplitter;
QWidget *containerWidgetLeft = new QWidget; // Adding a QLayout to a QSplitter is not supported, use proxy widget
QWidget *containerWidgetRight = new QWidget; // Adding a QLayout to a QSplitter is not supported, use proxy widget
splitter->addWidget( containerWidgetLeft );
splitter->addWidget( containerWidgetRight );
QVBoxLayout *vbox = new QVBoxLayout( containerWidgetLeft );
QHBoxLayout *hbox = new QHBoxLayout( containerWidgetRight );
hbox->setContentsMargins( 0, 0, 0, 0 );
vbox->setContentsMargins( 0, 0, 0, 0 );
hbox->setSpacing( 0 );
vbox->setSpacing( 0 );
{ // menu bar
GtkToolbar* toolbar = toolbar_new();
gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( toolbar ), FALSE, FALSE, 0 );
QToolBar* toolbar = new QToolBar;
vbox->addWidget( toolbar );
GtkToolButton* button = toolbar_append_button( toolbar, "Reload Model Folders Tree View", "texbro_refresh.png", FreeCaller<ModelBrowser_constructTree>() );
// gtk_widget_set_size_request( GTK_WIDGET( button ), 22, 22 );
toolbar_append_button( toolbar, "Reload Model Folders Tree View", "texbro_refresh.png", FreeCaller<ModelBrowser_constructTree>() );
}
{ // TreeView
GtkWidget* scr = gtk_scrolled_window_new( NULL, NULL );
gtk_container_set_border_width( GTK_CONTAINER( scr ), 0 );
// vertical only scrolling for treeview
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS );
gtk_widget_show( scr );
g_ModelBrowser.m_treeView = new TexBro_QTreeView;
g_ModelBrowser.m_treeView->setHeaderHidden( true );
g_ModelBrowser.m_treeView->setEditTriggers( QAbstractItemView::EditTrigger::NoEditTriggers );
g_ModelBrowser.m_treeView->setUniformRowHeights( true ); // optimization
g_ModelBrowser.m_treeView->setFocusPolicy( Qt::FocusPolicy::ClickFocus );
g_ModelBrowser.m_treeView->setExpandsOnDoubleClick( false );
g_ModelBrowser.m_treeViewTree = gtk_tree_view_new();
GtkTreeView* treeview = GTK_TREE_VIEW( g_ModelBrowser.m_treeViewTree );
//gtk_tree_view_set_enable_search( treeview, FALSE );
gtk_tree_view_set_headers_visible( treeview, FALSE );
g_signal_connect( treeview, "row-activated", (GCallback) TreeView_onRowActivated, NULL );
GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
//g_object_set( G_OBJECT( renderer ), "ellipsize", PANGO_ELLIPSIZE_START, NULL );
gtk_tree_view_insert_column_with_attributes( treeview, -1, "", renderer, "text", 0, NULL );
QObject::connect( g_ModelBrowser.m_treeView, &QAbstractItemView::activated, TreeView_onRowActivated );
ModelBrowser_constructTree();
//gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( scr ), g_ModelBrowser.m_treeViewTree );
gtk_container_add( GTK_CONTAINER( scr ), g_ModelBrowser.m_treeViewTree ); //GtkTreeView has native scrolling support; should not be used with the GtkViewport proxy.
gtk_widget_show( g_ModelBrowser.m_treeViewTree );
gtk_box_pack_start( GTK_BOX( vbox ), scr, TRUE, TRUE, 0 );
}
{ // gl_widget scrollbar
GtkWidget* w = g_ModelBrowser.m_gl_scroll = gtk_vscrollbar_new( GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 0, 1, 1, 0 ) ) );
gtk_table_attach( GTK_TABLE( table ), w, 2, 3, 0, 1, GTK_SHRINK, GTK_FILL, 0, 0 );
gtk_widget_show( w );
GtkAdjustment *vadjustment = gtk_range_get_adjustment( GTK_RANGE( w ) );
g_signal_connect( G_OBJECT( vadjustment ), "value_changed", G_CALLBACK( ModelBrowser_scrollbarScroll ), &g_ModelBrowser );
vbox->addWidget( g_ModelBrowser.m_treeView );
}
{ // gl_widget
GtkWidget* w = g_ModelBrowser.m_gl_widget = glwidget_new( TRUE );
g_object_ref( G_OBJECT( w ) );
g_ModelBrowser.m_gl_widget = new ModelBrowserGLWidget( g_ModelBrowser );
hbox->addWidget( g_ModelBrowser.m_gl_widget );
}
{ // gl_widget scrollbar
auto scroll = g_ModelBrowser.m_gl_scroll = new QScrollBar;
hbox->addWidget( scroll );
gtk_widget_set_events( w, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK );
gtk_widget_set_can_focus( w, TRUE );
gtk_table_attach_defaults( GTK_TABLE( table ), w, 1, 2, 0, 1 );
gtk_widget_show( w );
g_ModelBrowser.m_sizeHandler = g_signal_connect( G_OBJECT( w ), "size_allocate", G_CALLBACK( ModelBrowser_size_allocate ), &g_ModelBrowser );
g_ModelBrowser.m_exposeHandler = g_signal_connect( G_OBJECT( w ), "expose_event", G_CALLBACK( ModelBrowser_expose ), &g_ModelBrowser );
g_signal_connect( G_OBJECT( w ), "button_press_event", G_CALLBACK( ModelBrowser_button_press ), &g_ModelBrowser );
g_signal_connect( G_OBJECT( w ), "button_release_event", G_CALLBACK( ModelBrowser_button_release ), &g_ModelBrowser );
g_signal_connect( G_OBJECT( w ), "scroll_event", G_CALLBACK( ModelBrowser_mouseScroll ), &g_ModelBrowser );
QObject::connect( scroll, &QAbstractSlider::valueChanged, []( int value ){
g_ModelBrowser.m_scrollAdjustment.value_changed( value );
} );
}
//prevent focusing on filter entry or tex dirs treeview after click on tab of floating group dialog (np, if called via hotkey)
gtk_container_set_focus_chain( GTK_CONTAINER( table ), NULL );
return table;
splitter->setStretchFactor( 0, 0 ); // consistent treeview side sizing on resizes
splitter->setStretchFactor( 1, 1 );
g_guiSettings.addSplitter( splitter, "ModelBrowser/splitter", { 100, 500 } );
return splitter;
}
void ModelBrowser_destroyWindow(){
g_signal_handler_disconnect( G_OBJECT( g_ModelBrowser.m_gl_widget ), g_ModelBrowser.m_sizeHandler );
g_signal_handler_disconnect( G_OBJECT( g_ModelBrowser.m_gl_widget ), g_ModelBrowser.m_exposeHandler );
g_object_unref( G_OBJECT( g_ModelBrowser.m_gl_widget ) );
g_ModelBrowser.m_gl_widget = nullptr;
delete g_ModelBrowser.m_fbo;
}
@@ -1370,7 +1347,7 @@ typedef ReferenceCaller1<CopiedString, const char*, FoldersToLoadImport> Folders
void ModelBrowser_constructPage( PreferenceGroup& group ){
PreferencesPage page( group.createPage( "Model Browser", "Model Browser Preferences" ) );
page.appendSpinner( "Model View Size", 80.0, 16.0, 8192.0,
page.appendSpinner( "Model View Size", 16.0, 8192.0,
IntImportCallback( CellSizeImportCaller( g_ModelBrowser.m_cellSize ) ),
IntExportCallback( IntExportCaller( g_ModelBrowser.m_cellSize ) ) );
page.appendEntry( "List of *folderToLoad/depth*",
@@ -1396,10 +1373,6 @@ void ModelBrowser_Destroy(){
delete g_modelGraph;
}
GtkWidget* ModelBrowser_getGLWidget(){
return g_ModelBrowser.m_gl_widget;
}
void ModelBrowser_flushReferences(){
ModelGraph_clear();
g_ModelBrowser.queueDraw();