#include "SpriteEditor.h" #include "SpritePicker.h" #include "TilePicker.h" #include "ColorWells.h" #include "ColorEditor.h" #include "BorderPanel.h" #include "labels.h" #include "SelectionManager.h" #include "GridOverlay.h" extern ColorWells *mainColorStack; extern SpritePicker *mainSpritePicker; extern TilePicker *mainTilePicker; extern ColorEditor *mainColorEditor; extern int gameWidth, gameHeight; extern TextGL *largeTextFixed; template <> void SizeLimitedVector::deleteElementOfType( SpriteResource inElement ) { // no delete necessary } SpriteEditor::SpriteEditor( ScreenGL *inScreen ) : Editor( inScreen ), mUndoStack( MAX_UNDOS, false ) { mCloseButton->setToolTip( "tip_closeEdit_sprite" ); /* LabelGL *titleLabel = new LabelGL( 0.75, 0.5, 0.25, 0.1, "Tile Editor", largeText ); mSidePanel->add( titleLabel ); */ mSidePanel->add( mainColorStack ); mainColorStack->addActionListener( this ); mSidePanel->add( mainSpritePicker ); mainSpritePicker->addActionListener( this ); mPickerTransButton = mainSpritePicker->getTransButton(); mPickerTransButton->addActionListener( this ); mEditColorButton = new EditButtonGL( mainColorStack->getAnchorX() - 9, mainColorStack->getAnchorY() + mainColorStack->getHeight() - 7, 8, 8 ); mSidePanel->add( mEditColorButton ); mEditColorButton->addActionListener( this ); mEditColorButton->setToolTip( "tip_edit_color" ); mEditPaletteButton = new EditButtonGL( mainColorStack->getAnchorX() - 9, mainColorStack->getAnchorY() + mainColorStack->getHeight() - 51, 8, 8 ); mSidePanel->add( mEditPaletteButton ); mEditPaletteButton->addActionListener( this ); mEditPaletteButton->setToolTip( "tip_edit_palette" ); /* // 1-pixel wide white border // inner panel to provide a 1-pixel wide black gap BorderPanel *workingColorBorderPanel = new BorderPanel( 256, 206, 48, 14, new Color( 0, 0, 0, 1 ), new Color( 1, 1, 1, 1 ), 1 ); // smaller to leave black gap GUIPanelGL *workingColorPanel = new GUIPanelGL( 258, 208, 44, 10, mWorkingColor ); mSidePanel->add( workingColorBorderPanel ); workingColorBorderPanel->add( workingColorPanel ); mAddButton = new AddButtonGL( 272, 184, 16, 16 ); mSidePanel->add( mAddButton ); mAddButton->addActionListener( this ); */ double offset = P; double buttonSize = (gameHeight - 2 * offset - 8) / P; rgbaColor c = { { 0,0,0,255 } }; for( int y=0; yadd( mButtonGrid[y][x] ); mButtonGrid[y][x]->addActionListener( this ); } } mToolSet = new DrawToolSet( 2, 42 ); mMainPanel->add( mToolSet ); mToolSet->addActionListener( this ); mSelectionButton = new SelectableButtonGL( new Sprite( "selection.tga", true ), 1, mToolSet->getAnchorX() + mToolSet->getWidth() + 5, 42, 20, 20 ); mMainPanel->add( mSelectionButton ); mSelectionButton->setSelected( false ); mSelectionButton->addActionListener( this ); mSelectionButton->setToolTip( "tip_selection" ); EightPixelLabel *fieldLabel = new EightPixelLabel( 150, 54, "spriteName" ); mMainPanel->add( fieldLabel ); int fieldHeight = 8; int fieldWidth = 8 * 10; const char *defaultText = "default"; mNameField = new TextFieldGL( 150, 43, fieldWidth, fieldHeight, 1, defaultText, largeTextFixed, new Color( 0.75, 0.75, 0.75 ), new Color( 0.75, 0.75, 0.75 ), new Color( 0.15, 0.15, 0.15 ), new Color( 0.75, 0.75, 0.75 ), 10, false ); mMainPanel->add( mNameField ); mNameField->setFocus( true ); //mNameField->lockFocus( true ); mNameField->setCursorPosition( strlen( defaultText ) ); mNameField->addActionListener( this ); // center add button double gridEdge = 8 + P * buttonSize; double extra = gameHeight - gridEdge; // center it vertically on sprite picker double addY = mainSpritePicker->getAnchorY() + mainSpritePicker->getHeight() - 15; double sideButtonsX = gridEdge + (extra - 16) / 2; mAddButton = new AddButtonGL( sideButtonsX, addY, 16, 16 ); mMainPanel->add( mAddButton ); mAddButton->addActionListener( this ); mAddButton->setToolTip( "tip_addSprite" ); mAddAction = false; double miniButtonSize = P + 4; mMiniViewButton = new TwoSpriteButtonGL( NULL, NULL, 1, gridEdge + ( extra - miniButtonSize ) / 2, addY - 24, miniButtonSize, miniButtonSize ); mMainPanel->add( mMiniViewButton ); mTransformToolSet = new TransformToolSet( sideButtonsX, mMiniViewButton->getAnchorY() - 8 - 100, true ) ; mMainPanel->add( mTransformToolSet ); mTransformToolSet->addActionListener( this ); // old: // align with grid bottom double undoButtonY = gameWidth - ( 48 + P * buttonSize ); // new // down more to make room for Trans button undoButtonY -= 10; mUndoButton = new UndoButtonGL( sideButtonsX, undoButtonY, 16, 16 ); mMainPanel->add( mUndoButton ); mUndoButton->addActionListener( this ); mUndoButton->setEnabled( false ); mRedoButton = new RedoButtonGL( sideButtonsX, undoButtonY + 19, 16, 16 ); mMainPanel->add( mRedoButton ); mRedoButton->addActionListener( this ); mRedoButton->setEnabled( false ); mEraseButton = new SelectableButtonGL( new Sprite( "erase.tga", true ), 1, sideButtonsX - 2, undoButtonY + 20 + 17, 20, 20 ); mMainPanel->add( mEraseButton ); mEraseButton->setSelected( false ); mEraseButton->addActionListener( this ); mEraseButton->setToolTip( "tip_erase" ); setSpriteToEdit( mainSpritePicker->getSelectedResource() ); mPenDown = false; // listen for tile changes mainTilePicker->addActionListener( this ); // fully-opaque gray to avoid color distortions Color gridColor( 0.25, 0.25, 0.25, 1.0 ); GridOverlay *overlay = new GridOverlay( 8, gameWidth - ( 48 + P * buttonSize ), P * buttonSize, P * buttonSize, P, gridColor ); mMainPanel->add( overlay ); postConstruction(); } SpriteEditor::~SpriteEditor() { mSidePanel->remove( mainColorStack ); mSidePanel->remove( mainSpritePicker ); } void SpriteEditor::setSpriteToEdit( SpriteResource inSprite ) { mSpriteToEdit = inSprite; for( int y=0; ysetColor( mSpriteToEdit.getColor( x, y ) ); mButtonGrid[y][x]->setHighlight( mSpriteToEdit.getTrans( x, y ) ); } } refreshMiniView(); char *name = mSpriteToEdit.getSpriteName(); mNameField->setText( name ); mNameField->setCursorPosition( strlen( name ) ); delete [] name; } void SpriteEditor::refreshMiniView() { char useTrans = mainSpritePicker->isTransOn(); // don't use cached version mMiniViewButton->setFrontSprite( mSpriteToEdit.getSprite( useTrans, false ) ); if( useTrans ) { // backdrop behind sprite when transparency shown mMiniViewButton->setSprite( mainTilePicker->getBackgroundTile().getSprite() ); } else { // no backdrop mMiniViewButton->setSprite( NULL ); } } char SpriteEditor::recursiveFill( int inX, int inY, rgbaColor inOldColor, char inOldTrans, rgbaColor inNewColor, drawTool inTool ) { char fillable = // filling non-trans area, and colors match ( !inOldTrans && ! mSpriteToEdit.getTrans( inX, inY ) && equal( mSpriteToEdit.getColor( inX, inY ), inOldColor ) ) // or filling a trans area, and this pixel is trans || ( inOldTrans && mSpriteToEdit.getTrans( inX, inY ) ); char alreadyFilled = ! mSpriteToEdit.getTrans( inX, inY ) && equal( mSpriteToEdit.getColor( inX, inY ), inNewColor ); if( fillable && ! alreadyFilled ) { mButtonGrid[inY][inX]->setColor( inNewColor ); mSpriteToEdit.editSprite( inX, inY, inNewColor ); // always turn trans off wherever user draws with colors mSpriteToEdit.editTrans( inX, inY, false ); mButtonGrid[inY][inX]->setHighlight( false ); // call on neighbors if( inTool == fill || inTool == horLine ) { if( inX > 0 ) { recursiveFill( inX - 1, inY, inOldColor, inOldTrans, inNewColor, inTool ); } if( inX < P - 1 ) { recursiveFill( inX + 1, inY, inOldColor, inOldTrans, inNewColor, inTool ); } } if( inTool == fill || inTool == verLine ) { if( inY > 0 ) { recursiveFill( inX, inY - 1, inOldColor, inOldTrans, inNewColor, inTool ); } if( inY < P - 1 ) { recursiveFill( inX, inY + 1, inOldColor, inOldTrans, inNewColor, inTool ); } } return true; } return false; } char SpriteEditor::recursiveFill( int inX, int inY, rgbaColor inOldColor, char inOldTrans, char inNewTrans, drawTool inTool ) { if( equal( mSpriteToEdit.getColor( inX, inY ), inOldColor ) && mSpriteToEdit.getTrans( inX, inY ) == inOldTrans && mSpriteToEdit.getTrans( inX, inY ) != inNewTrans ) { mButtonGrid[inY][inX]->setHighlight( inNewTrans ); mSpriteToEdit.editTrans( inX, inY, inNewTrans ); // call on neighbors if( inTool == fill || inTool == horLine ) { if( inX > 0 ) { recursiveFill( inX - 1, inY, inOldColor, inOldTrans, inNewTrans, inTool ); } if( inX < P - 1 ) { recursiveFill( inX + 1, inY, inOldColor, inOldTrans, inNewTrans, inTool ); } } if( inTool == fill || inTool == verLine ) { if( inY > 0 ) { recursiveFill( inX, inY - 1, inOldColor, inOldTrans, inNewTrans, inTool ); } if( inY < P - 1 ) { recursiveFill( inX, inY + 1, inOldColor, inOldTrans, inNewTrans, inTool ); } } return true; } return false; } char SpriteEditor::recursiveSelectionFill( int inX, int inY, rgbaColor inOldColor, char inOldTrans, char inOldSelection, char inNewSelection, drawTool inTool ) { if( ( equal( mSpriteToEdit.getColor( inX, inY ), inOldColor ) || ( inOldTrans && mSpriteToEdit.getTrans( inX, inY ) ) ) && SelectionManager::isInSelection( inX, inY ) == inOldSelection && SelectionManager::isInSelection( inX, inY ) != inNewSelection ) { SelectionManager::toggleSelection( inX, inY, inNewSelection ); mButtonGrid[inY][inX]->setSelection( inNewSelection ); if( inNewSelection ) { SelectionManager::setColor( inX, inY, mSpriteToEdit.getColor( inX, inY ) ); SelectionManager::setTrans( inX, inY, mSpriteToEdit.getTrans( inX, inY ) ); } // call on neighbors if( inTool == fill || inTool == horLine ) { if( inX > 0 ) { recursiveSelectionFill( inX - 1, inY, inOldColor, inOldTrans, inOldSelection, inNewSelection, inTool ); } if( inX < P - 1 ) { recursiveSelectionFill( inX + 1, inY, inOldColor, inOldTrans, inOldSelection, inNewSelection, inTool ); } } if( inTool == fill || inTool == verLine ) { if( inY > 0 ) { recursiveSelectionFill( inX, inY - 1, inOldColor, inOldTrans, inOldSelection, inNewSelection, inTool ); } if( inY < P - 1 ) { recursiveSelectionFill( inX, inY + 1, inOldColor, inOldTrans, inOldSelection, inNewSelection, inTool ); } } return true; } return false; } void SpriteEditor::toggleErase() { mEraseButton->setSelected( ! mEraseButton->getSelected() ); char showErase = mEraseButton->getSelected(); /* // FIXME for( int y=0; ysetHighlight( mSpriteToEdit.getTrans( x, y ) && showTrans ); } } */ if( showErase && mSelectionButton->getSelected() ) { // only one on at a time toggleSelection(); } // update display of trans in mini view refreshMiniView(); } void SpriteEditor::toggleSelection() { mSelectionButton->setSelected( ! mSelectionButton->getSelected() ); char showSel = mSelectionButton->getSelected(); for( int y=0; ysetSelection( SelectionManager::isInSelection( x, y ) && showSel ); } } // commit selection only when it is turned on // (right before it is turned off, the last edits of selection have updated // the selected colors) if( showSel ) { // copy colors for( int y=0; ygetSelected() ) { // only one on at a time toggleErase(); } clearStampOverlay(); } void SpriteEditor::clearStampOverlay() { // turn all overlays off for( int by=0; by setOverlay( false ); } } } void SpriteEditor::colorEditorClosed() { // it might have been closed by an EditPalette button press if( mainColorEditor->mEditPalettePressed ) { showPaletteEditor(); } } void SpriteEditor::actionPerformed( GUIComponent *inTarget ) { // superclass Editor::actionPerformed( inTarget ); if( inTarget == mainColorStack ) { // new color picked on stack // non-erase mode if( mEraseButton->getSelected() ) { toggleErase(); } } else if( inTarget == mainSpritePicker ) { if( ! mAddAction && ! mainSpritePicker->wasLastActionFromPress() ) { // will change sprite mUndoStack.push_back( mSpriteToEdit ); mUndoButton->setEnabled( true ); setSpriteToEdit( mainSpritePicker->getSelectedResource() ); // new branch... "redo" future now impossible mRedoStack.deleteAll(); mRedoButton->setEnabled( false ); } } else if( inTarget == mPickerTransButton ) { refreshMiniView(); } else if( inTarget == mainTilePicker ) { // tile change... tile used behind sprite transparency refreshMiniView(); } else if( inTarget == mNameField ) { mUndoStack.push_back( mSpriteToEdit ); mUndoButton->setEnabled( true ); // new branch... "redo" future now impossible mRedoStack.deleteAll(); mRedoButton->setEnabled( false ); mSpriteToEdit.editSpriteName( mNameField->getText() ); } else if( inTarget == mAddButton ) { addSprite(); } else if( inTarget == mEditColorButton ) { mainColorEditor->setEditPaletteButtonVisible( true ); showColorEditor(); } else if( inTarget == mEditPaletteButton ) { showPaletteEditor(); } else if( inTarget == mUndoButton ) { int lastIndex = mUndoStack.size() - 1; SpriteResource last = *( mUndoStack.getElement( lastIndex ) ); mUndoStack.deleteElement( lastIndex ); if( mUndoStack.size() == 0 ) { mUndoButton->setEnabled( false ); } mRedoStack.push_back( mSpriteToEdit ); mRedoButton->setEnabled( true ); setSpriteToEdit( last ); } else if( inTarget == mRedoButton ) { int nextIndex = mRedoStack.size() - 1; SpriteResource next = *( mRedoStack.getElement( nextIndex ) ); mRedoStack.deleteElement( nextIndex ); if( mRedoStack.size() == 0 ) { mRedoButton->setEnabled( false ); } mUndoStack.push_back( mSpriteToEdit ); mUndoButton->setEnabled( true ); setSpriteToEdit( next ); } else if( inTarget == mEraseButton ) { toggleErase(); } else if( inTarget == mSelectionButton ) { toggleSelection(); } else if( inTarget == mTransformToolSet ) { mUndoStack.push_back( mSpriteToEdit ); mUndoButton->setEnabled( true ); // new branch... "redo" future now impossible mRedoStack.deleteAll(); mRedoButton->setEnabled( false ); SpriteResource oldSpriteState = mSpriteToEdit; switch( mTransformToolSet->getLastPressed() ) { case flipH: { for( int y=0; ysetColor( flipColor ); mSpriteToEdit.editSprite( x, y, flipColor ); mSpriteToEdit.editTrans( x, y, flipTrans ); } } } break; case flipV: { for( int y=0; ysetColor( flipColor ); mSpriteToEdit.editSprite( x, y, flipColor ); mSpriteToEdit.editTrans( x, y, flipTrans ); } } } break; case rotateCCW: { for( int y=0; ysetColor( flipColor ); mSpriteToEdit.editSprite( x, y, flipColor ); mSpriteToEdit.editTrans( x, y, flipTrans ); } } } break; case rotateCW: { for( int y=0; ysetColor( flipColor ); mSpriteToEdit.editSprite( x, y, flipColor ); mSpriteToEdit.editTrans( x, y, flipTrans ); } } } break; case clear: { rgbaColor black; black.comp.r = 0; black.comp.g = 0; black.comp.b = 0; black.comp.a = 255; for( int y=0; ysetColor( black ); mSpriteToEdit.editSprite( x, y, black ); mSpriteToEdit.editTrans( x, y, true ); } } } break; case colorize: { rgbaColor c = mainColorStack->getSelectedColor(); for( int y=0; ysetColor( oldColor ); mSpriteToEdit.editSprite( x, y, oldColor ); } } } break; } char showTrans = true; for( int y=0; ysetHighlight( mSpriteToEdit.getTrans( x, y ) && showTrans ); } } refreshMiniView(); } else if( inTarget == mToolSet ) { clearStampOverlay(); } else if( !mainColorEditor->getDragging() ){ // check grid // but only if not in the middle of drag-picking a color in the editor char found = false; for( int y=0; y

wasLastActionHover() ) { found = true; SpriteResource oldSpriteState = mSpriteToEdit; char changed = false; switch( mToolSet->getSelected() ) { case pen: { if( mEraseButton->getSelected() ) { // add transparency with pen if( !mPenDown ) { // set ink until released mTransInk = true; mPenDown = true; changed = true; } // use ink already set mSpriteToEdit.editTrans( x, y, mTransInk ); mButtonGrid[y][x]->setHighlight( mTransInk ); refreshMiniView(); } else if( mSelectionButton->getSelected() ) { // edit selection with pen if( !mPenDown ) { // set ink until released mSelectionInk = ! SelectionManager:: isInSelection( x, y ); mPenDown = true; changed = true; } // use ink already set SelectionManager::toggleSelection( x, y, mSelectionInk ); mButtonGrid[y][x]->setSelection( mSelectionInk ); if( mSelectionInk ) { SelectionManager::setColor( x, y, mSpriteToEdit.getColor( x, y ) ); SelectionManager::setTrans( x, y, mSpriteToEdit.getTrans( x, y ) ); } } else { if( mainColorEditor->isVisible() ) { // push edited color onto stack first mainColorEditor->addColor(); } // edit pixel colors rgbaColor c = mainColorStack->getSelectedColor(); if( ! equal( c, mSpriteToEdit.getColor( x, y ) ) || mSpriteToEdit.getTrans( x, y ) ) { mButtonGrid[y][x]->setColor( c ); mSpriteToEdit.editSprite( x, y, c ); // always turn trans off wherever user // draws with colors mSpriteToEdit.editTrans( x, y, false ); mButtonGrid[y][x]->setHighlight( false ); refreshMiniView(); // don't count single pen dots as undoable // until pen is released // save undo state only on on initial // presses if( !mPenDown ) { mPenDown = true; changed = true; } } } if( !mButtonGrid[y][x]->isPressed() ) { // a release mPenDown = false; } } break; case horLine: case verLine: case fill: if( mEraseButton->getSelected() ) { // add to trans if( !mPenDown ) { mTransInk = true; } changed = recursiveFill( x, y, mSpriteToEdit.getColor( x, y ), mSpriteToEdit.getTrans( x, y ), mTransInk, mToolSet->getSelected() ); refreshMiniView(); if( !mPenDown ) { mPenDown = true; } if( !mButtonGrid[y][x]->isPressed() ) { // a release mPenDown = false; } } else if( mSelectionButton->getSelected() ) { if( !mPenDown ) { mSelectionInk = ! SelectionManager:: isInSelection( x, y ); } recursiveSelectionFill( x, y, mSpriteToEdit.getColor( x, y ), mSpriteToEdit.getTrans( x, y ), SelectionManager:: isInSelection( x, y ), mSelectionInk, mToolSet->getSelected() ); if( !mPenDown ) { mPenDown = true; } if( !mButtonGrid[y][x]->isPressed() ) { // a release mPenDown = false; } } else { if( mainColorEditor->isVisible() ) { // push edited color onto stack first mainColorEditor->addColor(); } changed = recursiveFill( x, y, mSpriteToEdit.getColor( x, y ), mSpriteToEdit.getTrans( x, y ), mainColorStack->getSelectedColor(), mToolSet->getSelected() ); refreshMiniView(); } break; case pickColor: if( mSpriteToEdit.getTrans( x, y ) ) { // trans ink here, switch to erase mode // thus "picking up" trans ink if( ! mEraseButton->getSelected() ) { toggleErase(); } } else { // pick up color mainColorStack->pushColor( mSpriteToEdit.getColor( x, y ) ); // non-erase mode if( mEraseButton->getSelected() ) { toggleErase(); } } break; case stamp: { if( !mPenDown ) { changed = true; mPenDown = true; } // insert colors from selection intPair center = SelectionManager::getSelectionCenter(); for( int sy=0; sy= 0 && ix >= 0 ) { rgbaColor c = SelectionManager:: getColor( sx, sy ); char t = SelectionManager:: getTrans( sx, sy ); // always turn trans off wherever // user draws with colors, // unless that spot in selection is // trans mButtonGrid[iy][ix]->setColor( c ); mButtonGrid[iy][ix]->setHighlight( t ); mSpriteToEdit.editSprite( ix, iy, c ); mSpriteToEdit.editTrans( ix, iy, t ); } } } } refreshMiniView(); if( !mButtonGrid[y][x]->isPressed() ) { // a release mPenDown = false; } } break; } if( changed ) { mUndoStack.push_back( oldSpriteState ); mUndoButton->setEnabled( true ); // new branch... "redo" future now impossible mRedoStack.deleteAll(); mRedoButton->setEnabled( false ); } } else if( inTarget == mButtonGrid[y][x] && mButtonGrid[y][x]->wasLastActionHover() ) { found = true; if( mToolSet->getSelected() == stamp ) { // preview of selection that will be inserted // upon click // first, turn all overlays off for( int by=0; by setOverlay( false ); } } intPair center = SelectionManager::getSelectionCenter(); for( int sy=0; sy= 0 && ix >= 0 ) { rgbaColor c = SelectionManager:: getColor( sx, sy ); char t = SelectionManager:: getTrans( sx, sy ); mButtonGrid[iy][ix]-> setOverlayColor( c ); mButtonGrid[iy][ix]-> setOverlayTrans( t ); mButtonGrid[iy][ix]-> setOverlay( true ); } } } } } } } } } } void SpriteEditor::editorClosing() { addSprite(); } void SpriteEditor::addSprite() { mAddAction = true; mSpriteToEdit.finishEdit(); mainSpritePicker->setSelectedResource( mSpriteToEdit, true ); mAddAction = false; }