Added: Simple editor - Button timeline header

This commit is contained in:
Jindra Petřík
2025-05-17 17:55:51 +02:00
parent b8c67a2bc3
commit dd49a7e73b
6 changed files with 106 additions and 62 deletions
@@ -31,7 +31,6 @@ import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import com.jpexs.helpers.Helper;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -52,7 +51,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -76,7 +74,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
private Timeline timeline;
public static final int FRAME_WIDTH = 8;
private static final int FRAME_WIDTH = 8;
private static final int BUTTON_FRAME_WIDTH = 40;
private static final int MARKER_SIZE = 4;
public static final int FRAME_HEIGHT = 18;
@@ -223,6 +225,20 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
});
}
public static int getFrameWidthForTimeline(Timeline timeline) {
if (timeline == null) {
return FRAME_WIDTH;
}
if (timeline.timelined instanceof ButtonTag) {
return BUTTON_FRAME_WIDTH;
}
return FRAME_WIDTH;
}
public int getFrameWidth() {
return getFrameWidthForTimeline(timeline);
}
@Override
protected void paintComponent(Graphics g1) {
@@ -232,18 +248,20 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
g.fillRect(0, 0, getWidth(), getHeight());
if (timeline == null) {
return;
}
int frameWidth = getFrameWidth();
Set<Integer> emptyFrames = new LinkedHashSet<>();
if (timeline.timelined instanceof ButtonTag) {
emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames();
frameWidth = BUTTON_FRAME_WIDTH;
}
Rectangle clip = g.getClipBounds();
int frameWidth = FRAME_WIDTH;
int frameHeight = FRAME_HEIGHT;
int start_f = clip.x / frameWidth;
int start_d = clip.y / frameHeight;
@@ -285,14 +303,14 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
Frame fr = timeline.getFrame(f);
if (fr != null && !fr.actions.isEmpty()) {
if (firstAction) {
drawBlock(g, getEmptyFrameColor(), 0, 0, f, BlockType.EMPTY);
drawBlock(g, getEmptyFrameColor(), 0, 0, f, BlockType.EMPTY, frameWidth);
}
int f2 = f + 1;
while (f2 <= max_f && timeline.getFrame(f2) != null && timeline.getFrame(f2).actions.isEmpty()) {
f2++;
}
drawBlock(g, getEmptyFrameColor(), 0, f, f2 - f, BlockType.EMPTY);
drawBlock(g, getEmptyFrameColor(), 0, f, f2 - f, BlockType.EMPTY, frameWidth);
g.setColor(A_COLOR);
g.setFont(getFont().deriveFont(FONT_SIZE));
g.drawString("a", f * frameWidth + frameWidth / 2 - awidth / 2, frameHeight / 2);
@@ -322,11 +340,11 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
for (int f = start_f2; f <= end_f2; f++) {
DepthState fl = timeline.getFrame(f).layers.get(d);
if (emptyFrames.contains(f)) {
fl = null;
}
boolean motionTween = fl == null ? false : fl.motionTween;
DepthState flNext = f < max_f ? timeline.getFrame(f + 1).layers.get(d) : null;
@@ -372,7 +390,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
blockType = shapeTween ? BlockType.SHAPE_TWEEN : motionTween ? BlockType.MOTION_TWEEN : BlockType.NORMAL;
}
drawBlock(g, backColor, d, draw_f, num_frames, blockType);
drawBlock(g, backColor, d, draw_f, num_frames, blockType, frameWidth);
}
}
@@ -383,8 +401,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
}
}
private void drawBlock(Graphics2D g, Color backColor, int depth, int frame, int num_frames, BlockType blockType) {
int frameWidth = FRAME_WIDTH;
private void drawBlock(Graphics2D g, Color backColor, int depth, int frame, int num_frames, BlockType blockType, int frameWidth) {
int frameHeight = FRAME_HEIGHT;
for (int n = frame; n < frame + num_frames; n++) {
@@ -429,9 +446,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
g.setColor(KEY_COLOR);
}
if (blockType == BlockType.EMPTY) {
g.drawOval(frame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2);
g.drawOval(frame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight * 3 / 4 - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE);
} else {
g.fillOval(frame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2);
g.fillOval(frame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight * 3 / 4 - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE);
}
if (num_frames > 1) {
@@ -442,7 +459,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
g.setColor(KEY_COLOR);
}
if (isTween) {
g.fillOval(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight * 3 / 4 - frameWidth / 2 / 2, frameWidth / 2, frameWidth / 2);
g.fillOval(endFrame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight * 3 / 4 - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE);
} else {
if (cursor != null && cursor.contains(new Point(endFrame, depth))) {
@@ -450,13 +467,13 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
} else {
g.setColor(STOP_COLOR);
}
g.fillRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2);
g.fillRect(endFrame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight / 2 - 2, MARKER_SIZE, frameHeight / 2);
if (cursor != null && cursor.contains(new Point(endFrame, depth))) {
g.setBackground(getSelectedColorText());
} else {
g.setColor(STOP_BORDER_COLOR);
}
g.drawRect(endFrame * frameWidth + frameWidth / 4, depth * frameHeight + frameHeight / 2 - 2, frameWidth / 2, frameHeight / 2);
g.drawRect(endFrame * frameWidth + frameWidth / 2 - MARKER_SIZE / 2, depth * frameHeight + frameHeight / 2 - 2, MARKER_SIZE, frameHeight / 2);
}
for (int n = frame + 1; n < frame + num_frames; n++) {
@@ -580,7 +597,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
return;
}
Point p = e.getPoint();
p.x = p.x / FRAME_WIDTH;
p.x = p.x / getFrameWidth();
p.y = p.y / FRAME_HEIGHT;
/*if (p.x >= timeline.getFrameCount()) {
p.x = timeline.getFrameCount() - 1;
@@ -624,7 +641,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
DepthState ds = fr == null ? null : fr.layers.get(depth);
Set<Integer> emptyFrames = new LinkedHashSet<>();
if (timeline.timelined instanceof ButtonTag) {
emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames();
emptyFrames = ((ButtonTag) timeline.timelined).getEmptyFrames();
}
boolean thisEmpty = emptyFrames.contains(frame) || ds == null || ds.getCharacter() == null;
boolean previousEmpty = true;
@@ -724,7 +741,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
if (rec != null) {
rec.setFrame(nf, false);
}
for(int i = nf + 1; i < button.getFrameCount(); i++) {
for (int i = nf + 1; i < button.getFrameCount(); i++) {
rec = button.getButtonRecordAt(i, nd, false);
if (rec != null) {
rec.setFrame(i - 1, true);
@@ -817,7 +834,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
}
}
}
timelined.resetTimeline();
Point firstCursor = orderedCursor.iterator().next();
@@ -874,22 +891,22 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
continue;
}
ds.key = true;
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
BUTTONRECORD rec = button.getButtonRecordAt(nf, nd, false);
if (rec != null) {
BUTTONRECORD rec2 = new BUTTONRECORD(rec.getSwf(), rec.getTag());
rec2.fromPlaceObject(rec.toPlaceObject());
for(int i = nf; i < button.getFrameCount(); i++) {
for (int i = nf; i < button.getFrameCount(); i++) {
rec2.setFrame(i, rec.hasFrame(i));
rec.setFrame(i, false);
}
button.getRecords().add(rec2);
}
continue;
}
}
PlaceObjectTypeTag place;
try {
place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag();
@@ -977,14 +994,14 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
}
}
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
ButtonTag button = (ButtonTag) timelined;
BUTTONRECORD rec = button.getButtonRecordAt(nonEmptyFrame, fdepth, false);
BUTTONRECORD rec2 = new BUTTONRECORD(rec.getSwf(), rec.getTag());
rec2.fromPlaceObject(rec.toPlaceObject());
rec2.setFrame(fframe, true);
button.getRecords().add(rec2);
} else {
} else {
PlaceObjectTypeTag place;
try {
place = (PlaceObjectTypeTag) ds.placeObjectTag.cloneTag();
@@ -1042,13 +1059,12 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
int nf = p.x;
int nd = p.y;
if (nf >= timelined.getFrameCount()) {
if (timelined instanceof ButtonTag) {
if (timelined instanceof ButtonTag) {
continue;
}
}
ReadOnlyTagList tags = timelined.getTags();
for (int i = tags.size() - 1; i >= 0; i--) {
Tag t = tags.get(i);
@@ -1094,7 +1110,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
boolean somethingAfter = false;
Set<Integer> emptyFrames = new LinkedHashSet<>();
if (timelined instanceof ButtonTag) {
if (timelined instanceof ButtonTag) {
emptyFrames = ((ButtonTag) timelined).getEmptyFrames();
}
for (int f = nf + 1; f < timeline.getFrameCount(); f++) {
@@ -1112,8 +1128,8 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
if (!empty || somethingAfter) {
int moveFrameCount = somethingAfter ? 1 : nf - f;
for (int mf = 0; mf < moveFrameCount; mf++) {
if (timelined instanceof ButtonTag) {
if (timelined instanceof ButtonTag) {
ButtonTag button = (ButtonTag) timelined;
if (!somethingAfter) {
BUTTONRECORD rec = button.getButtonRecordAt(f, nd, true);
@@ -1129,7 +1145,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
}
button.packRecords();
continue;
}
}
int pos = timelined.indexOfTag(timeline.getFrame(f).showFrameTag);
ReadOnlyTagList tags = timelined.getTags();
@@ -1293,9 +1309,9 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
public Rectangle getFrameBounds(int frame, int depth) {
Rectangle rect = new Rectangle();
rect.width = FRAME_WIDTH;
rect.width = getFrameWidth();
rect.height = FRAME_HEIGHT;
rect.x = frame * FRAME_WIDTH;
rect.x = frame * getFrameWidth();
rect.y = depth * FRAME_HEIGHT;
return rect;
}
@@ -1303,7 +1319,7 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe
public void refresh() {
int frameCount = timeline == null ? 0 : timeline.getFrameCount();
int maxDepth = timeline == null ? 0 : timeline.getMaxDepth();
Dimension dim = new Dimension(FRAME_WIDTH * frameCount + 1, FRAME_HEIGHT * (maxDepth + 1 /*actions*/ + 1 /*one additional*/));
Dimension dim = new Dimension(getFrameWidth() * frameCount + 1, FRAME_HEIGHT * (maxDepth + 1 /*actions*/ + 1 /*one additional*/));
setSize(dim);
setPreferredSize(dim);
}
@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.easygui;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.timeline.Timeline;
import java.awt.Color;
import java.awt.Dimension;
@@ -46,6 +47,8 @@ public class TimelineTimePanel extends JPanel implements MouseListener {
private int selectedFrame = -1;
private int leftPos = 0;
private Timeline timeline;
private final List<FrameSelectionListener> listeners = new ArrayList<>();
@@ -62,6 +65,7 @@ public class TimelineTimePanel extends JPanel implements MouseListener {
setFont(getFont().deriveFont(TimelineDepthPanel.FONT_SIZE));
int maxDepthW = getFontMetrics(getFont()).stringWidth(maxDepthStr);
leftPos = maxDepthW + 2 * TimelineDepthPanel.PADDING;
this.timeline = timeline;
}
@@ -89,29 +93,53 @@ public class TimelineTimePanel extends JPanel implements MouseListener {
@Override
protected void paintComponent(Graphics g) {
Rectangle clip = g.getClipBounds();
int start_f = (scrollOffset + clip.x) / TimelineBodyPanel.FRAME_WIDTH;
int end_f = (scrollOffset + clip.x + clip.width) / TimelineBodyPanel.FRAME_WIDTH;
int frameWidth = TimelineBodyPanel.getFrameWidthForTimeline(timeline);
int start_f = (scrollOffset + clip.x) / frameWidth;
int end_f = (scrollOffset + clip.x + clip.width) / frameWidth;
g.setColor(TimelineBodyPanel.getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(borderColor);
int xofs = leftPos - TimelineBodyPanel.FRAME_WIDTH - scrollOffset % TimelineBodyPanel.FRAME_WIDTH;
int xofs = -scrollOffset % frameWidth;
for (int f = 0; f <= end_f; f++) {
g.drawLine(xofs + f * TimelineBodyPanel.FRAME_WIDTH + 1, TimelineBodyPanel.FRAME_HEIGHT - 1, xofs + f * TimelineBodyPanel.FRAME_WIDTH + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength);
g.drawLine(xofs + f * frameWidth + 1, TimelineBodyPanel.FRAME_HEIGHT - 1, xofs + f * frameWidth+ 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength);
}
g.setFont(g.getFont().deriveFont(fontSize));
for (int f = 0; f <= end_f; f++) {
int cur_f = start_f + f;
if (selectedFrame == cur_f) {
g.setColor(TimelineBodyPanel.SELECTED_COLOR);
g.fillRect(xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + 1, 0, TimelineBodyPanel.FRAME_WIDTH, TimelineBodyPanel.FRAME_HEIGHT - 1);
g.fillRect(xofs + f * frameWidth + 1, 0, frameWidth, TimelineBodyPanel.FRAME_HEIGHT - 1);
g.setColor(TimelineBodyPanel.SELECTED_BORDER_COLOR);
g.drawRect(xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + 1, 0, TimelineBodyPanel.FRAME_WIDTH, TimelineBodyPanel.FRAME_HEIGHT - 1);
g.drawRect(xofs + f * frameWidth + 1, 0, frameWidth, TimelineBodyPanel.FRAME_HEIGHT - 1);
}
g.setColor(fontColor);
if ((cur_f + 1) % 5 == 0 || cur_f == 0) {
String timeStr = Integer.toString(cur_f + 1);
int w = g.getFontMetrics().stringWidth(timeStr);
g.drawString(timeStr, xofs + (f - 1) * TimelineBodyPanel.FRAME_WIDTH + TimelineBodyPanel.FRAME_WIDTH / 2 - w / 2 + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace);
if (timeline != null && timeline.timelined instanceof ButtonTag) {
if (f < 5) {
String timeStr = "";
switch (f) {
case ButtonTag.FRAME_UP:
timeStr = "Up";
break;
case ButtonTag.FRAME_OVER:
timeStr = "Over";
break;
case ButtonTag.FRAME_DOWN:
timeStr = "Down";
break;
case ButtonTag.FRAME_HITTEST:
timeStr = "Hit";
break;
}
int w = g.getFontMetrics().stringWidth(timeStr);
g.drawString(timeStr, xofs + f * frameWidth + frameWidth / 2 - w / 2 + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace);
}
} else {
if ((cur_f + 1) % 5 == 0 || cur_f == 0) {
String timeStr = Integer.toString(cur_f + 1);
int w = g.getFontMetrics().stringWidth(timeStr);
g.drawString(timeStr, xofs + f * frameWidth + frameWidth / 2 - w / 2 + 1, TimelineBodyPanel.FRAME_HEIGHT - lineLength - lineTextSpace);
}
}
}
}
@@ -122,7 +150,7 @@ public class TimelineTimePanel extends JPanel implements MouseListener {
@Override
public void mousePressed(MouseEvent e) {
int frame = (scrollOffset + e.getX()) / TimelineBodyPanel.FRAME_WIDTH;
int frame = (scrollOffset + e.getX()) / TimelineBodyPanel.getFrameWidthForTimeline(timeline);
frameSelect(frame);
for (FrameSelectionListener l : listeners) {
l.frameSelected(frame, new ArrayList<>());
@@ -43,14 +43,14 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation
this.swfPanel = swfPanel;
this.timelined = timelined;
this.fframe = swfPanel.getFrame();
this.fdepths = swfPanel.getDepths();
this.fdepths = swfPanel.getDepths();
}
@Override
public void doOperation() {
swfPanel.setTimelined(timelined);
swfPanel.setFrame(fframe, fdepths);
saveTagList();
wasModified = timelined.isModified();
timelined.setModified(true);
@@ -59,7 +59,7 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation
protected void saveTagList() {
if (timelined instanceof ButtonTag) {
List<BUTTONRECORD> recordsCopy = new ArrayList<>();
for (BUTTONRECORD rec :((ButtonTag) timelined).getRecords()) {
for (BUTTONRECORD rec : ((ButtonTag) timelined).getRecords()) {
recordsCopy.add(new BUTTONRECORD(rec));
}
buttonRecords = recordsCopy;
@@ -87,7 +87,7 @@ public abstract class TimelinedTagListDoableOperation implements DoableOperation
timelined.addTag(tags.get(i));
}
timelined.resetTimeline();
timelined.setFrameCount(timelined.getTimeline().getFrameCount());
timelined.setFrameCount(timelined.getTimeline().getFrameCount());
}
}