mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-04-23 08:00:56 -04:00
Various fixes
This commit is contained in:
@@ -27,12 +27,13 @@ import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
|
||||
|
||||
private final BrowserSessionTab origin;
|
||||
private final ObservableList<UUID> terminalRequests;
|
||||
private final TerminalDockView dockModel = new TerminalDockView();
|
||||
private final TerminalDockView dockModel = new TerminalDockView(UnaryOperator.identity());
|
||||
private final BooleanProperty opened = new SimpleBooleanProperty();
|
||||
private TerminalView.Listener listener;
|
||||
private ObservableBooleanValue viewActive;
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.xpipe.app.core.window.AppMainWindow;
|
||||
import io.xpipe.app.platform.InputHelper;
|
||||
import io.xpipe.app.terminal.TerminalDockHubComp;
|
||||
import io.xpipe.app.terminal.TerminalDockHubManager;
|
||||
import io.xpipe.app.terminal.TerminalDockMode;
|
||||
import io.xpipe.app.util.ObservableSubscriber;
|
||||
|
||||
import javafx.scene.input.KeyCode;
|
||||
@@ -51,10 +52,15 @@ public class StoreLayoutComp extends SimpleComp {
|
||||
});
|
||||
});
|
||||
|
||||
var model = TerminalDockHubManager.get();
|
||||
var dock = new TerminalDockHubComp(model.getDockModel());
|
||||
var stack = new StackPane(comp.createRegion(), dock.createRegion());
|
||||
var stack = new StackPane(comp.createRegion());
|
||||
stack.getStyleClass().add("store-layout");
|
||||
|
||||
if (TerminalDockHubManager.isPossiblySupported()) {
|
||||
var model = TerminalDockHubManager.get();
|
||||
var dock = new TerminalDockHubComp(model.getDockModel());
|
||||
stack.getChildren().add(dock.createRegion());
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,6 @@ public class NativeWinWindowControl {
|
||||
User32.INSTANCE.SetWindowLong(windowHandle, User32.GWL_STYLE, mod);
|
||||
}
|
||||
|
||||
public void removeShadow() {}
|
||||
|
||||
public boolean isIconified() {
|
||||
return (User32.INSTANCE.GetWindowLong(windowHandle, User32.GWL_STYLE) & User32.WS_MINIMIZE) != 0;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ public interface AlacrittyTerminalType extends ExternalTerminalType, TrackableTe
|
||||
|
||||
class Windows implements ExternalApplicationType.PathApplication, ExternalTerminalType, AlacrittyTerminalType {
|
||||
|
||||
@Override
|
||||
public TerminalDockMode getDockMode() {
|
||||
return TerminalDockMode.WITH_BORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(TerminalLaunchConfiguration configuration) throws Exception {
|
||||
// Alacritty is bugged and will not accept arguments with spaces even if they are correctly passed/escaped
|
||||
|
||||
@@ -16,8 +16,6 @@ public abstract class ControllableTerminalSession extends TerminalView.TerminalS
|
||||
|
||||
public abstract void removeBorders();
|
||||
|
||||
public abstract void removeShadow();
|
||||
|
||||
public abstract void show();
|
||||
|
||||
public abstract void minimize();
|
||||
|
||||
@@ -8,6 +8,11 @@ import java.util.Base64;
|
||||
|
||||
public class PwshTerminalType implements ExternalApplicationType.PathApplication, TrackableTerminalType {
|
||||
|
||||
@Override
|
||||
public TerminalDockMode getDockMode() {
|
||||
return TerminalDockMode.WITH_BORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalOpenFormat getOpenFormat() {
|
||||
return TerminalOpenFormat.NEW_WINDOW;
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.xpipe.app.core.window.AppMainWindow;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
|
||||
import io.xpipe.app.util.GlobalTimer;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
@@ -25,6 +26,7 @@ import javafx.stage.WindowEvent;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class TerminalDockBrowserComp extends SimpleComp {
|
||||
@@ -105,11 +107,13 @@ public class TerminalDockBrowserComp extends SimpleComp {
|
||||
var focus = new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
if (newValue) {
|
||||
model.onFocusGain();
|
||||
} else {
|
||||
model.onFocusLost();
|
||||
}
|
||||
GlobalTimer.delay(() -> {
|
||||
if (newValue) {
|
||||
model.onFocusGain();
|
||||
} else {
|
||||
model.onFocusLost();
|
||||
}
|
||||
}, Duration.ofMillis(100));
|
||||
}
|
||||
};
|
||||
var show = new EventHandler<WindowEvent>() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.comp.SimpleComp;
|
||||
import io.xpipe.app.core.window.AppMainWindow;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.EventHandler;
|
||||
@@ -66,7 +67,9 @@ public class TerminalDockHubComp extends SimpleComp {
|
||||
if (newValue) {
|
||||
model.onFocusGain();
|
||||
} else {
|
||||
model.onFocusLost();
|
||||
Platform.runLater(() -> {
|
||||
model.onFocusLost();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -126,9 +129,9 @@ public class TerminalDockHubComp extends SimpleComp {
|
||||
var sx = region.getScene().getWindow().getOutputScaleX();
|
||||
var sy = region.getScene().getWindow().getOutputScaleY();
|
||||
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()));
|
||||
(int) Math.round(bounds.getMinX() * sx + p.getLeft()),
|
||||
(int) Math.round(bounds.getMinY() * sy + p.getTop()),
|
||||
(int) Math.round(bounds.getWidth() * sx - p.getRight() - p.getLeft()),
|
||||
(int) Math.round(bounds.getHeight() * sy - p.getBottom() - p.getTop()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.comp.base.ModalOverlay;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.core.window.AppDialog;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.GlobalTimer;
|
||||
import io.xpipe.app.util.Rect;
|
||||
import io.xpipe.core.OsType;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -19,6 +24,14 @@ import java.util.UUID;
|
||||
@Getter
|
||||
public class TerminalDockHubManager {
|
||||
|
||||
public static boolean isPossiblySupported() {
|
||||
if (OsType.ofLocal() != OsType.WINDOWS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isSupported() {
|
||||
if (OsType.ofLocal() != OsType.WINDOWS) {
|
||||
return false;
|
||||
@@ -45,15 +58,14 @@ public class TerminalDockHubManager {
|
||||
private static TerminalDockHubManager INSTANCE;
|
||||
|
||||
public static void init() {
|
||||
if (!isPossiblySupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
INSTANCE = new TerminalDockHubManager();
|
||||
|
||||
AppLayoutModel.get().getSelected().addListener((observable, oldValue, newValue) -> {
|
||||
if (AppLayoutModel.get().getEntries().indexOf(newValue) == 0) {
|
||||
INSTANCE.selectHub();
|
||||
} else {
|
||||
INSTANCE.unselectHub();
|
||||
}
|
||||
});
|
||||
INSTANCE.addLayoutListeners();
|
||||
INSTANCE.addDialogListeners();
|
||||
|
||||
TerminalView.get().addListener(INSTANCE.createListener());
|
||||
|
||||
@@ -68,15 +80,34 @@ public class TerminalDockHubManager {
|
||||
}
|
||||
|
||||
private final Set<UUID> hubRequests = new HashSet<>();
|
||||
private final BooleanProperty enabled = new SimpleBooleanProperty();
|
||||
private final BooleanProperty showing = new SimpleBooleanProperty();
|
||||
private final BooleanProperty detached = new SimpleBooleanProperty();
|
||||
private final BooleanProperty minimized = new SimpleBooleanProperty();
|
||||
private final TerminalDockView dockModel = new TerminalDockView();
|
||||
private final TerminalDockView dockModel = new TerminalDockView(rect -> {
|
||||
var term = AppPrefs.get().terminalType().getValue();
|
||||
var adjust = term instanceof TrackableTerminalType t && t.getDockMode() != TerminalDockMode.BORDERLESS;
|
||||
return adjust ? new Rect(rect.getX() - 9, rect.getY() - 1, rect.getW() + 16, rect.getH() + 9) : rect;
|
||||
});
|
||||
private final AppLayoutModel.QueueEntry queueEntry = new AppLayoutModel.QueueEntry(
|
||||
AppI18n.observable("toggleTerminalDock"),
|
||||
new LabelGraphic.IconGraphic("mdi2c-console"), () -> {
|
||||
refreshDockStatus();
|
||||
|
||||
if (!enabled.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!showing.get()) {
|
||||
// Run later to guarantee order of operations
|
||||
Platform.runLater(() -> {
|
||||
AppLayoutModel.get().selectConnections();
|
||||
showDock();
|
||||
attach();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minimized.get() || detached.get()) {
|
||||
attach();
|
||||
return false;
|
||||
@@ -87,14 +118,48 @@ public class TerminalDockHubManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!showing.get()) {
|
||||
showDock();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
private void addDialogListeners() {
|
||||
var wasShowing = new SimpleBooleanProperty();
|
||||
var wasAttached = new SimpleBooleanProperty();
|
||||
AppDialog.getModalOverlays().addListener((ListChangeListener<? super ModalOverlay>) c -> {
|
||||
if (c.getList().size() == 0) {
|
||||
if (wasShowing.get()) {
|
||||
INSTANCE.showDock();
|
||||
}
|
||||
if (wasAttached.get()) {
|
||||
INSTANCE.attach();
|
||||
}
|
||||
} else {
|
||||
wasAttached.set(!INSTANCE.minimized.get() && !INSTANCE.detached.get() && INSTANCE.showing.get());
|
||||
wasShowing.set(INSTANCE.showing.get());
|
||||
INSTANCE.hideDock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addLayoutListeners() {
|
||||
var wasShowing = new SimpleBooleanProperty();
|
||||
var wasAttached = new SimpleBooleanProperty();
|
||||
AppLayoutModel.get().getSelected().addListener((observable, oldValue, newValue) -> {
|
||||
if (AppLayoutModel.get().getEntries().indexOf(newValue) == 0) {
|
||||
if (wasShowing.get()) {
|
||||
INSTANCE.showDock();
|
||||
}
|
||||
if (wasAttached.get()) {
|
||||
INSTANCE.attach();
|
||||
}
|
||||
} else {
|
||||
wasAttached.set(!INSTANCE.minimized.get() && !INSTANCE.detached.get() && INSTANCE.showing.get());
|
||||
wasShowing.set(INSTANCE.showing.get());
|
||||
INSTANCE.hideDock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private TerminalView.Listener createListener() {
|
||||
var listener = new TerminalView.Listener() {
|
||||
@Override
|
||||
@@ -114,14 +179,13 @@ public class TerminalDockHubManager {
|
||||
return;
|
||||
}
|
||||
|
||||
controllable.get().removeShadow();
|
||||
if (t.getDockMode() == TerminalDockMode.BORDERLESS) {
|
||||
controllable.get().removeBorders();
|
||||
}
|
||||
}
|
||||
dockModel.trackTerminal(controllable.get(), !detached.get());
|
||||
dockModel.closeOtherTerminals(session.getRequest());
|
||||
openDock();
|
||||
enableDock();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,7 +205,7 @@ public class TerminalDockHubManager {
|
||||
&& s.getTerminal().isRunning())
|
||||
.toList();
|
||||
if (remaining.isEmpty()) {
|
||||
closeDock();
|
||||
disableDock();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -160,13 +224,6 @@ public class TerminalDockHubManager {
|
||||
detached.set(dockModel.isCustomBounds());
|
||||
}
|
||||
|
||||
public void selectHub() {
|
||||
dockModel.onFocusLost();
|
||||
}
|
||||
|
||||
public void unselectHub() {
|
||||
}
|
||||
|
||||
public void openTerminal(UUID request) {
|
||||
if (!isSupported()) {
|
||||
return;
|
||||
@@ -175,19 +232,33 @@ public class TerminalDockHubManager {
|
||||
hubRequests.add(request);
|
||||
}
|
||||
|
||||
public void openDock() {
|
||||
public void enableDock() {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (showing.get()) {
|
||||
if (enabled.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(true);
|
||||
enabled.set(true);
|
||||
showing.set(true);
|
||||
|
||||
AppLayoutModel.get().getQueueEntries().add(queueEntry);
|
||||
});
|
||||
}
|
||||
|
||||
public void disableDock() {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (!enabled.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(false);
|
||||
enabled.set(false);
|
||||
showing.set(false);
|
||||
AppLayoutModel.get().getQueueEntries().remove(queueEntry);
|
||||
});
|
||||
}
|
||||
|
||||
public void showDock() {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (showing.get()) {
|
||||
@@ -199,11 +270,6 @@ public class TerminalDockHubManager {
|
||||
});
|
||||
}
|
||||
|
||||
public void attach() {
|
||||
dockModel.attach();
|
||||
detached.set(false);
|
||||
}
|
||||
|
||||
public void hideDock() {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (!showing.get()) {
|
||||
@@ -215,15 +281,8 @@ public class TerminalDockHubManager {
|
||||
});
|
||||
}
|
||||
|
||||
public void closeDock() {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (!showing.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(false);
|
||||
showing.set(false);
|
||||
AppLayoutModel.get().getQueueEntries().remove(queueEntry);
|
||||
});
|
||||
public void attach() {
|
||||
dockModel.attach();
|
||||
detached.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.platform.NativeWinWindowControl;
|
||||
import io.xpipe.app.util.Rect;
|
||||
|
||||
import lombok.Getter;
|
||||
@@ -9,15 +10,20 @@ import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class TerminalDockView {
|
||||
|
||||
@Getter
|
||||
private final Set<ControllableTerminalSession> terminalInstances = new HashSet<>();
|
||||
private final UnaryOperator<Rect> windowBoundsFunction;
|
||||
|
||||
|
||||
private Rect viewBounds;
|
||||
private boolean viewActive;
|
||||
|
||||
public TerminalDockView(UnaryOperator<Rect> windowBoundsFunction) {this.windowBoundsFunction = windowBoundsFunction;}
|
||||
|
||||
public synchronized boolean isRunning() {
|
||||
return terminalInstances.stream().anyMatch(terminal -> terminal.isRunning());
|
||||
}
|
||||
@@ -31,7 +37,10 @@ public class TerminalDockView {
|
||||
}
|
||||
|
||||
public synchronized void trackTerminal(ControllableTerminalSession terminal, boolean dock) {
|
||||
terminalInstances.add(terminal);
|
||||
if (!terminalInstances.add(terminal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The main window always loses focus when the terminal is opened,
|
||||
// so only put it in front
|
||||
// If we refocus the main window, it will get put always in front then
|
||||
@@ -71,7 +80,10 @@ public class TerminalDockView {
|
||||
|
||||
this.viewActive = active;
|
||||
if (active) {
|
||||
terminalInstances.forEach(terminalInstance -> terminalInstance.alwaysInFront());
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
terminalInstance.frontOfMainWindow();
|
||||
terminalInstance.focus();
|
||||
});
|
||||
updatePositions();
|
||||
} else {
|
||||
terminalInstances.forEach(terminalInstance -> terminalInstance.back());
|
||||
@@ -183,7 +195,7 @@ public class TerminalDockView {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewBounds = new Rect(x, y, w, h);
|
||||
this.viewBounds = windowBoundsFunction.apply(new Rect(x, y, w, h));
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
@@ -192,7 +204,8 @@ public class TerminalDockView {
|
||||
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
terminalInstance.show();
|
||||
terminalInstance.alwaysInFront();
|
||||
terminalInstance.frontOfMainWindow();
|
||||
terminalInstance.focus();
|
||||
terminalInstance.updatePosition(viewBounds);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -169,6 +169,11 @@ public class TerminalLauncher {
|
||||
preferTabs && AppPrefs.get().preferTerminalTabs().get();
|
||||
var launchConfig = new TerminalLaunchConfiguration(color, adjustedTitle, cleanTitle, preferTabs, paneList);
|
||||
|
||||
// Dock terminal if needed
|
||||
for (TerminalPaneConfiguration pane : launchConfig.getPanes()) {
|
||||
TerminalDockHubManager.get().openTerminal(pane.getRequest());
|
||||
}
|
||||
|
||||
if (effectivePreferTabs) {
|
||||
synchronized (TerminalLauncher.class) {
|
||||
// There will be timing issues when launching multiple tabs in a short time span
|
||||
@@ -206,10 +211,6 @@ public class TerminalLauncher {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TerminalPaneConfiguration pane : config.getPanes()) {
|
||||
TerminalDockHubManager.get().openTerminal(pane.getRequest());
|
||||
}
|
||||
|
||||
try {
|
||||
type.launch(config);
|
||||
latch.await();
|
||||
|
||||
@@ -46,11 +46,6 @@ public final class WindowsTerminalSession extends ControllableTerminalSession {
|
||||
control.removeBorders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShadow() {
|
||||
control.removeShadow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
this.control.show();
|
||||
|
||||
Reference in New Issue
Block a user