More docking rework

This commit is contained in:
crschnick
2024-11-05 15:10:14 +00:00
parent 82d2d48e8a
commit ddfa70d68b
18 changed files with 336 additions and 59 deletions
@@ -20,7 +20,7 @@ public class ConnectionBrowseExchangeImpl extends ConnectionBrowseExchange {
throw new BeaconClientException("Not a file system connection");
}
BrowserSessionModel.DEFAULT.openFileSystemSync(
e.ref(), msg.getDirectory() != null ? ignored -> msg.getDirectory() : null, null);
e.ref(), msg.getDirectory() != null ? ignored -> msg.getDirectory() : null, null, true);
AppLayoutModel.get().selectBrowser();
return Response.builder().build();
}
@@ -4,6 +4,7 @@ import io.xpipe.app.browser.session.BrowserAbstractSessionModel;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.browser.session.BrowserSessionTab;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.storage.DataColor;
import io.xpipe.app.terminal.TerminalDockComp;
@@ -18,11 +19,14 @@ import java.util.UUID;
public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
private final BrowserSessionTab origin;
private final ObservableList<UUID> terminalRequests;
private final TerminalDockModel dockModel = new TerminalDockModel();
private TerminalView.Listener listener;
public BrowserTerminalDockTabModel(BrowserAbstractSessionModel<?> browserModel, ObservableList<UUID> terminalRequests) {
public BrowserTerminalDockTabModel(BrowserAbstractSessionModel<?> browserModel, BrowserSessionTab origin, ObservableList<UUID> terminalRequests) {
super(browserModel, AppI18n.get("terminal"), null);
this.origin = origin;
this.terminalRequests = terminalRequests;
}
@@ -38,23 +42,32 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
@Override
public void init() throws Exception {
var sessions = new ArrayList<TerminalView.Session>();
var terminals = new ArrayList<TerminalViewInstance>();
TerminalView.get().addListener(new TerminalView.Listener() {
listener = new TerminalView.Listener() {
@Override
public void onSessionOpened(TerminalView.Session session) {
if (!terminalRequests.contains(session.getRequest())) {
return;
}
var tv = terminals.stream().filter(instance -> instance.getTerminalProcess().equals(session.getTerminal())).findFirst();
tv.ifPresent(instance -> {
dockModel.trackTerminal(instance);
});
sessions.add(session);
var tv = terminals.stream().filter(instance -> sessions.stream().anyMatch(s -> instance.getTerminalProcess().equals(s.getTerminal()))).toList();
if (tv.isEmpty()) {
return;
}
for (int i = 0; i < tv.size() - 1; i++) {
dockModel.closeTerminal(tv.get(i));
}
var toTrack = tv.getLast();
dockModel.trackTerminal(toTrack);
}
@Override
public void onSessionClosed(TerminalView.Session session) {
sessions.remove(session);
}
@Override
@@ -64,13 +77,28 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
@Override
public void onTerminalClosed(TerminalViewInstance instance) {
terminals.remove(instance);
terminals.remove(instance);
if (terminals.isEmpty()) {
((BrowserSessionModel) browserModel).unsplitTab(BrowserTerminalDockTabModel.this);
}
}
};
TerminalView.get().addListener(listener);
this.browserModel.getSelectedEntry().addListener((observable, oldValue, newValue) -> {
dockModel.toggleView(newValue == origin);
});
AppLayoutModel.get().getSelected().addListener((observable, oldValue, newValue) -> {
dockModel.toggleView(AppLayoutModel.get().getEntries().indexOf(newValue) == 1);
});
}
@Override
public void close() {}
public void close() {
if (listener != null) {
TerminalView.get().removeListener(listener);
}
dockModel.onClose();
}
@Override
public String getIcon() {
@@ -81,9 +109,4 @@ terminals.remove(instance);
public DataColor getColor() {
return null;
}
@Override
public boolean isCloseable() {
return false;
}
}
@@ -6,7 +6,7 @@ import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.fs.OpenFileSystemComp;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.comp.base.DialogComp;
import io.xpipe.app.comp.base.SideSplitPaneComp;
import io.xpipe.app.comp.base.LeftSplitPaneComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppLayoutModel;
@@ -148,7 +148,7 @@ public class BrowserChooserComp extends DialogComp {
});
var vertical = new VerticalComp(List.of(bookmarkTopBar, bookmarksContainer)).styleClass("left");
var splitPane = new SideSplitPaneComp(vertical, stack)
var splitPane = new LeftSplitPaneComp(vertical, stack)
.withInitialWidth(AppLayoutModel.get().getSavedState().getBrowserConnectionsWidth())
.withOnDividerChange(AppLayoutModel.get().getSavedState()::setBrowserConnectionsWidth)
.styleClass("background")
@@ -4,13 +4,14 @@ import io.xpipe.app.browser.BrowserBookmarkComp;
import io.xpipe.app.browser.BrowserBookmarkHeaderComp;
import io.xpipe.app.browser.BrowserTransferComp;
import io.xpipe.app.comp.base.LoadingOverlayComp;
import io.xpipe.app.comp.base.SideSplitPaneComp;
import io.xpipe.app.comp.base.LeftSplitPaneComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.AnchorComp;
import io.xpipe.app.fxcomps.impl.LabelComp;
import io.xpipe.app.fxcomps.impl.StackComp;
import io.xpipe.app.fxcomps.impl.VerticalComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
@@ -104,8 +105,9 @@ public class BrowserSessionComp extends SimpleComp {
var vertical =
new VerticalComp(List.of(bookmarkTopBar, bookmarksContainer, localDownloadStage)).styleClass("left");
var split = new SimpleDoubleProperty();
var tabs = new BrowserSessionTabsComp(model, split);
var leftSplit = new SimpleDoubleProperty();
var rightSplit = new SimpleDoubleProperty();
var tabs = new BrowserSessionTabsComp(model, leftSplit, rightSplit);
tabs.apply(struc -> {
struc.get().setViewOrder(1);
struc.get().setPickOnBounds(false);
@@ -114,6 +116,7 @@ public class BrowserSessionComp extends SimpleComp {
AnchorPane.setLeftAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
});
vertical.apply(struc -> {
struc.get()
.paddingProperty()
@@ -126,14 +129,41 @@ public class BrowserSessionComp extends SimpleComp {
AnchorPane.setRightAnchor(struc.get(), 0.0);
})
.styleClass("tab-loading-indicator");
var loadingStack = new AnchorComp(List.of(tabs, loadingIndicator));
var splitPane = new SideSplitPaneComp(vertical, loadingStack)
var pinnedStack = new StackComp(List.of(new LabelComp("a")));
pinnedStack.apply(struc -> {
model.getEffectiveRightTab().subscribe( (newValue) -> {
PlatformThread.runLaterIfNeeded(() -> {
if (newValue != null) {
var r = newValue.comp().createRegion();
struc.get().getChildren().add(r);
} else {
struc.get().getChildren().clear();
}
});
});
rightSplit.addListener((observable, oldValue, newValue) -> {
struc.get().setMinWidth(newValue.doubleValue());
struc.get().setMaxWidth(newValue.doubleValue());
struc.get().setPrefWidth(newValue.doubleValue());
});
AnchorPane.setBottomAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
tabs.getHeaderHeight().subscribe(number -> {
AnchorPane.setTopAnchor(struc.get(), number.doubleValue());
});
});
var loadingStack = new AnchorComp(List.of(tabs, pinnedStack, loadingIndicator));
var splitPane = new LeftSplitPaneComp(vertical, loadingStack)
.withInitialWidth(AppLayoutModel.get().getSavedState().getBrowserConnectionsWidth())
.withOnDividerChange(d -> {
AppLayoutModel.get().getSavedState().setBrowserConnectionsWidth(d);
split.set(d);
})
.apply(struc -> {
leftSplit.set(d);
});
splitPane.apply(struc -> {
struc.getLeft().setMinWidth(200);
struc.getLeft().setMaxWidth(500);
struc.get().setPickOnBounds(false);
@@ -5,6 +5,7 @@ import io.xpipe.app.browser.BrowserSavedState;
import io.xpipe.app.browser.BrowserSavedStateImpl;
import io.xpipe.app.browser.BrowserTransferModel;
import io.xpipe.app.browser.fs.OpenFileSystemModel;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.BooleanScope;
@@ -12,14 +13,20 @@ import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileNames;
import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.util.FailableFunction;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableMap;
import lombok.Getter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Getter
public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSessionTab> {
@@ -32,6 +39,84 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel(this);
private final Property<Boolean> draggingFiles = new SimpleBooleanProperty();
private final Property<BrowserSessionTab> globalPinnedTab = new SimpleObjectProperty<>();
private final ObservableMap<BrowserSessionTab, BrowserSessionTab> splits = FXCollections.observableHashMap();
private final ObservableValue<BrowserSessionTab> effectiveRightTab = createEffectiveRightTab();
private ObservableValue<BrowserSessionTab> createEffectiveRightTab() {
return Bindings.createObjectBinding(() -> {
var current = selectedEntry.getValue();
if (!current.isCloseable()) {
return null;
}
var split = splits.get(current);
if (split != null) {
return split;
}
var global = globalPinnedTab.getValue();
if (global == null) {
return null;
}
if (global == selectedEntry.getValue()) {
return null;
}
return global;
}, globalPinnedTab, selectedEntry, splits);
}
public BrowserSessionModel() {
sessionEntries.addListener((ListChangeListener<? super BrowserSessionTab>) c -> {
var v = globalPinnedTab.getValue();
if (v != null && !c.getList().contains(v)) {
globalPinnedTab.setValue(null);
}
splits.keySet().removeIf(browserSessionTab -> !c.getList().contains(browserSessionTab));
});
}
public void splitTab(BrowserSessionTab tab, BrowserSessionTab split) {
if (splits.containsKey(tab)) {
return;
}
splits.put(tab, split);
ThreadHelper.runFailableAsync(() -> {
split.init();
});
}
public void unsplitTab(BrowserSessionTab tab) {
if (splits.values().remove(tab)) {
ThreadHelper.runFailableAsync(() -> {
tab.close();
});
}
}
public void pinTab(BrowserSessionTab tab) {
if (tab.equals(globalPinnedTab.getValue())) {
return;
}
globalPinnedTab.setValue(tab);
var nextIndex = getSessionEntries().indexOf(tab) + 1;
if (nextIndex < getSessionEntries().size()) {
getSelectedEntry().setValue(getSessionEntries().get(nextIndex));
}
}
public void unpinTab(BrowserSessionTab tab) {
ThreadHelper.runFailableAsync(() -> {
globalPinnedTab.setValue(null);
});
}
public void restoreState(BrowserSavedState state) {
ThreadHelper.runAsync(() -> {
@@ -53,7 +138,8 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
public void reset() {
synchronized (BrowserSessionModel.this) {
for (var o : new ArrayList<>(sessionEntries)) {
var all = new ArrayList<>(sessionEntries);
for (var o : all) {
// Don't close busy connections gracefully
// as we otherwise might lock up
if (!o.canImmediatelyClose()) {
@@ -79,14 +165,15 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
}
ThreadHelper.runFailableAsync(() -> {
openFileSystemSync(store, path, externalBusy);
openFileSystemSync(store, path, externalBusy, true);
});
}
public OpenFileSystemModel openFileSystemSync(
DataStoreEntryRef<? extends FileSystemStore> store,
FailableFunction<OpenFileSystemModel, String, Exception> path,
BooleanProperty externalBusy)
BooleanProperty externalBusy,
boolean select)
throws Exception {
OpenFileSystemModel model;
try (var b = new BooleanScope(externalBusy != null ? externalBusy : new SimpleBooleanProperty()).start()) {
@@ -96,8 +183,10 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
// Prevent multiple calls from interfering with each other
synchronized (BrowserSessionModel.this) {
sessionEntries.add(model);
// The tab pane doesn't automatically select new tabs
selectedEntry.setValue(model);
if (select) {
// The tab pane doesn't automatically select new tabs
selectedEntry.setValue(model);
}
}
}
}
@@ -108,4 +197,14 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
}
return model;
}
@Override
public void closeSync(BrowserSessionTab e) {
var split = splits.get(e);
if (split != null) {
split.close();
}
super.closeSync(e);
}
}
@@ -4,8 +4,12 @@ import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.storage.DataColor;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.beans.value.ObservableValue;
import lombok.Getter;
@Getter
@@ -15,6 +19,7 @@ public abstract class BrowserSessionTab {
protected final BrowserAbstractSessionModel<?> browserModel;
protected final String name;
protected final String tooltip;
protected final Property<BrowserSessionTab> splitTab = new SimpleObjectProperty<>();
public BrowserSessionTab(BrowserAbstractSessionModel<?> browserModel, String name, String tooltip) {
this.browserModel = browserModel;
@@ -18,6 +18,7 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
@@ -33,6 +34,7 @@ import atlantafx.base.theme.Styles;
import lombok.Getter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import static atlantafx.base.theme.Styles.DENSE;
import static atlantafx.base.theme.Styles.toggleStyleClass;
@@ -42,13 +44,15 @@ public class BrowserSessionTabsComp extends SimpleComp {
private final BrowserSessionModel model;
private final ObservableDoubleValue leftPadding;
private final DoubleProperty rightPadding;
@Getter
private final DoubleProperty headerHeight;
public BrowserSessionTabsComp(BrowserSessionModel model, ObservableDoubleValue leftPadding) {
public BrowserSessionTabsComp(BrowserSessionModel model, ObservableDoubleValue leftPadding, DoubleProperty rightPadding) {
this.model = model;
this.leftPadding = leftPadding;
this.rightPadding = rightPadding;
this.headerHeight = new SimpleDoubleProperty();
}
@@ -258,9 +262,28 @@ public class BrowserSessionTabsComp extends SimpleComp {
return tabs;
}
private ContextMenu createContextMenu(TabPane tabs, Tab tab) {
private ContextMenu createContextMenu(TabPane tabs, Tab tab, BrowserSessionTab tabModel) {
var cm = ContextMenuHelper.create();
if (tabModel.isCloseable()) {
var unsplit = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("unpinTab"));
unsplit.visibleProperty().bind(PlatformThread.sync(Bindings.createBooleanBinding(() -> {
return model.getGlobalPinnedTab().getValue() != null && model.getGlobalPinnedTab().getValue().equals(tabModel);
}, model.getGlobalPinnedTab())));
unsplit.setOnAction(event -> {
model.unpinTab(tabModel);
event.consume();
});
cm.getItems().add(unsplit);
var split = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("pinTab"));
split.setOnAction(event -> {
model.pinTab(tabModel);
event.consume();
});
cm.getItems().add(split);
}
var select = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("selectTab"));
select.acceleratorProperty()
.bind(Bindings.createObjectBinding(
@@ -340,7 +363,7 @@ public class BrowserSessionTabsComp extends SimpleComp {
private Tab createTab(TabPane tabs, BrowserSessionTab tabModel) {
var tab = new Tab();
tab.setContextMenu(createContextMenu(tabs, tab));
tab.setContextMenu(createContextMenu(tabs, tab, tabModel));
tab.setClosable(tabModel.isCloseable());
@@ -368,10 +391,62 @@ public class BrowserSessionTabsComp extends SimpleComp {
},
PlatformThread.sync(tabModel.getBusy())));
}
tab.setText(tabModel.getName());
if (tabModel.getBrowserModel() instanceof BrowserSessionModel sessionModel) {
var global = PlatformThread.sync(sessionModel.getGlobalPinnedTab());
tab.textProperty().bind(Bindings.createStringBinding(() -> {
return tabModel.getName() + (global.getValue() == tabModel ? " (" + AppI18n.get("pinned") + ")" : "");
}, global, AppPrefs.get().language()));
} else {
tab.setText(tabModel.getName());
}
Comp<?> comp = tabModel.comp();
tab.setContent(comp.createRegion());
var compRegion = comp.createRegion();
var empty = new StackPane();
empty.widthProperty().addListener((observable, oldValue, newValue) -> {
if (tabModel.isCloseable() && tabs.getSelectionModel().getSelectedItem() == tab) {
rightPadding.setValue(newValue.doubleValue());
}
});
var split = new SplitPane(compRegion);
if (tabModel.isCloseable()) {
split.getItems().add(empty);
}
model.getEffectiveRightTab().subscribe(browserSessionTab -> {
PlatformThread.runLaterIfNeeded(() -> {
if (browserSessionTab != null && split.getItems().size() > 1) {
split.getItems().set(1, empty);
} else if (browserSessionTab != null && split.getItems().size() == 1) {
split.getItems().add(empty);
} else if (browserSessionTab == null && split.getItems().size() > 1) {
split.getItems().remove(1);
}
});
});
tab.setContent(split);
// var lastSplitRegion = new AtomicReference<Region>();
// model.getGlobalPinnedTab().subscribe( (newValue) -> {
// PlatformThread.runLaterIfNeeded(() -> {
// if (newValue != null) {
// var r = newValue.comp().createRegion();
// split.getItems().add(r);
// lastSplitRegion.set(r);
// } else if (split.getItems().size() > 1) {
// split.getItems().removeLast();
// }
// });
// });
// model.getSelectedEntry().addListener((observable, oldValue, newValue) -> {
// PlatformThread.runLaterIfNeeded(() -> {
// if (newValue != null && newValue.equals(model.getGlobalPinnedTab().getValue()) && split.getItems().size() > 1) {
// split.getItems().remove(lastSplitRegion.get());
// } else if (split.getItems().size() > 1 && !split.getItems().contains(lastSplitRegion.get())) {
// split.getItems().add(lastSplitRegion.get());
// }
// });
// });
var id = UUID.randomUUID().toString();
tab.setId(id);
@@ -11,14 +11,14 @@ import lombok.Value;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
public class SideSplitPaneComp extends Comp<SideSplitPaneComp.Structure> {
public class LeftSplitPaneComp extends Comp<LeftSplitPaneComp.Structure> {
private final Comp<?> left;
private final Comp<?> center;
private Double initialWidth;
private Consumer<Double> onDividerChange;
public SideSplitPaneComp(Comp<?> left, Comp<?> center) {
public LeftSplitPaneComp(Comp<?> left, Comp<?> center) {
this.left = left;
this.center = center;
}
@@ -58,12 +58,12 @@ public class SideSplitPaneComp extends Comp<SideSplitPaneComp.Structure> {
return new Structure(sidebar, c, r, r.getDividers().getFirst());
}
public SideSplitPaneComp withInitialWidth(double val) {
public LeftSplitPaneComp withInitialWidth(double val) {
this.initialWidth = val;
return this;
}
public SideSplitPaneComp withOnDividerChange(Consumer<Double> onDividerChange) {
public LeftSplitPaneComp withOnDividerChange(Consumer<Double> onDividerChange) {
this.onDividerChange = onDividerChange;
return this;
}
@@ -1,6 +1,6 @@
package io.xpipe.app.comp.store;
import io.xpipe.app.comp.base.SideSplitPaneComp;
import io.xpipe.app.comp.base.LeftSplitPaneComp;
import io.xpipe.app.core.AppActionLinkDetector;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.fxcomps.SimpleComp;
@@ -15,7 +15,7 @@ public class StoreLayoutComp extends SimpleComp {
@Override
protected Region createSimple() {
var struc = new SideSplitPaneComp(new StoreSidebarComp(), new StoreEntryListComp())
var struc = new LeftSplitPaneComp(new StoreSidebarComp(), new StoreEntryListComp())
.withInitialWidth(AppLayoutModel.get().getSavedState().getSidebarWidth())
.withOnDividerChange(aDouble -> {
AppLayoutModel.get().getSavedState().setSidebarWidth(aDouble);
@@ -61,11 +61,11 @@ public class TerminalCategory extends AppPrefsCategory {
.apply(struc -> struc.get().setPromptText("myterminal -e $CMD"))
.hide(prefs.terminalType.isNotEqualTo(ExternalTerminalType.CUSTOM)))
.addComp(terminalTest)
.nameAndDescription("clearTerminalOnInit")
.addToggle(prefs.clearTerminalOnInit)
.nameAndDescription("enableTerminalDocking")
.pref(prefs.enableTerminalDocking)
.addToggle(prefs.enableTerminalDocking)
.hide(new SimpleBooleanProperty(!TerminalView.isSupported()))
.nameAndDescription("clearTerminalOnInit")
.addToggle(prefs.clearTerminalOnInit)
)
.buildComp();
}
@@ -58,18 +58,30 @@ public class TerminalDockComp extends SimpleComp {
});
s.addEventFilter(WindowEvent.WINDOW_HIDING,event -> {
model.onClose();
});
s.focusedProperty().addListener((observable, oldValue, newValue) -> {
});
stack.setOnMouseClicked(event -> {
model.clickView();
event.consume();
});
stack.getStyleClass().add("terminal-dock-comp");
return stack;
}
private void update(Region region) {
if (region.getScene() == null || region.getScene().getWindow() == null) {
return;
}
var bounds = region.localToScreen(region.getBoundsInLocal());
var p = region.getPadding();
var sx = region.getScene().getWindow().getOutputScaleX();
var sy = region.getScene().getWindow().getOutputScaleY();
model.resizeView((int) Math.ceil(bounds.getMinX() * sx), (int) Math.ceil(bounds.getMinY() * sy),(int) Math.floor(bounds.getWidth() * sx), (int) Math.floor(bounds.getHeight() * sy));
model.resizeView((int) Math.ceil(bounds.getMinX() * sx + p.getLeft()),
(int) Math.ceil(bounds.getMinY() * sy + p.getTop()),
(int) Math.floor(bounds.getWidth() * sx - p.getRight() - p.getLeft()),
(int) Math.floor(bounds.getHeight() * sy - p.getBottom() - p.getTop()));
}
}
@@ -4,9 +4,12 @@ import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.Rect;
import io.xpipe.core.process.OsType;
import lombok.Getter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TerminalDockModel {
@@ -16,10 +19,26 @@ public class TerminalDockModel {
private Rect viewBounds;
private boolean viewActive;
private final List<TerminalViewInstance> terminalInstances = new ArrayList<>();
@Getter
private final Set<TerminalViewInstance> terminalInstances = new HashSet<>();
public TerminalDockModel() {
int a = 0;
}
public synchronized void trackTerminal(TerminalViewInstance terminal) {
terminalInstances.add(terminal);
terminal.alwaysInFront();
terminal.updatePosition(viewBounds);
}
public synchronized void closeTerminal(TerminalViewInstance terminal) {
if (!terminalInstances.contains(terminal)) {
return;
}
terminal.close();
terminalInstances.remove(terminal);
}
public boolean isEnabled() {
@@ -130,6 +149,7 @@ public class TerminalDockModel {
terminalInstance.close();
});
terminalInstances.clear();
}
private void updatePositions() {
@@ -43,12 +43,16 @@ public class TerminalView {
private final List<TerminalViewInstance> terminalInstances = new ArrayList<>();
private final List<Listener> listeners = new ArrayList<>();
public void addListener(Listener listener) {
public synchronized void addListener(Listener listener) {
this.listeners.add(listener);
}
public synchronized void removeListener(Listener listener) {
this.listeners.remove(listener);
}
public boolean isEnabled() {
return isSupported() && AppPrefs.get().enableTerminalDocking().get();
return isSupported();
}
public synchronized void open(UUID request, long pid) {
@@ -125,7 +129,7 @@ public class TerminalView {
ThreadHelper.createPlatformThread("terminal-view", true, () -> {
while (true) {
instance.tick();
ThreadHelper.sleep(1000);
ThreadHelper.sleep(500);
}
}).start();
INSTANCE = instance;
@@ -49,7 +49,7 @@ public final class WindowsTerminalViewInstance extends TerminalViewInstance {
@Override
public void updatePosition(Rect bounds) {
control.move(bounds);
this.lastBounds = bounds;
this.lastBounds = queryBounds();
this.customBounds = false;
}
@@ -75,6 +75,10 @@
-fx-background-color: -color-bg-default;
}
.browser .terminal-dock-comp {
-fx-padding: 7 0 7 7;
}
.selected-file-list {
-fx-spacing: 5px;
-fx-padding: 8px;