mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Minor improvements to UITreeView and related views.
Also improved UISplitter. Improved side panel in ecode.
This commit is contained in:
10
TODO.md
10
TODO.md
@@ -25,20 +25,18 @@ Detect errors and log them.
|
||||
|
||||
* Add XML tags auto-close.
|
||||
|
||||
## UITreeView
|
||||
|
||||
Implement a simple tree view widget, to at least cover the most common use cases.
|
||||
|
||||
## Code Editor
|
||||
|
||||
* Support single instance (when a new file is opened while a previous instance exists, open it in the first instance).
|
||||
|
||||
* Once `UITreeView` is finished add a directory/project tree view.
|
||||
|
||||
* Add support for function listing.
|
||||
|
||||
* Display number of results in search and number of replacements.
|
||||
|
||||
* Add support for global/project search.
|
||||
|
||||
* Add support for find and open file.
|
||||
|
||||
## UI Editor
|
||||
|
||||
* Integrate the `UICodeEditor` into the editor in order to be able to edit the layouts and CSS in app.
|
||||
|
||||
@@ -1508,6 +1508,27 @@ Sets the [skin](#skin) tint color.
|
||||
|
||||
---
|
||||
|
||||
### splitter-always-show
|
||||
|
||||
Sets if the splitter divisor is always visible. If false it will be shown only if two views are
|
||||
attached to the splitter.
|
||||
|
||||
* Applicable to: EE::UI::UISplitter (Splitter)
|
||||
* Data Type: [boolean](#boolean-data-type)
|
||||
* Default value: `true`
|
||||
|
||||
---
|
||||
|
||||
### splitter-partition
|
||||
|
||||
Sets the space ocuppied by the first view contained by the splitter.
|
||||
|
||||
* Applicable to: EE::UI::UISplitter (Splitter)
|
||||
* Data Type: [length-percentage](#length-percentage-data-type)
|
||||
* Default value: `50%`
|
||||
|
||||
---
|
||||
|
||||
### src
|
||||
|
||||
Sets the source of a resource in an element that supports source.
|
||||
|
||||
@@ -46,6 +46,14 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
||||
|
||||
int visibleColumnCount() const;
|
||||
|
||||
/** In pixels. */
|
||||
void setRowHeight( const Float& rowHeight );
|
||||
|
||||
void columnResizeToContent( const size_t& colIndex );
|
||||
|
||||
/** In pixels. */
|
||||
void setColumnWidth( const size_t& colIndex, const Float& width );
|
||||
|
||||
protected:
|
||||
friend class EE::UI::UITableHeaderColumn;
|
||||
|
||||
@@ -53,6 +61,8 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
||||
|
||||
UIAbstractTableView( const std::string& tag );
|
||||
|
||||
Float mRowHeight{0};
|
||||
|
||||
struct ColumnData {
|
||||
Float width{0};
|
||||
bool visible{true};
|
||||
|
||||
@@ -191,6 +191,8 @@ enum class PropertyId : Uint32 {
|
||||
TabBarHideOnSingleTab = String::hash( "tabbar-hide-on-single-tab" ),
|
||||
TabBarAllowRearrange = String::hash( "tabbar-allow-rearrange" ),
|
||||
TabBarAllowDragAndDrop = String::hash( "tabbar-allow-drag-and-drop-tabs" ),
|
||||
SplitterPartition = String::hash( "splitter-partition" ),
|
||||
SplitterAlwaysShow = String::hash( "splitter-always-show" ),
|
||||
};
|
||||
|
||||
enum class PropertyType : Uint32 {
|
||||
|
||||
@@ -40,7 +40,7 @@ class EE_API StyleSheetLength {
|
||||
|
||||
StyleSheetLength( const Float& val, const Unit& unit );
|
||||
|
||||
StyleSheetLength( std::string val, const Float& defaultValue = 0 );
|
||||
StyleSheetLength( const std::string& val, const Float& defaultValue = 0 );
|
||||
|
||||
StyleSheetLength( const StyleSheetLength& val );
|
||||
|
||||
@@ -56,7 +56,9 @@ class EE_API StyleSheetLength {
|
||||
Float asDp( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi,
|
||||
const Float& elFontSize = 12, const Float& globalFontSize = 12 ) const;
|
||||
|
||||
bool operator==( const StyleSheetLength& val );
|
||||
bool operator==( const StyleSheetLength& val ) const;
|
||||
|
||||
bool operator!=( const StyleSheetLength& val ) const;
|
||||
|
||||
StyleSheetLength& operator=( const StyleSheetLength& val );
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ class EE_API FileSystemModel : public Model {
|
||||
Permissions,
|
||||
ModificationTime,
|
||||
Inode,
|
||||
Path,
|
||||
SymlinkTarget,
|
||||
Count,
|
||||
};
|
||||
@@ -32,10 +33,14 @@ class EE_API FileSystemModel : public Model {
|
||||
Node* getParent() const { return mParent; }
|
||||
const FileInfo& info() const { return mInfo; }
|
||||
bool isSelected() const { return mSelected; }
|
||||
void setSelected( bool selected );
|
||||
void setSelected( bool selected ) { mSelected = selected; };
|
||||
const std::string& fullPath() const;
|
||||
const std::string& getMimeType() const { return mMimeType; }
|
||||
size_t childCount() const { return mChildren.size(); }
|
||||
const Node& getChild( const size_t& index ) {
|
||||
eeASSERT( index < mChildren.size() );
|
||||
return mChildren[index];
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FileSystemModel;
|
||||
@@ -49,7 +54,7 @@ class EE_API FileSystemModel : public Model {
|
||||
bool mSelected{false};
|
||||
ModelIndex index( const FileSystemModel& model, int column ) const;
|
||||
void traverseIfNeeded( const FileSystemModel& );
|
||||
void reifyIfNeeded( const FileSystemModel& );
|
||||
void refreshIfNeeded( const FileSystemModel& );
|
||||
bool fetchData( const String& fullPath );
|
||||
};
|
||||
|
||||
@@ -62,6 +67,8 @@ class EE_API FileSystemModel : public Model {
|
||||
|
||||
void setRootPath( const std::string& rootPath );
|
||||
|
||||
void reload();
|
||||
|
||||
void update();
|
||||
|
||||
const Node& node( const ModelIndex& index ) const;
|
||||
@@ -76,14 +83,24 @@ class EE_API FileSystemModel : public Model {
|
||||
|
||||
virtual Drawable* iconFor( const Node& node, const ModelIndex& index ) const;
|
||||
|
||||
FileSystemModel( const std::string& rootPath, const Mode& mode );
|
||||
void setMode( const Mode& mode );
|
||||
|
||||
void updateNodeSelection( const ModelIndex& index, const bool selected );
|
||||
|
||||
ModelIndex getPreviouslySelectedIndex() const;
|
||||
|
||||
void setPreviouslySelectedIndex( const ModelIndex& previouslySelectedIndex );
|
||||
|
||||
protected:
|
||||
std::string mRootPath;
|
||||
std::unique_ptr<Node> mRoot{nullptr};
|
||||
Mode mMode{Mode::FilesAndDirectories};
|
||||
|
||||
ModelIndex mPreviouslySelectedIndex{};
|
||||
|
||||
Node& nodeRef( const ModelIndex& index ) const;
|
||||
|
||||
FileSystemModel( const std::string& rootPath, const Mode& mode );
|
||||
};
|
||||
|
||||
}}} // namespace EE::UI::Models
|
||||
|
||||
@@ -80,6 +80,8 @@ class EE_API Model {
|
||||
|
||||
void unregisterView( UIAbstractView* );
|
||||
|
||||
void refreshView();
|
||||
|
||||
void setOnUpdate( const std::function<void()>& onUpdate );
|
||||
|
||||
protected:
|
||||
|
||||
@@ -53,6 +53,8 @@ class EE_API UIPushButton : public UIWidget {
|
||||
|
||||
explicit UIPushButton( const std::string& tag );
|
||||
|
||||
virtual void updateLayout();
|
||||
|
||||
virtual void onSizeChange();
|
||||
|
||||
virtual void onAlphaChange();
|
||||
|
||||
@@ -42,10 +42,15 @@ class EE_API UIScrollableWidget : public UIWidget {
|
||||
|
||||
void scrollToBottom();
|
||||
|
||||
void scrollToPosition( const Vector2f& pos, const bool& scrollVertically = true,
|
||||
const bool& scrollHorizontally = false );
|
||||
|
||||
Sizef getScrollableArea() const;
|
||||
|
||||
Sizef getVisibleArea() const;
|
||||
|
||||
Rectf getVisibleRect() const;
|
||||
|
||||
protected:
|
||||
ScrollViewType mViewType;
|
||||
ScrollBarMode mVScrollMode;
|
||||
|
||||
@@ -9,8 +9,6 @@ class EE_API UISplitter : public UILayout {
|
||||
public:
|
||||
static UISplitter* New();
|
||||
|
||||
UISplitter();
|
||||
|
||||
~UISplitter();
|
||||
|
||||
Uint32 getType() const;
|
||||
@@ -25,9 +23,9 @@ class EE_API UISplitter : public UILayout {
|
||||
|
||||
void setAlwaysShowSplitter( bool alwaysShowSplitter );
|
||||
|
||||
const Float& getDivisionSplit() const;
|
||||
const StyleSheetLength& getSplitPartition() const;
|
||||
|
||||
void setDivisionSplit( const Float& divisionSplit );
|
||||
void setSplitPartition( const StyleSheetLength& divisionSplit );
|
||||
|
||||
void swap();
|
||||
|
||||
@@ -39,15 +37,21 @@ class EE_API UISplitter : public UILayout {
|
||||
|
||||
UIWidget* getLastWidget() const;
|
||||
|
||||
bool applyProperty( const StyleSheetProperty& attribute );
|
||||
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex = 0 );
|
||||
|
||||
protected:
|
||||
UIOrientation mOrientation;
|
||||
bool mSplitOnlyWhenNeeded;
|
||||
bool mAlwaysShowSplitter;
|
||||
Float mDivisionSplit;
|
||||
StyleSheetLength mSplitPartition;
|
||||
UIWidget* mSplitter;
|
||||
UIWidget* mFirstWidget;
|
||||
UIWidget* mLastWidget;
|
||||
|
||||
UISplitter();
|
||||
|
||||
virtual void onChildCountChange( Node* child, const bool& removed );
|
||||
|
||||
virtual void updateLayout();
|
||||
|
||||
@@ -10,7 +10,7 @@ class UIAbstractTableView;
|
||||
}
|
||||
using namespace Abstract;
|
||||
|
||||
class UITableHeaderColumn : public UIPushButton {
|
||||
class EE_API UITableHeaderColumn : public UIPushButton {
|
||||
public:
|
||||
UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex );
|
||||
|
||||
|
||||
39
include/eepp/ui/uitablerow.hpp
Normal file
39
include/eepp/ui/uitablerow.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef EE_UI_UITABLEROW_HPP
|
||||
#define EE_UI_UITABLEROW_HPP
|
||||
|
||||
#include <eepp/ui/models/modelindex.hpp>
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
|
||||
using namespace EE::UI::Models;
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API UITableRow : public UIWidget {
|
||||
public:
|
||||
static UITableRow* New( const std::string& tag ) { return eeNew( UITableRow, ( tag ) ); }
|
||||
|
||||
ModelIndex getCurIndex() const { return mCurIndex; }
|
||||
|
||||
void setCurIndex( const ModelIndex& curIndex ) { mCurIndex = curIndex; }
|
||||
|
||||
protected:
|
||||
UITableRow( const std::string& tag ) : UIWidget( tag ) {}
|
||||
|
||||
virtual Uint32 onMessage( const NodeMessage* msg ) {
|
||||
if ( msg->getMsg() == NodeMessage::MouseDown && ( msg->getFlags() & EE_BUTTON_LMASK ) &&
|
||||
( !getEventDispatcher()->getMouseDownNode() ||
|
||||
getEventDispatcher()->getMouseDownNode() == this ||
|
||||
isParentOf( getEventDispatcher()->getMouseDownNode() ) ) &&
|
||||
getEventDispatcher()->getNodeDragging() == nullptr ) {
|
||||
sendMouseEvent( Event::MouseDown, getEventDispatcher()->getMousePos(),
|
||||
msg->getFlags() );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ModelIndex mCurIndex;
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif // EE_UI_UITABLEROW_HPP
|
||||
@@ -2,6 +2,7 @@
|
||||
#define EE_UI_UITREEVIEW_HPP
|
||||
|
||||
#include <eepp/ui/abstract/uiabstracttableview.hpp>
|
||||
#include <eepp/ui/uitablerow.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -71,12 +72,14 @@ class EE_API UITreeView : public UIAbstractTableView {
|
||||
|
||||
virtual void onColumnSizeChange( const size_t& colIndex );
|
||||
|
||||
virtual UITableRow* createRow();
|
||||
|
||||
virtual UITableRow* updateRow( const int& rowIndex, const ModelIndex& index,
|
||||
const Float& yOffset );
|
||||
|
||||
virtual UIWidget* updateCell( const int& rowIndex, const ModelIndex& index, const size_t& col,
|
||||
const size_t& indentLevel, const Float& yOffset );
|
||||
|
||||
virtual UIWidget* updateRow( const int& rowIndex, const ModelIndex& index,
|
||||
const Float& yOffset );
|
||||
|
||||
virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index, const size_t& col );
|
||||
|
||||
virtual void onScrollChange();
|
||||
|
||||
@@ -399,6 +399,7 @@
|
||||
../../include/eepp/ui/uitablecell.hpp
|
||||
../../include/eepp/ui/uitable.hpp
|
||||
../../include/eepp/ui/uitableheadercolumn.hpp
|
||||
../../include/eepp/ui/uitablerow.hpp
|
||||
../../include/eepp/ui/uitabwidget.hpp
|
||||
../../include/eepp/ui/uitextedit.hpp
|
||||
../../include/eepp/ui/uitextinput.hpp
|
||||
|
||||
@@ -26,8 +26,29 @@ bool UIAbstractTableView::isType( const Uint32& type ) const {
|
||||
}
|
||||
|
||||
Float UIAbstractTableView::getRowHeight() const {
|
||||
return eeceil( columnData( 0 ).widget ? columnData( 0 ).widget->getPixelsSize().getHeight()
|
||||
: 16 );
|
||||
return mRowHeight != 0 ? mRowHeight
|
||||
: ( eeceil( columnData( 0 ).widget
|
||||
? columnData( 0 ).widget->getPixelsSize().getHeight()
|
||||
: 16 ) );
|
||||
}
|
||||
|
||||
void UIAbstractTableView::setRowHeight( const Float& rowHeight ) {
|
||||
if ( mRowHeight != rowHeight ) {
|
||||
mRowHeight = rowHeight;
|
||||
createOrUpdateColumns();
|
||||
}
|
||||
}
|
||||
|
||||
void UIAbstractTableView::columnResizeToContent( const size_t& colIndex ) {
|
||||
onColumnResizeToContent( colIndex );
|
||||
}
|
||||
|
||||
void UIAbstractTableView::setColumnWidth( const size_t& colIndex, const Float& width ) {
|
||||
if ( columnData( colIndex ).width != width ) {
|
||||
columnData( colIndex ).width = width;
|
||||
updateHeaderSize();
|
||||
onColumnSizeChange( colIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void UIAbstractTableView::selectAll() {
|
||||
|
||||
@@ -91,7 +91,7 @@ StyleSheetLength::StyleSheetLength() : mUnit( Px ), mValue( 0 ) {}
|
||||
StyleSheetLength::StyleSheetLength( const Float& val, const StyleSheetLength::Unit& unit ) :
|
||||
mUnit( unit ), mValue( val ) {}
|
||||
|
||||
StyleSheetLength::StyleSheetLength( std::string val, const Float& defaultValue ) :
|
||||
StyleSheetLength::StyleSheetLength( const std::string& val, const Float& defaultValue ) :
|
||||
StyleSheetLength( fromString( val, defaultValue ) ) {}
|
||||
|
||||
StyleSheetLength::StyleSheetLength( const StyleSheetLength& val ) {
|
||||
@@ -170,10 +170,14 @@ Float StyleSheetLength::asDp( const Float& parentSize, const Sizef& viewSize,
|
||||
asPixels( parentSize, viewSize, displayDpi, elFontSize, globalFontSize ) );
|
||||
}
|
||||
|
||||
bool StyleSheetLength::operator==( const StyleSheetLength& length ) {
|
||||
bool StyleSheetLength::operator==( const StyleSheetLength& length ) const {
|
||||
return mValue == length.mValue && mUnit == length.mUnit;
|
||||
}
|
||||
|
||||
bool StyleSheetLength::operator!=( const StyleSheetLength& length ) const {
|
||||
return !( *this == length );
|
||||
}
|
||||
|
||||
StyleSheetLength& StyleSheetLength::operator=( const Float& val ) {
|
||||
mValue = val;
|
||||
mUnit = Unit::Px;
|
||||
|
||||
@@ -364,6 +364,11 @@ void StyleSheetSpecification::registerDefaultProperties() {
|
||||
registerProperty( "tabbar-allow-rearrange", "false" );
|
||||
registerProperty( "tabbar-allow-drag-and-drop-tabs", "false" );
|
||||
|
||||
registerProperty( "splitter-partition", "50%" )
|
||||
.setType( PropertyType::NumberLength )
|
||||
.setRelativeTarget( PropertyRelativeTarget::LocalBlockWidth );
|
||||
registerProperty( "splitter-always-show", "true" ).setType( PropertyType::Bool );
|
||||
|
||||
// Shorthands
|
||||
registerShorthand( "margin", {"margin-top", "margin-right", "margin-bottom", "margin-left"},
|
||||
"box" );
|
||||
|
||||
@@ -59,7 +59,7 @@ void FileSystemModel::Node::traverseIfNeeded( const FileSystemModel& model ) {
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemModel::Node::reifyIfNeeded( const FileSystemModel& model ) {
|
||||
void FileSystemModel::Node::refreshIfNeeded( const FileSystemModel& model ) {
|
||||
traverseIfNeeded( model );
|
||||
if ( mInfoDirty )
|
||||
fetchData( fullPath() );
|
||||
@@ -75,7 +75,7 @@ bool FileSystemModel::Node::fetchData( const String& fullPath ) {
|
||||
|
||||
std::shared_ptr<FileSystemModel> FileSystemModel::New( const std::string& rootPath,
|
||||
const FileSystemModel::Mode& mode ) {
|
||||
return std::make_shared<FileSystemModel>( rootPath, mode );
|
||||
return std::shared_ptr<FileSystemModel>( new FileSystemModel( rootPath, mode ) );
|
||||
}
|
||||
|
||||
FileSystemModel::FileSystemModel( const std::string& rootPath, const FileSystemModel::Mode& mode ) :
|
||||
@@ -92,6 +92,10 @@ void FileSystemModel::setRootPath( const std::string& rootPath ) {
|
||||
update();
|
||||
}
|
||||
|
||||
void FileSystemModel::reload() {
|
||||
setRootPath( mRootPath );
|
||||
}
|
||||
|
||||
void FileSystemModel::update() {
|
||||
mRoot = std::make_unique<Node>( mRootPath, *this );
|
||||
onModelUpdate();
|
||||
@@ -109,7 +113,7 @@ FileSystemModel::Node& FileSystemModel::nodeRef( const ModelIndex& index ) const
|
||||
|
||||
size_t FileSystemModel::rowCount( const ModelIndex& index ) const {
|
||||
Node& node = const_cast<Node&>( this->node( index ) );
|
||||
node.reifyIfNeeded( *this );
|
||||
node.refreshIfNeeded( *this );
|
||||
if ( node.info().isDirectory() )
|
||||
return node.mChildren.size();
|
||||
return 0;
|
||||
@@ -139,6 +143,8 @@ std::string FileSystemModel::columnName( const size_t& column ) const {
|
||||
return "Inode";
|
||||
case Column::SymlinkTarget:
|
||||
return "Symlink target";
|
||||
case Column::Path:
|
||||
return "Path";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -172,7 +178,7 @@ Variant FileSystemModel::data( const ModelIndex& index, Model::Role role ) const
|
||||
|
||||
if ( role == Role::Custom ) {
|
||||
eeASSERT( index.column() == Column::Name );
|
||||
return Variant( node.info().getFilepath() );
|
||||
return Variant( node.info().getFilepath().c_str() );
|
||||
}
|
||||
|
||||
if ( role == Role::Sort ) {
|
||||
@@ -195,6 +201,8 @@ Variant FileSystemModel::data( const ModelIndex& index, Model::Role role ) const
|
||||
return node.info().getInode();
|
||||
case Column::SymlinkTarget:
|
||||
return Variant( node.info().linksTo() );
|
||||
case Column::Path:
|
||||
return Variant( node.info().getFilepath().c_str() );
|
||||
default:
|
||||
eeASSERT( false );
|
||||
}
|
||||
@@ -220,6 +228,8 @@ Variant FileSystemModel::data( const ModelIndex& index, Model::Role role ) const
|
||||
return Variant( String::toString( node.info().getInode() ) );
|
||||
case Column::SymlinkTarget:
|
||||
return node.info().isLink() ? Variant( node.info().linksTo() ) : Variant();
|
||||
case Column::Path:
|
||||
return Variant( node.info().getFilepath().c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +254,7 @@ ModelIndex FileSystemModel::index( int row, int column, const ModelIndex& parent
|
||||
if ( row < 0 || column < 0 )
|
||||
return {};
|
||||
auto& node = this->node( parent );
|
||||
const_cast<Node&>( node ).reifyIfNeeded( *this );
|
||||
const_cast<Node&>( node ).refreshIfNeeded( *this );
|
||||
if ( static_cast<size_t>( row ) >= node.mChildren.size() )
|
||||
return {};
|
||||
return createIndex( row, column, &node.mChildren[row] );
|
||||
@@ -258,4 +268,25 @@ Drawable* FileSystemModel::iconFor( const Node& node, const ModelIndex& index )
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FileSystemModel::setMode( const Mode& mode ) {
|
||||
if ( mode != mMode ) {
|
||||
mMode = mode;
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemModel::updateNodeSelection( const ModelIndex& index, const bool selected ) {
|
||||
Node& node = const_cast<Node&>( this->node( index ) );
|
||||
node.setSelected( selected );
|
||||
setPreviouslySelectedIndex( index );
|
||||
}
|
||||
|
||||
ModelIndex FileSystemModel::getPreviouslySelectedIndex() const {
|
||||
return mPreviouslySelectedIndex;
|
||||
}
|
||||
|
||||
void FileSystemModel::setPreviouslySelectedIndex( const ModelIndex& previouslySelectedIndex ) {
|
||||
mPreviouslySelectedIndex = previouslySelectedIndex;
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Models
|
||||
|
||||
@@ -18,6 +18,10 @@ void Model::unregisterView( UIAbstractView* view ) {
|
||||
mViews.erase( view );
|
||||
}
|
||||
|
||||
void Model::refreshView() {
|
||||
forEachView( [&]( UIAbstractView* view ) { view->invalidateDraw(); } );
|
||||
}
|
||||
|
||||
void Model::registerView( UIAbstractView* view ) {
|
||||
mViews.insert( view );
|
||||
}
|
||||
@@ -43,4 +47,4 @@ bool Model::acceptsDrag( const ModelIndex&, const std::string& ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Model
|
||||
}}} // namespace EE::UI::Models
|
||||
|
||||
@@ -108,15 +108,7 @@ void UIPushButton::onAutoSize() {
|
||||
}
|
||||
}
|
||||
|
||||
void UIPushButton::onPaddingChange() {
|
||||
onSizeChange();
|
||||
|
||||
UIWidget::onPaddingChange();
|
||||
}
|
||||
|
||||
void UIPushButton::onSizeChange() {
|
||||
onAutoSize();
|
||||
|
||||
void UIPushButton::updateLayout() {
|
||||
Rectf autoPadding;
|
||||
|
||||
if ( mFlags & UI_AUTO_PADDING ) {
|
||||
@@ -225,6 +217,18 @@ void UIPushButton::onSizeChange() {
|
||||
if ( NULL != eWidget && eWidget->isVisible() ) {
|
||||
eWidget->setPixelsPosition( ePos );
|
||||
}
|
||||
}
|
||||
|
||||
void UIPushButton::onPaddingChange() {
|
||||
onSizeChange();
|
||||
|
||||
UIWidget::onPaddingChange();
|
||||
}
|
||||
|
||||
void UIPushButton::onSizeChange() {
|
||||
onAutoSize();
|
||||
|
||||
updateLayout();
|
||||
|
||||
UIWidget::onSizeChange();
|
||||
}
|
||||
@@ -237,7 +241,7 @@ void UIPushButton::setTheme( UITheme* Theme ) {
|
||||
}
|
||||
|
||||
void UIPushButton::onThemeLoaded() {
|
||||
onSizeChange();
|
||||
updateLayout();
|
||||
|
||||
UIWidget::onThemeLoaded();
|
||||
}
|
||||
@@ -245,7 +249,7 @@ void UIPushButton::onThemeLoaded() {
|
||||
UIPushButton* UIPushButton::setIcon( Drawable* Icon ) {
|
||||
if ( mIcon->getDrawable() != Icon ) {
|
||||
mIcon->setDrawable( Icon );
|
||||
onSizeChange();
|
||||
updateLayout();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -255,8 +259,10 @@ UIImage* UIPushButton::getIcon() const {
|
||||
}
|
||||
|
||||
UIPushButton* UIPushButton::setText( const String& text ) {
|
||||
mTextBox->setText( text );
|
||||
onSizeChange();
|
||||
if ( text != mTextBox->getText() ) {
|
||||
mTextBox->setText( text );
|
||||
updateLayout();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ void UIScrollableWidget::onContentSizeChange() {
|
||||
|
||||
Sizef UIScrollableWidget::getScrollableArea() const {
|
||||
Sizef contentSize( getContentSize() );
|
||||
Sizef size = getVisibleArea();
|
||||
Sizef size( getVisibleArea() );
|
||||
return contentSize - size;
|
||||
}
|
||||
|
||||
@@ -185,6 +185,10 @@ Sizef UIScrollableWidget::getVisibleArea() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
Rectf UIScrollableWidget::getVisibleRect() const {
|
||||
return Rectf( mScrollOffset, getVisibleArea() );
|
||||
}
|
||||
|
||||
void UIScrollableWidget::updateScroll() {
|
||||
Sizef totalScroll = getScrollableArea();
|
||||
Vector2f initScroll( mScrollOffset );
|
||||
@@ -238,6 +242,25 @@ void UIScrollableWidget::scrollToBottom() {
|
||||
mVScroll->setValue( 1 );
|
||||
}
|
||||
|
||||
void UIScrollableWidget::scrollToPosition( const Vector2f& pos, const bool& scrollVertically,
|
||||
const bool& scrollHorizontally ) {
|
||||
Rectf visibleRect( getVisibleRect() );
|
||||
if ( visibleRect.contains( pos ) )
|
||||
return;
|
||||
|
||||
if ( scrollVertically ) {
|
||||
if ( pos.y < visibleRect.Top || pos.y > visibleRect.Bottom ) {
|
||||
mVScroll->setValue( pos.y / getContentSize().y );
|
||||
}
|
||||
}
|
||||
|
||||
if ( scrollHorizontally ) {
|
||||
if ( pos.x < visibleRect.Left || pos.x > visibleRect.Right ) {
|
||||
mHScroll->setValue( pos.x / getContentSize().x );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UIScrollableWidget::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
if ( !checkPropertyDefinition( attribute ) )
|
||||
return false;
|
||||
|
||||
@@ -11,9 +11,8 @@ UISplitter* UISplitter::New() {
|
||||
UISplitter::UISplitter() :
|
||||
UILayout( "splitter" ),
|
||||
mOrientation( UIOrientation::Horizontal ),
|
||||
mSplitOnlyWhenNeeded( true ),
|
||||
mAlwaysShowSplitter( true ),
|
||||
mDivisionSplit( 0.5f ),
|
||||
mSplitPartition( StyleSheetLength( "50%" ) ),
|
||||
mFirstWidget( NULL ),
|
||||
mLastWidget( NULL ) {
|
||||
mFlags |= UI_OWNS_CHILDS_POSITION;
|
||||
@@ -77,13 +76,13 @@ void UISplitter::setAlwaysShowSplitter( bool alwaysShowSplitter ) {
|
||||
}
|
||||
}
|
||||
|
||||
const Float& UISplitter::getDivisionSplit() const {
|
||||
return mDivisionSplit;
|
||||
const StyleSheetLength& UISplitter::getSplitPartition() const {
|
||||
return mSplitPartition;
|
||||
}
|
||||
|
||||
void UISplitter::setDivisionSplit( const Float& divisionSplit ) {
|
||||
if ( eeclamp( divisionSplit, 0.f, 1.f ) != mDivisionSplit ) {
|
||||
mDivisionSplit = eeclamp( divisionSplit, 0.f, 1.f );
|
||||
void UISplitter::setSplitPartition( const StyleSheetLength& divisionSplit ) {
|
||||
if ( divisionSplit != mSplitPartition ) {
|
||||
mSplitPartition = divisionSplit;
|
||||
setLayoutDirty();
|
||||
}
|
||||
}
|
||||
@@ -113,6 +112,35 @@ UIWidget* UISplitter::getLastWidget() const {
|
||||
return mLastWidget;
|
||||
}
|
||||
|
||||
bool UISplitter::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
if ( !checkPropertyDefinition( attribute ) )
|
||||
return false;
|
||||
|
||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
||||
case PropertyId::SplitterPartition:
|
||||
setSplitPartition( StyleSheetLength( attribute.asString() ) );
|
||||
case PropertyId::SplitterAlwaysShow:
|
||||
setAlwaysShowSplitter( attribute.asBool() );
|
||||
default:
|
||||
return UILayout::applyProperty( attribute );
|
||||
}
|
||||
}
|
||||
|
||||
std::string UISplitter::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex ) {
|
||||
if ( NULL == propertyDef )
|
||||
return "";
|
||||
|
||||
switch ( propertyDef->getPropertyId() ) {
|
||||
case PropertyId::SplitterPartition:
|
||||
return getSplitPartition().toString();
|
||||
case PropertyId::SplitterAlwaysShow:
|
||||
return alwaysShowSplitter() ? "true" : "false";
|
||||
default:
|
||||
return UILayout::getPropertyString( propertyDef, propertyIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void UISplitter::onChildCountChange( Node* child, const bool& removed ) {
|
||||
if ( child != mSplitter ) {
|
||||
if ( !removed ) {
|
||||
@@ -131,6 +159,7 @@ void UISplitter::onChildCountChange( Node* child, const bool& removed ) {
|
||||
} else {
|
||||
mLastWidget = childWidget;
|
||||
}
|
||||
childWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
mSplitter->toFront();
|
||||
}
|
||||
} else {
|
||||
@@ -234,13 +263,19 @@ void UISplitter::updateFromDrag() {
|
||||
}
|
||||
|
||||
if ( UIOrientation::Horizontal == mOrientation ) {
|
||||
mDivisionSplit =
|
||||
( mSplitter->getPixelsPosition().x + mSplitter->getPixelsSize().getWidth() ) /
|
||||
( mSize.getWidth() - mRealPadding.Left - mRealPadding.Right );
|
||||
mSplitPartition = StyleSheetLength(
|
||||
eeclamp<Float>(
|
||||
( mSplitter->getPixelsPosition().x + mSplitter->getPixelsSize().getWidth() ) /
|
||||
( mSize.getWidth() - mRealPadding.Left - mRealPadding.Right ) * 100,
|
||||
0, 100 ),
|
||||
StyleSheetLength::Percentage );
|
||||
} else {
|
||||
mDivisionSplit =
|
||||
( mSplitter->getPixelsPosition().y + mSplitter->getPixelsSize().getHeight() ) /
|
||||
( mSize.getHeight() - mRealPadding.Top - mRealPadding.Bottom );
|
||||
mSplitPartition = StyleSheetLength(
|
||||
eeclamp<Float>(
|
||||
( mSplitter->getPixelsPosition().y + mSplitter->getPixelsSize().getHeight() ) /
|
||||
( mSize.getHeight() - mRealPadding.Top - mRealPadding.Bottom ) * 100,
|
||||
0, 100 ),
|
||||
StyleSheetLength::Percentage );
|
||||
}
|
||||
|
||||
mDirtyLayout = false;
|
||||
@@ -286,7 +321,7 @@ void UISplitter::updateLayout() {
|
||||
}
|
||||
}
|
||||
|
||||
if ( mSplitOnlyWhenNeeded && !mLastWidget ) {
|
||||
if ( !mLastWidget ) {
|
||||
mSplitter->setVisible( false )->setEnabled( false );
|
||||
|
||||
if ( mFirstWidget ) {
|
||||
@@ -311,7 +346,7 @@ void UISplitter::updateLayout() {
|
||||
: mSplitter->getPixelsSize().getHeight();
|
||||
}
|
||||
|
||||
Float firstSplit = ( totalSpace * mDivisionSplit );
|
||||
Float firstSplit = convertLength( mSplitPartition, totalSpace );
|
||||
Float secondSplit = totalSpace - firstSplit;
|
||||
|
||||
if ( mFirstWidget ) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <deque>
|
||||
#include <eepp/graphics/renderer/renderer.hpp>
|
||||
#include <eepp/ui/uilinearlayout.hpp>
|
||||
#include <eepp/ui/uipushbutton.hpp>
|
||||
@@ -96,49 +97,29 @@ void UITreeView::onColumnSizeChange( const size_t& ) {
|
||||
updateContentSize();
|
||||
}
|
||||
|
||||
class UITableRow : public UIWidget {
|
||||
public:
|
||||
UITableRow( const std::string& tag ) : UIWidget( tag ) {}
|
||||
|
||||
ModelIndex getCurIndex() const { return mCurIndex; }
|
||||
|
||||
void setCurIndex( const ModelIndex& curIndex ) { mCurIndex = curIndex; }
|
||||
|
||||
protected:
|
||||
virtual Uint32 onMessage( const NodeMessage* msg ) {
|
||||
if ( msg->getMsg() == NodeMessage::MouseDown && ( msg->getFlags() & EE_BUTTON_LMASK ) &&
|
||||
( !getEventDispatcher()->getMouseDownNode() ||
|
||||
UITableRow* UITreeView::createRow() {
|
||||
UITableRow* rowWidget = UITableRow::New( "table::row" );
|
||||
rowWidget->setParent( this );
|
||||
rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
rowWidget->reloadStyle( true, true, true );
|
||||
rowWidget->addEventListener( Event::MouseDown, [&]( const Event* event ) {
|
||||
if ( ( !getEventDispatcher()->getMouseDownNode() ||
|
||||
getEventDispatcher()->getMouseDownNode() == this ||
|
||||
isParentOf( getEventDispatcher()->getMouseDownNode() ) ) &&
|
||||
getEventDispatcher()->getNodeDragging() == nullptr ) {
|
||||
sendMouseEvent( Event::MouseDown, getEventDispatcher()->getMousePos(),
|
||||
msg->getFlags() );
|
||||
getSelection().set( event->getNode()->asType<UITableRow>()->getCurIndex() );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} );
|
||||
return rowWidget;
|
||||
}
|
||||
|
||||
ModelIndex mCurIndex;
|
||||
};
|
||||
|
||||
UIWidget* UITreeView::updateRow( const int& rowIndex, const ModelIndex& index,
|
||||
const Float& yOffset ) {
|
||||
UITableRow* UITreeView::updateRow( const int& rowIndex, const ModelIndex& index,
|
||||
const Float& yOffset ) {
|
||||
if ( rowIndex >= (int)mRows.size() )
|
||||
mRows.resize( rowIndex + 1, nullptr );
|
||||
UITableRow* rowWidget = nullptr;
|
||||
if ( mRows[rowIndex] == nullptr ) {
|
||||
rowWidget = eeNew( UITableRow, ( "table::row" ) );
|
||||
rowWidget->clipEnable();
|
||||
rowWidget->setParent( this );
|
||||
rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
rowWidget->reloadStyle( true, true, true );
|
||||
rowWidget->addEventListener( Event::MouseDown, [&]( const Event* event ) {
|
||||
if ( ( !getEventDispatcher()->getMouseDownNode() ||
|
||||
getEventDispatcher()->getMouseDownNode() == this ||
|
||||
isParentOf( getEventDispatcher()->getMouseDownNode() ) ) &&
|
||||
getEventDispatcher()->getNodeDragging() == nullptr ) {
|
||||
getSelection().set( event->getNode()->asType<UITableRow>()->getCurIndex() );
|
||||
}
|
||||
} );
|
||||
rowWidget = createRow();
|
||||
mRows[rowIndex] = rowWidget;
|
||||
} else {
|
||||
rowWidget = mRows[rowIndex];
|
||||
@@ -375,10 +356,63 @@ Uint32 UITreeView::onKeyDown( const KeyEvent& event ) {
|
||||
auto curIndex = getSelection().first();
|
||||
|
||||
switch ( event.getKeyCode() ) {
|
||||
case KEY_PAGEUP: {
|
||||
int pageSize = eefloor( getVisibleArea().getHeight() / getRowHeight() ) - 1;
|
||||
std::deque<std::pair<ModelIndex, Float>> deque;
|
||||
Float curY;
|
||||
traverseTree(
|
||||
[&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) {
|
||||
deque.push_back( {index, offsetY} );
|
||||
if ( (int)deque.size() > pageSize )
|
||||
deque.pop_front();
|
||||
if ( index == curIndex )
|
||||
return IterationDecision::Break;
|
||||
return IterationDecision::Continue;
|
||||
} );
|
||||
curY = deque.front().second - getHeaderHeight();
|
||||
getSelection().set( deque.front().first );
|
||||
scrollToPosition( {mScrollOffset.x, curY} );
|
||||
break;
|
||||
}
|
||||
case KEY_PAGEDOWN: {
|
||||
int pageSize = eefloor( getVisibleArea().getHeight() / getRowHeight() ) - 1;
|
||||
int counted = 0;
|
||||
bool foundStart = false;
|
||||
bool resultFound = false;
|
||||
ModelIndex foundIndex;
|
||||
Float curY;
|
||||
Float lastOffsetY;
|
||||
ModelIndex lastIndex;
|
||||
traverseTree(
|
||||
[&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) {
|
||||
if ( index == curIndex ) {
|
||||
foundStart = true;
|
||||
} else if ( foundStart ) {
|
||||
counted++;
|
||||
if ( counted == pageSize ) {
|
||||
foundIndex = index;
|
||||
curY = offsetY;
|
||||
resultFound = true;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
}
|
||||
lastOffsetY = offsetY;
|
||||
lastIndex = index;
|
||||
return IterationDecision::Continue;
|
||||
} );
|
||||
if ( !resultFound ) {
|
||||
foundIndex = lastIndex;
|
||||
curY = lastOffsetY;
|
||||
}
|
||||
curY += getRowHeight();
|
||||
getSelection().set( foundIndex );
|
||||
scrollToPosition( {mScrollOffset.x, curY} );
|
||||
break;
|
||||
}
|
||||
case KEY_UP: {
|
||||
ModelIndex prevIndex;
|
||||
ModelIndex foundIndex;
|
||||
Float curY;
|
||||
Float curY = 0;
|
||||
traverseTree(
|
||||
[&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) {
|
||||
if ( index == curIndex ) {
|
||||
@@ -404,7 +438,7 @@ Uint32 UITreeView::onKeyDown( const KeyEvent& event ) {
|
||||
case KEY_DOWN: {
|
||||
ModelIndex prevIndex;
|
||||
ModelIndex foundIndex;
|
||||
Float curY;
|
||||
Float curY = 0;
|
||||
traverseTree(
|
||||
[&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) {
|
||||
if ( prevIndex == curIndex ) {
|
||||
|
||||
@@ -257,6 +257,7 @@ void App::loadConfig() {
|
||||
mConfig.window.maximized = mIniState.getValueB( "window", "maximized", false );
|
||||
mConfig.window.pixelDensity = mIniState.getValueF( "window", "pixeldensity" );
|
||||
mConfig.window.winIcon = mIni.getValue( "window", "winicon", mResPath + "assets/icon/ee.png" );
|
||||
mConfig.window.panelPartition = mIniState.getValue( "window", "panel_partition", "15%" );
|
||||
mConfig.editor.showLineNumbers = mIni.getValueB( "editor", "show_line_numbers", true );
|
||||
mConfig.editor.showWhiteSpaces = mIni.getValueB( "editor", "show_white_spaces", true );
|
||||
mConfig.editor.highlightMatchingBracket =
|
||||
@@ -265,6 +266,7 @@ void App::loadConfig() {
|
||||
mIni.getValueB( "editor", "highlight_current_line", true );
|
||||
mConfig.editor.horizontalScrollbar = mIni.getValueB( "editor", "horizontal_scrollbar", false );
|
||||
mConfig.ui.fontSize = mIni.getValue( "ui", "font_size", "11dp" );
|
||||
mConfig.ui.showSidePanel = mIni.getValueB( "ui", "show_side_panel", true );
|
||||
mConfig.editor.trimTrailingWhitespaces =
|
||||
mIni.getValueB( "editor", "trim_trailing_whitespaces", false );
|
||||
mConfig.editor.forceNewLineAtEndOfFile =
|
||||
@@ -296,6 +298,9 @@ void App::saveConfig() {
|
||||
mIniState.setValueI( "window", "height", mConfig.window.size.getHeight() );
|
||||
mIniState.setValueB( "window", "maximized", mConfig.window.maximized );
|
||||
mIniState.setValueF( "window", "pixeldensity", mConfig.window.pixelDensity );
|
||||
mIniState.setValue( "window", "panel_partition",
|
||||
mProjectSplitter ? mProjectSplitter->getSplitPartition().toString()
|
||||
: "15%" );
|
||||
mIniState.setValue( "files", "recentfiles", String::join( mRecentFiles, ';' ) );
|
||||
mIni.setValueB( "editor", "show_line_numbers", mConfig.editor.showLineNumbers );
|
||||
mIni.setValueB( "editor", "show_white_spaces", mConfig.editor.showWhiteSpaces );
|
||||
@@ -305,6 +310,7 @@ void App::saveConfig() {
|
||||
mIni.setValueB( "editor", "horizontal_scrollbar", mConfig.editor.horizontalScrollbar );
|
||||
mIni.setValue( "editor", "font_size", mConfig.editor.fontSize.toString() );
|
||||
mIni.setValue( "ui", "font_size", mConfig.ui.fontSize.toString() );
|
||||
mIni.setValueB( "ui", "show_side_panel", mConfig.ui.showSidePanel );
|
||||
mIni.setValueB( "editor", "trim_trailing_whitespaces", mConfig.editor.trimTrailingWhitespaces );
|
||||
mIni.setValueB( "editor", "force_new_line_at_end_of_file",
|
||||
mConfig.editor.forceNewLineAtEndOfFile );
|
||||
@@ -547,101 +553,46 @@ void App::updateRecentFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
UIMenu* App::createViewMenu() {
|
||||
mViewMenu = UIPopUpMenu::New();
|
||||
mViewMenu->addCheckBox( "Show Line Numbers" )->setActive( mConfig.editor.showLineNumbers );
|
||||
mViewMenu->addCheckBox( "Show White Space" )->setActive( mConfig.editor.showWhiteSpaces );
|
||||
mViewMenu->addCheckBox( "Show Document Info" )->setActive( mConfig.editor.showDocInfo );
|
||||
mViewMenu->addCheckBox( "Highlight Matching Bracket" )
|
||||
->setActive( mConfig.editor.highlightMatchingBracket );
|
||||
mViewMenu->addCheckBox( "Highlight Current Line" )
|
||||
->setActive( mConfig.editor.highlightCurrentLine );
|
||||
mViewMenu->addCheckBox( "Highlight Selection Match" )
|
||||
->setActive( mConfig.editor.highlightSelectionMatch );
|
||||
mViewMenu->addCheckBox( "Enable Horizontal ScrollBar" )
|
||||
->setActive( mConfig.editor.horizontalScrollbar );
|
||||
mViewMenu->addCheckBox( "Enable Color Preview" )
|
||||
->setActive( mConfig.editor.colorPreview )
|
||||
->setTooltipText( "Enables a quick preview of a color when the mouse\n"
|
||||
"is hover a word that represents a color." );
|
||||
mViewMenu->addCheckBox( "Enable Color Picker" )
|
||||
->setActive( mConfig.editor.colorPickerSelection )
|
||||
->setTooltipText( "Enables the color picker tool when a double click selection\n"
|
||||
"is done over a word representing a color." );
|
||||
mViewMenu->addCheckBox( "Enable Auto Complete" )
|
||||
->setActive( mConfig.editor.autoComplete )
|
||||
->setTooltipText( "Auto complete shows the completion popup as you type, so you can fill\n"
|
||||
"in long words by typing only a few characters." );
|
||||
mViewMenu->add( "Line Breaking Column" );
|
||||
mViewMenu->addSeparator();
|
||||
mViewMenu->add( "UI Scale Factor (Pixel Density)", findIcon( "pixel-density" ) );
|
||||
mViewMenu->add( "UI Font Size", findIcon( "font-size" ) );
|
||||
mViewMenu->add( "Editor Font Size", findIcon( "font-size" ) );
|
||||
mViewMenu->addSeparator();
|
||||
mViewMenu->addCheckBox( "Full Screen Mode" )
|
||||
void App::showSidePanel( bool show ) {
|
||||
if ( show == mSidePanel->isVisible() )
|
||||
return;
|
||||
|
||||
if ( show ) {
|
||||
mSidePanel->setVisible( true );
|
||||
mSidePanel->setParent( mProjectSplitter );
|
||||
mProjectSplitter->swap();
|
||||
} else {
|
||||
mSidePanel->setVisible( false );
|
||||
mSidePanel->setParent( mUISceneNode->getRoot() );
|
||||
}
|
||||
}
|
||||
|
||||
UIMenu* App::createWindowMenu() {
|
||||
mWindowMenu = UIPopUpMenu::New();
|
||||
mWindowMenu->add( "UI Scale Factor (Pixel Density)", findIcon( "pixel-density" ) );
|
||||
mWindowMenu->add( "UI Font Size", findIcon( "font-size" ) );
|
||||
mWindowMenu->add( "Editor Font Size", findIcon( "font-size" ) );
|
||||
mWindowMenu->addSeparator();
|
||||
mWindowMenu->addCheckBox( "Full Screen Mode" )
|
||||
->setShortcutText( getKeybind( "fullscreen-toggle" ) )
|
||||
->setId( "fullscreen-mode" );
|
||||
mViewMenu->addSeparator();
|
||||
mViewMenu->add( "Split Left", findIcon( "split-horizontal" ), getKeybind( "split-left" ) );
|
||||
mViewMenu->add( "Split Right", findIcon( "split-horizontal" ), getKeybind( "split-right" ) );
|
||||
mViewMenu->add( "Split Top", findIcon( "split-vertical" ), getKeybind( "split-top" ) );
|
||||
mViewMenu->add( "Split Bottom", findIcon( "split-vertical" ), getKeybind( "split-bottom" ) );
|
||||
mViewMenu->addSeparator();
|
||||
mViewMenu->add( "Zoom In", findIcon( "zoom-in" ), getKeybind( "font-size-grow" ) );
|
||||
mViewMenu->add( "Zoom Out", findIcon( "zoom-out" ), getKeybind( "font-size-shrink" ) );
|
||||
mViewMenu->add( "Zoom Reset", findIcon( "zoom-reset" ), getKeybind( "font-size-reset" ) );
|
||||
mViewMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
|
||||
mWindowMenu->addCheckBox( "Show Side Panel" )->setActive( mConfig.ui.showSidePanel );
|
||||
mWindowMenu->addSeparator();
|
||||
mWindowMenu->add( "Split Left", findIcon( "split-horizontal" ), getKeybind( "split-left" ) );
|
||||
mWindowMenu->add( "Split Right", findIcon( "split-horizontal" ), getKeybind( "split-right" ) );
|
||||
mWindowMenu->add( "Split Top", findIcon( "split-vertical" ), getKeybind( "split-top" ) );
|
||||
mWindowMenu->add( "Split Bottom", findIcon( "split-vertical" ), getKeybind( "split-bottom" ) );
|
||||
mWindowMenu->addSeparator();
|
||||
mWindowMenu->add( "Zoom In", findIcon( "zoom-in" ), getKeybind( "font-size-grow" ) );
|
||||
mWindowMenu->add( "Zoom Out", findIcon( "zoom-out" ), getKeybind( "font-size-shrink" ) );
|
||||
mWindowMenu->add( "Zoom Reset", findIcon( "zoom-reset" ), getKeybind( "font-size-reset" ) );
|
||||
mWindowMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
|
||||
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) )
|
||||
return;
|
||||
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
|
||||
if ( item->getText() == "Show Line Numbers" ) {
|
||||
mConfig.editor.showLineNumbers = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setShowLineNumber( mConfig.editor.showLineNumbers );
|
||||
} );
|
||||
} else if ( item->getText() == "Show White Space" ) {
|
||||
mConfig.editor.showWhiteSpaces = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setShowWhitespaces( mConfig.editor.showWhiteSpaces );
|
||||
} );
|
||||
} else if ( item->getText() == "Highlight Matching Bracket" ) {
|
||||
mConfig.editor.highlightMatchingBracket = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHighlightMatchingBracket( mConfig.editor.highlightMatchingBracket );
|
||||
} );
|
||||
} else if ( item->getText() == "Highlight Current Line" ) {
|
||||
mConfig.editor.highlightCurrentLine = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHighlightCurrentLine( mConfig.editor.highlightCurrentLine );
|
||||
} );
|
||||
} else if ( item->getText() == "Highlight Selection Match" ) {
|
||||
mConfig.editor.highlightSelectionMatch = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHighlightSelectionMatch( mConfig.editor.highlightSelectionMatch );
|
||||
} );
|
||||
} else if ( item->getText() == "Enable Horizontal ScrollBar" ) {
|
||||
mConfig.editor.horizontalScrollbar = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHorizontalScrollBarEnabled( mConfig.editor.horizontalScrollbar );
|
||||
} );
|
||||
} else if ( item->getText() == "Enable Color Picker" ) {
|
||||
mConfig.editor.colorPickerSelection = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setEnableColorPickerOnSelection( mConfig.editor.colorPickerSelection );
|
||||
} );
|
||||
} else if ( item->getText() == "Enable Auto Complete" ) {
|
||||
setAutoComplete( item->asType<UIMenuCheckBox>()->isActive() );
|
||||
} else if ( item->getText() == "Enable Color Preview" ) {
|
||||
mConfig.editor.colorPreview = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setEnableColorPickerOnSelection( mConfig.editor.colorPreview );
|
||||
} );
|
||||
} else if ( item->getText() == "Show Document Info" ) {
|
||||
mConfig.editor.showDocInfo = item->asType<UIMenuCheckBox>()->isActive();
|
||||
if ( mDocInfo )
|
||||
mDocInfo->setVisible( mConfig.editor.showDocInfo );
|
||||
if ( mEditorSplitter->getCurEditor() )
|
||||
updateDocInfo( mEditorSplitter->getCurEditor()->getDocument() );
|
||||
if ( item->getText() == "Show Side Panel" ) {
|
||||
mConfig.ui.showSidePanel = item->asType<UIMenuCheckBox>()->isActive();
|
||||
showSidePanel( mConfig.ui.showSidePanel );
|
||||
} else if ( item->getText() == "UI Scale Factor (Pixel Density)" ) {
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::INPUT, "Set the UI scale factor (pixel density):\nMinimum value is "
|
||||
@@ -712,6 +663,104 @@ UIMenu* App::createViewMenu() {
|
||||
msgBox->closeWindow();
|
||||
} );
|
||||
setFocusEditorOnClose( msgBox );
|
||||
} else if ( "Zoom In" == item->getText() ) {
|
||||
mEditorSplitter->zoomIn();
|
||||
} else if ( "Zoom Out" == item->getText() ) {
|
||||
mEditorSplitter->zoomOut();
|
||||
} else if ( "Zoom Reset" == item->getText() ) {
|
||||
mEditorSplitter->zoomReset();
|
||||
} else if ( "Full Screen Mode" == item->getText() ) {
|
||||
runCommand( "fullscreen-toggle" );
|
||||
} else {
|
||||
String text = String( event->getNode()->asType<UIMenuItem>()->getText() ).toLower();
|
||||
String::replaceAll( text, " ", "-" );
|
||||
String::replaceAll( text, "/", "-" );
|
||||
runCommand( text );
|
||||
}
|
||||
} );
|
||||
return mWindowMenu;
|
||||
}
|
||||
|
||||
UIMenu* App::createViewMenu() {
|
||||
mViewMenu = UIPopUpMenu::New();
|
||||
mViewMenu->addCheckBox( "Show Line Numbers" )->setActive( mConfig.editor.showLineNumbers );
|
||||
mViewMenu->addCheckBox( "Show White Space" )->setActive( mConfig.editor.showWhiteSpaces );
|
||||
mViewMenu->addCheckBox( "Show Document Info" )->setActive( mConfig.editor.showDocInfo );
|
||||
mViewMenu->addCheckBox( "Highlight Matching Bracket" )
|
||||
->setActive( mConfig.editor.highlightMatchingBracket );
|
||||
mViewMenu->addCheckBox( "Highlight Current Line" )
|
||||
->setActive( mConfig.editor.highlightCurrentLine );
|
||||
mViewMenu->addCheckBox( "Highlight Selection Match" )
|
||||
->setActive( mConfig.editor.highlightSelectionMatch );
|
||||
mViewMenu->addCheckBox( "Enable Horizontal ScrollBar" )
|
||||
->setActive( mConfig.editor.horizontalScrollbar );
|
||||
mViewMenu->addCheckBox( "Enable Color Preview" )
|
||||
->setActive( mConfig.editor.colorPreview )
|
||||
->setTooltipText( "Enables a quick preview of a color when the mouse\n"
|
||||
"is hover a word that represents a color." );
|
||||
mViewMenu->addCheckBox( "Enable Color Picker" )
|
||||
->setActive( mConfig.editor.colorPickerSelection )
|
||||
->setTooltipText( "Enables the color picker tool when a double click selection\n"
|
||||
"is done over a word representing a color." );
|
||||
mViewMenu->addCheckBox( "Enable Auto Complete" )
|
||||
->setActive( mConfig.editor.autoComplete )
|
||||
->setTooltipText( "Auto complete shows the completion popup as you type, so you can fill\n"
|
||||
"in long words by typing only a few characters." );
|
||||
mViewMenu->add( "Line Breaking Column" );
|
||||
|
||||
mViewMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
|
||||
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) )
|
||||
return;
|
||||
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
|
||||
if ( item->getText() == "Show Line Numbers" ) {
|
||||
mConfig.editor.showLineNumbers = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setShowLineNumber( mConfig.editor.showLineNumbers );
|
||||
} );
|
||||
} else if ( item->getText() == "Show White Space" ) {
|
||||
mConfig.editor.showWhiteSpaces = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setShowWhitespaces( mConfig.editor.showWhiteSpaces );
|
||||
} );
|
||||
} else if ( item->getText() == "Highlight Matching Bracket" ) {
|
||||
mConfig.editor.highlightMatchingBracket = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHighlightMatchingBracket( mConfig.editor.highlightMatchingBracket );
|
||||
} );
|
||||
} else if ( item->getText() == "Highlight Current Line" ) {
|
||||
mConfig.editor.highlightCurrentLine = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHighlightCurrentLine( mConfig.editor.highlightCurrentLine );
|
||||
} );
|
||||
} else if ( item->getText() == "Highlight Selection Match" ) {
|
||||
mConfig.editor.highlightSelectionMatch = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHighlightSelectionMatch( mConfig.editor.highlightSelectionMatch );
|
||||
} );
|
||||
} else if ( item->getText() == "Enable Horizontal ScrollBar" ) {
|
||||
mConfig.editor.horizontalScrollbar = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setHorizontalScrollBarEnabled( mConfig.editor.horizontalScrollbar );
|
||||
} );
|
||||
} else if ( item->getText() == "Enable Color Picker" ) {
|
||||
mConfig.editor.colorPickerSelection = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setEnableColorPickerOnSelection( mConfig.editor.colorPickerSelection );
|
||||
} );
|
||||
} else if ( item->getText() == "Enable Auto Complete" ) {
|
||||
setAutoComplete( item->asType<UIMenuCheckBox>()->isActive() );
|
||||
} else if ( item->getText() == "Enable Color Preview" ) {
|
||||
mConfig.editor.colorPreview = item->asType<UIMenuCheckBox>()->isActive();
|
||||
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
|
||||
editor->setEnableColorPickerOnSelection( mConfig.editor.colorPreview );
|
||||
} );
|
||||
} else if ( item->getText() == "Show Document Info" ) {
|
||||
mConfig.editor.showDocInfo = item->asType<UIMenuCheckBox>()->isActive();
|
||||
if ( mDocInfo )
|
||||
mDocInfo->setVisible( mConfig.editor.showDocInfo );
|
||||
if ( mEditorSplitter->getCurEditor() )
|
||||
updateDocInfo( mEditorSplitter->getCurEditor()->getDocument() );
|
||||
|
||||
} else if ( item->getText() == "Line Breaking Column" ) {
|
||||
UIMessageBox* msgBox =
|
||||
UIMessageBox::New( UIMessageBox::INPUT, "Set Line Breaking Column:\n"
|
||||
@@ -732,14 +781,6 @@ UIMenu* App::createViewMenu() {
|
||||
}
|
||||
} );
|
||||
setFocusEditorOnClose( msgBox );
|
||||
} else if ( "Zoom In" == item->getText() ) {
|
||||
mEditorSplitter->zoomIn();
|
||||
} else if ( "Zoom Out" == item->getText() ) {
|
||||
mEditorSplitter->zoomOut();
|
||||
} else if ( "Zoom Reset" == item->getText() ) {
|
||||
mEditorSplitter->zoomReset();
|
||||
} else if ( "Full Screen Mode" == item->getText() ) {
|
||||
runCommand( "fullscreen-toggle" );
|
||||
} else {
|
||||
String text = String( event->getNode()->asType<UIMenuItem>()->getText() ).toLower();
|
||||
String::replaceAll( text, " ", "-" );
|
||||
@@ -1009,7 +1050,7 @@ void App::updateDocInfo( TextDocument& doc ) {
|
||||
if ( mConfig.editor.showDocInfo && mDocInfoText ) {
|
||||
mDocInfoText->setText( String::format(
|
||||
"line: %lld / %lu col: %lld %s", doc.getSelection().start().line() + 1,
|
||||
doc.linesCount(), doc.getSelection().start().column() + 1,
|
||||
doc.linesCount(), doc.getSelection().start().column(),
|
||||
doc.getLineEnding() == TextDocument::LineEnding::LF ? "LF" : "CRLF" ) );
|
||||
}
|
||||
}
|
||||
@@ -1115,7 +1156,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
|
||||
doc.setCommand( "close-app", [&] { closeApp(); } );
|
||||
doc.setCommand( "fullscreen-toggle", [&]() {
|
||||
mWindow->toggleFullscreen();
|
||||
mViewMenu->find( "fullscreen-mode" )
|
||||
mWindowMenu->find( "fullscreen-mode" )
|
||||
->asType<UIMenuCheckBox>()
|
||||
->setActive( !mWindow->isWindowed() );
|
||||
} );
|
||||
@@ -1208,6 +1249,7 @@ void App::createSettingsMenu() {
|
||||
mSettingsMenu->addSubMenu( "Document", nullptr, createDocumentMenu() );
|
||||
mSettingsMenu->addSubMenu( "Edit", nullptr, createEditMenu() );
|
||||
mSettingsMenu->addSubMenu( "View", nullptr, createViewMenu() );
|
||||
mSettingsMenu->addSubMenu( "Window", nullptr, createWindowMenu() );
|
||||
mSettingsMenu->addSeparator();
|
||||
mSettingsMenu->add( "Close", findIcon( "document-close" ), getKeybind( "close-doc" ) );
|
||||
mSettingsMenu->addSeparator();
|
||||
@@ -1528,8 +1570,13 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
|
||||
mUISceneNode->bind( "search_bar", mSearchBarLayout );
|
||||
mUISceneNode->bind( "doc_info", mDocInfo );
|
||||
mUISceneNode->bind( "doc_info_text", mDocInfoText );
|
||||
mUISceneNode->bind( "panel", mSidePanel );
|
||||
mUISceneNode->bind( "project_splitter", mProjectSplitter );
|
||||
mDocInfo->setVisible( mConfig.editor.showDocInfo );
|
||||
mSearchBarLayout->setVisible( false )->setEnabled( false );
|
||||
mProjectSplitter->setSplitPartition( StyleSheetLength( mConfig.window.panelPartition ) );
|
||||
if ( !mConfig.ui.showSidePanel )
|
||||
showSidePanel( mConfig.ui.showSidePanel );
|
||||
|
||||
mEditorSplitter = UICodeEditorSplitter::New(
|
||||
this, mUISceneNode,
|
||||
@@ -1544,13 +1591,12 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
|
||||
|
||||
mConsole = eeNew( Console, ( fontMono, true, true, 1024 * 1000, 0, mWindow ) );
|
||||
|
||||
UISplitter* projectSplitter = mUISceneNode->find<UISplitter>( "project_splitter" );
|
||||
projectSplitter->setDivisionSplit( 0.15f );
|
||||
UITreeView* tree = mUISceneNode->find<UITreeView>( "project_view" );
|
||||
tree->setColumnsHidden( {FileSystemModel::Icon, FileSystemModel::Size,
|
||||
FileSystemModel::Group, FileSystemModel::Inode,
|
||||
FileSystemModel::Owner, FileSystemModel::SymlinkTarget,
|
||||
FileSystemModel::Permissions, FileSystemModel::ModificationTime},
|
||||
FileSystemModel::Permissions, FileSystemModel::ModificationTime,
|
||||
FileSystemModel::Path},
|
||||
true );
|
||||
tree->setHeadersVisible( false );
|
||||
tree->addEventListener( Event::OnModelEvent, [&]( const Event* event ) {
|
||||
@@ -1558,8 +1604,8 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
|
||||
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
|
||||
Variant vPath( modelEvent->getModel()->data( modelEvent->getModelIndex(),
|
||||
Model::Role::Custom ) );
|
||||
if ( vPath.isValid() && vPath.is( Variant::Type::String ) ) {
|
||||
std::string path ( vPath.asString() );
|
||||
if ( vPath.isValid() && vPath.is( Variant::Type::cstr ) ) {
|
||||
std::string path( vPath.asCStr() );
|
||||
UITab* tab = mEditorSplitter->isDocumentOpen( path );
|
||||
if ( !tab ) {
|
||||
mEditorSplitter->loadFileFromPathInNewTab( path );
|
||||
|
||||
@@ -37,6 +37,7 @@ class UISearchBar : public UILinearLayout {
|
||||
|
||||
struct UIConfig {
|
||||
StyleSheetLength fontSize{12, StyleSheetLength::Dp};
|
||||
bool showSidePanel{true};
|
||||
};
|
||||
|
||||
struct WindowConfig {
|
||||
@@ -44,6 +45,7 @@ struct WindowConfig {
|
||||
Sizei size{1280, 720};
|
||||
std::string winIcon;
|
||||
bool maximized{false};
|
||||
std::string panelPartition;
|
||||
};
|
||||
|
||||
struct CodeEditorConfig {
|
||||
@@ -148,6 +150,9 @@ class App : public UICodeEditorSplitter::Client {
|
||||
AppConfig mConfig;
|
||||
UIPopUpMenu* mDocMenu{nullptr};
|
||||
UIPopUpMenu* mViewMenu{nullptr};
|
||||
UIPopUpMenu* mWindowMenu{nullptr};
|
||||
UISplitter* mProjectSplitter{nullptr};
|
||||
UITabWidget* mSidePanel{nullptr};
|
||||
UICodeEditorSplitter* mEditorSplitter{nullptr};
|
||||
std::string mInitColorScheme;
|
||||
std::map<std::string, std::string> mKeybindings;
|
||||
@@ -159,6 +164,8 @@ class App : public UICodeEditorSplitter::Client {
|
||||
std::string mResPath;
|
||||
AutoCompleteModule* mAutoCompleteModule{nullptr};
|
||||
|
||||
void showSidePanel( bool show );
|
||||
|
||||
void onFileDropped( String file );
|
||||
|
||||
void onTextDropped( String text );
|
||||
@@ -195,6 +202,8 @@ class App : public UICodeEditorSplitter::Client {
|
||||
|
||||
UIMenu* createEditMenu();
|
||||
|
||||
UIMenu* createWindowMenu();
|
||||
|
||||
Drawable* findIcon( const std::string& name );
|
||||
|
||||
UIMenu* createDocumentMenu();
|
||||
|
||||
Reference in New Issue
Block a user