Added: #2478 Simple editor - editing texts (not WYSIWYG, textual as in classic editor)

This commit is contained in:
Jindra Petřík
2025-07-06 18:51:24 +02:00
parent 8d0952a8fc
commit 0ac9ec748c
9 changed files with 214 additions and 21 deletions
@@ -104,11 +104,18 @@ public class EasySwfPanel extends JPanel {
private static final String PROPERTIES_DOCUMENT = "Document";
private static final String PROPERTIES_INSTANCE = "Instance";
private static final String PROPERTIES_TEXT = "Text";
private DocumentPropertiesPanel documentPropertiesPanel;
private InstancePropertiesPanel instancePropertiesPanel;
private InstancePropertiesPanel textInstancePropertiesPanel;
private final MainPanel mainPanel;
/**
*
* @param mainPanel
*/
public EasySwfPanel(MainPanel mainPanel) {
this.mainPanel = mainPanel;
setLayout(new BorderLayout());
stagePanel = new ImagePanel();
@@ -476,9 +483,11 @@ public class EasySwfPanel extends JPanel {
documentPropertiesPanel = new DocumentPropertiesPanel(undoManager);
propertiesPanel.setLayout(new CardLayout());
instancePropertiesPanel = new InstancePropertiesPanel(this, undoManager);
instancePropertiesPanel = new InstancePropertiesPanel(this, undoManager, false);
textInstancePropertiesPanel = new InstancePropertiesPanel(this, undoManager, true);
propertiesPanel.add(documentPropertiesPanel, PROPERTIES_DOCUMENT);
propertiesPanel.add(instancePropertiesPanel, PROPERTIES_INSTANCE);
propertiesPanel.add(textInstancePropertiesPanel, PROPERTIES_TEXT);
rightTabbedPane.addTab(EasyStrings.translate("properties"), propertiesPanel);
@@ -553,9 +562,12 @@ public class EasySwfPanel extends JPanel {
}
}
});
this.mainPanel = mainPanel;
}
public MainPanel getMainPanel() {
return mainPanel;
}
private void updatePropertiesPanel() {
CardLayout cl = (CardLayout) propertiesPanel.getLayout();
List<PlaceObjectTypeTag> places = getSelectedPlaceTags();
@@ -563,6 +575,16 @@ public class EasySwfPanel extends JPanel {
cl.show(propertiesPanel, PROPERTIES_DOCUMENT);
return;
}
if (places.size() == 1) {
int chid = places.get(0).getCharacterId();
if (chid > -1) {
if (places.get(0).getSwf().getCharacter(chid) instanceof TextTag) {
textInstancePropertiesPanel.update();
cl.show(propertiesPanel, PROPERTIES_TEXT);
return;
}
}
}
instancePropertiesPanel.update();
cl.show(propertiesPanel, PROPERTIES_INSTANCE);
}
@@ -592,6 +614,7 @@ public class EasySwfPanel extends JPanel {
timelineLabel.setText("");
documentPropertiesPanel.setSwf(null);
instancePropertiesPanel.update();
textInstancePropertiesPanel.update();
} else {
SWF swf = timelined.getSwf();
documentPropertiesPanel.setSwf(swf);
@@ -617,6 +640,7 @@ public class EasySwfPanel extends JPanel {
closeTimelineButton.setVisible(true);
}
instancePropertiesPanel.update();
textInstancePropertiesPanel.update();
}
updateUndos();
}
@@ -645,7 +669,8 @@ public class EasySwfPanel extends JPanel {
if (stagePanel.getTimelined() == null) {
return;
}
Main.getMainFrame().getPanel().updateUiWithCurrentOpenable();
updatePropertiesPanel();
Main.getMainFrame().getPanel().updateUiWithCurrentOpenable();
}
public void dispose() {
@@ -44,6 +44,7 @@ public abstract class AbstractPropertiesPanel extends JPanel {
protected String titleIdentifier;
private final Map<String, JPanel> cardHeaders = new LinkedHashMap<>();
private final Map<String, JPanel> cardContents = new LinkedHashMap<>();
private final Map<String, JLabel> cardPlusMinusLabels = new LinkedHashMap<>();
@@ -114,6 +115,7 @@ public abstract class AbstractPropertiesPanel extends JPanel {
//contents.setMaximumSize(new Dimension(getPreferredSize().width, contents.getPreferredSize().height + 10));
//cardPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
//cardPanel.setAlignmentY(Component.TOP_ALIGNMENT);
cardHeaders.put(id, headerPanel);
cardContents.put(id, contents);
cardPlusMinusLabels.put(id, plusMinusLabel);
@@ -143,6 +145,15 @@ public abstract class AbstractPropertiesPanel extends JPanel {
plusMinusLabel.setText("" + PLUS_CHAR);
}
}
protected void setCardVisible(String id, boolean visible) {
JPanel contents = cardContents.get(id);
if (!visible) {
contents.setVisible(false);
}
JPanel header = cardHeaders.get(id);
header.setVisible(visible);
}
protected void addToGrid(GridBagLayout layout, Container parent, Component component, int x, int y) {
addToGrid(layout, parent, component, x, y, 1, 1);
@@ -29,12 +29,16 @@ import com.jpexs.decompiler.flash.easygui.properties.PropertyChangeDoableOperati
import com.jpexs.decompiler.flash.easygui.properties.PropertyValidationInterface;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.gui.BoundsChangeListener;
import com.jpexs.decompiler.flash.gui.FasterScrollPane;
import com.jpexs.decompiler.flash.gui.PopupButton;
import com.jpexs.decompiler.flash.gui.RegistrationPointPosition;
import com.jpexs.decompiler.flash.gui.TextPanel;
import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.gui.ViewMessages;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.converters.PlaceObjectTypeConverter;
import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Timelined;
@@ -79,6 +83,7 @@ import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
@@ -123,6 +128,8 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
private final JComboBox<String> backgroundComboBox = new JComboBox<>();
private final JPanel backgroundColorPanel = new JPanel();
private final JLabel backgroundColorLabel = new JLabel();
private final TextPanel textPanel;
private final FiltersTreeTable filtersTable;
@@ -132,7 +139,7 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
public InstancePropertiesPanel(EasySwfPanel swfPanel, UndoManager undoManager) {
public InstancePropertiesPanel(EasySwfPanel swfPanel, UndoManager undoManager, boolean withText) {
super("instance");
setLayout(new BorderLayout());
@@ -245,7 +252,7 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
colorEffectPanel.add(new JPanel(), gbc);
JPanel displayPanel = new JPanel();
gridBag = new GridBagLayout();
displayPanel.setLayout(gridBag);
@@ -371,7 +378,9 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
gbc.fill = GridBagConstraints.HORIZONTAL;
displayPanel.add(new JPanel(), gbc);
textPanel = new TextPanel(swfPanel.getMainPanel(), undoManager);
JPanel filtersPanel = new JPanel(new BorderLayout());
filtersPanel.setPreferredSize(new Dimension(400, 200));
filtersTable = new FiltersTreeTable();
JPanel filtersToolbar = new JPanel(new FlowLayout(FlowLayout.LEFT));
@@ -562,7 +571,10 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
addCard(propertiesPanel, "positionSize", null, positionSizePanel, gbc, false);
addCard(propertiesPanel, "colorEffect", null, colorEffectPanel, gbc, false);
addCard(propertiesPanel, "display", null, displayPanel, gbc, false);
addCard(propertiesPanel, "filters", null, filtersPanel, gbc, true);
addCard(propertiesPanel, "filters", null, filtersPanel, gbc, !withText);
if (withText) {
addCard(propertiesPanel, "text", null, textPanel, gbc, true);
}
setCardOpened("positionSize", true);
@@ -865,7 +877,10 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
}
});
add(propertiesPanel, BorderLayout.CENTER);
JScrollPane sp1 = new FasterScrollPane(propertiesPanel);
sp1.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
add(sp1, BorderLayout.CENTER);
swfPanel.getStagePanel().addBoundsChangeListener(new BoundsChangeListener() {
@Override
@@ -925,7 +940,8 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
Set<List<FILTER>> filters = new HashSet<>();
Set<Integer> ratio = new HashSet<>();
TextTag text = null;
for (DepthState ds : dss) {
if (ds == null) {
continue;
@@ -960,8 +976,16 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
backgroundColor.add(ds.backGroundColor);
filters.add(ds.filters);
ratio.add(ds.ratio == -1 ? 0 : ds.ratio);
CharacterTag ch = ds.getCharacter();
if (ch instanceof TextTag) {
text = (TextTag) ch;
}
}
if (dss.size() > 1) {
text = null;
}
if (visible.size() == 0) {
return;
}
@@ -1056,6 +1080,10 @@ public class InstancePropertiesPanel extends AbstractPropertiesPanel {
} else {
filtersTable.setFilters(null);
}
if (text != null) {
textPanel.setText(text);
}
updating = false;
revalidate();
}
@@ -236,7 +236,7 @@ public class FontPanel extends JPanel implements TagEditorPanel {
TextTag textTag = (TextTag) tag;
if (textTag.getFontIds().contains(fontId)) {
String text = textTag.getFormattedText(true).text;
mainPanel.saveText(textTag, text, null, null);
mainPanel.saveText(textTag, text, null, null, null);
}
}
}
@@ -46,7 +46,10 @@ import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys;
import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode;
import com.jpexs.decompiler.flash.easygui.DoableOperation;
import com.jpexs.decompiler.flash.easygui.EasyPanel;
import com.jpexs.decompiler.flash.easygui.EasyStrings;
import com.jpexs.decompiler.flash.easygui.UndoManager;
import com.jpexs.decompiler.flash.exporters.BinaryDataExporter;
import com.jpexs.decompiler.flash.exporters.Font4Exporter;
import com.jpexs.decompiler.flash.exporters.FontExporter;
@@ -5024,7 +5027,42 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
};
}
public boolean saveText(TextTag textTag, String formattedText, String[] texts, LineMarkedEditorPane editor) {
public boolean saveText(TextTag textTag, String formattedText, String[] texts, LineMarkedEditorPane editor, UndoManager undoManager) {
if (undoManager != null) {
String prevText = textTag.getFormattedText(false).text;
if (saveTextInternal(textTag, formattedText, texts, editor)) {
undoManager.doOperation(new DoableOperation() {
boolean first = true;
@Override
public void doOperation() {
if (first) {
first = false;
return;
}
saveTextInternal(textTag, formattedText, texts, editor);
}
@Override
public void undoOperation() {
saveTextInternal(textTag, prevText, texts, editor);
}
@Override
public String getDescription() {
return EasyStrings.translate("action.change").replace("%item%", EasyStrings.translate("action.change.text"));
}
}, textTag.getSwf());
return true;
}
return false;
}
return saveTextInternal(textTag, formattedText, texts, editor);
}
private boolean saveTextInternal(TextTag textTag, String formattedText, String[] texts, LineMarkedEditorPane editor) {
try {
if (textTag.setFormattedText(getMissingCharacterHandler(), formattedText, texts)) {
return true;
@@ -386,7 +386,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
private void createParametersPanel() {
displayWithPreview = new JPanel(new CardLayout());
textPanel = new TextPanel(mainPanel);
textPanel = new TextPanel(mainPanel, null);
displayWithPreview.add(textPanel, CARDTEXTPANEL);
fontPanel = new FontPanel(mainPanel);
@@ -18,6 +18,9 @@ package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.easygui.DoableOperation;
import com.jpexs.decompiler.flash.easygui.EasyStrings;
import com.jpexs.decompiler.flash.easygui.UndoManager;
import com.jpexs.decompiler.flash.gui.controls.JRepeatButton;
import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane;
import com.jpexs.decompiler.flash.helpers.HighlightedText;
@@ -41,7 +44,7 @@ import java.util.logging.Logger;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.JScrollPane;
import javax.swing.text.BadLocationException;
/**
@@ -86,9 +89,15 @@ public class TextPanel extends JPanel implements TagEditorPanel {
private final JButton undoChangesButton;
private TextTag textTag;
private final UndoManager undoManager;
private String oldText = null;
public TextPanel(final MainPanel mainPanel) {
public TextPanel(final MainPanel mainPanel, UndoManager undoManager) {
super(new BorderLayout());
this.undoManager = undoManager;
this.mainPanel = mainPanel;
textSearchPanel = new SearchPanel<>(new FlowLayout(), mainPanel);
@@ -97,14 +106,18 @@ public class TextPanel extends JPanel implements TagEditorPanel {
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
topPanel.add(textSearchPanel);
textValue = new LineMarkedEditorPane();
add(new FasterScrollPane(textValue), BorderLayout.CENTER);
JScrollPane sp = new FasterScrollPane(textValue);
sp.setPreferredSize(new Dimension(200, 200));
//sp.setMaximumSize(sp.getSize());
//sp.setPreferredSize(sp.getMaximumSize());
add(sp, BorderLayout.CENTER);
textValue.setFont(Configuration.getSourceFont());
textValue.changeContentType("text/swftext");
textValue.addTextChangedListener(this::textChanged);
JPanel textButtonsPanel = new JPanel();
textButtonsPanel.setLayout(new FlowLayout(SwingConstants.WEST));
textButtonsPanel.setMinimumSize(new Dimension(10, textButtonsPanel.getMinimumSize().height));
textButtonsPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
//textButtonsPanel.setMinimumSize(new Dimension(10, textButtonsPanel.getMinimumSize().height));
selectPreviousTagButton = createButton(null, "arrowup16", "selectPreviousTag", e -> mainPanel.previousTag());
selectNextTagButton = createButton(null, "arrowdown16", "selectNextTag", e -> mainPanel.nextTag());
@@ -126,7 +139,9 @@ public class TextPanel extends JPanel implements TagEditorPanel {
textButtonsPanel.add(decreaseTranslateXButton);
textButtonsPanel.add(increaseTranslateXButton);
textButtonsPanel.add(changeCaseButton);
textButtonsPanel.add(undoChangesButton);
if (undoManager == null) {
textButtonsPanel.add(undoChangesButton);
}
textButtonsPanel.setAlignmentX(0);
topPanel.add(textButtonsPanel);
@@ -148,6 +163,10 @@ public class TextPanel extends JPanel implements TagEditorPanel {
add(buttonsPanel, BorderLayout.SOUTH);
}
public TextTag getTextTag() {
return textTag;
}
private JButton createButton(String textResource, String iconName, String toolTipResource, ActionListener actionListener) {
return createButton(textResource, iconName, toolTipResource, actionListener, false);
}
@@ -180,6 +199,7 @@ public class TextPanel extends JPanel implements TagEditorPanel {
}
textValue.setText(formattedText);
oldText = formattedText;
}
public void setText(TextTag textTag) {
@@ -203,6 +223,7 @@ public class TextPanel extends JPanel implements TagEditorPanel {
undoChangesButton.setVisible(!readOnly);
selectPreviousTagButton.setVisible(mainPanel.getCurrentView() == MainPanel.VIEW_RESOURCES);
selectNextTagButton.setVisible(mainPanel.getCurrentView() == MainPanel.VIEW_RESOURCES);
updateButtonsVisibility();
}
private boolean isModified() {
@@ -281,10 +302,15 @@ public class TextPanel extends JPanel implements TagEditorPanel {
}
}
boolean editable = textValue.isEditable();
textValue.setEditable(true);
textValue.replaceSelection(selected.toString());
saveText(true);
textValue.setEditable(editable);
if (!editable) {
saveText(true);
updateButtonsVisibility();
updateButtonsVisibility();
}
textTag.getSwf().clearImageCache();
mainPanel.repaintTree();
@@ -340,12 +366,13 @@ public class TextPanel extends JPanel implements TagEditorPanel {
private void cancelText() {
setEditText(false);
refresh();
mainPanel.reload(true);
mainPanel.clearEditingStatus();
}
private void saveText(boolean refresh) {
if (mainPanel.saveText(textTag, textValue.getText(), null, textValue)) {
if (mainPanel.saveText(textTag, textValue.getText(), null, textValue, undoManager)) {
setEditText(false);
setModified(false);
textTag.getSwf().clearImageCache();
@@ -357,16 +384,74 @@ public class TextPanel extends JPanel implements TagEditorPanel {
}
private void textAlign(TextAlign textAlign) {
if (undoManager != null) {
String prevText = textTag.getFormattedText(false).text;
undoManager.doOperation(new DoableOperation() {
@Override
public void doOperation() {
textAlignInternal(textAlign);
}
@Override
public void undoOperation() {
try {
textTag.setFormattedText(null, prevText, null);
} catch (TextParseException ex) {
//ignore
}
}
@Override
public String getDescription() {
return EasyStrings.translate("action.change").replace("%item%", EasyStrings.translate("action.change.text"));
}
}, textTag.getSwf());
return;
}
textAlignInternal(textAlign);
}
private void textAlignInternal(TextAlign textAlign) {
if (textTag.alignText(textAlign)) {
updateButtonsVisibility();
refresh();
textTag.getSwf().clearImageCache();
mainPanel.repaintTree();
}
}
private void translateX(int delta, int repeatCount) {
if (undoManager != null) {
undoManager.doOperation(new DoableOperation() {
@Override
public void doOperation() {
translateXInternal(delta, repeatCount);
}
@Override
public void undoOperation() {
translateXInternal(-delta, repeatCount);
}
@Override
public String getDescription() {
return EasyStrings.translate("action.change").replace("%item%", EasyStrings.translate("action.change.text"));
}
}, textTag.getSwf());
return;
}
translateXInternal(delta, repeatCount);
}
private void translateXInternal(int delta, int repeatCount) {
if (textTag.translateText(delta * (repeatCount + 1))) {
updateButtonsVisibility();
refresh();
textTag.getSwf().clearImageCache();
mainPanel.repaintTree();
}
@@ -194,4 +194,8 @@ property.instance.filters.innerGlow = inner glow
property.instance.filters.menu.enable = Enable or disable filter
property.instance.display.ratio = Ratio
property.instance.display.ratio = Ratio
#after 24.0.1
properties.instance.header.text = Text
action.change.text = Text