mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-05-04 03:40:32 +00:00
Rework terminal dock
This commit is contained in:
@@ -129,14 +129,22 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
|
||||
AppLayoutModel.get().getSelected());
|
||||
viewActive.subscribe(aBoolean -> {
|
||||
Platform.runLater(() -> {
|
||||
dockModel.toggleView(aBoolean);
|
||||
if (aBoolean) {
|
||||
dockModel.activateView();
|
||||
} else {
|
||||
dockModel.deactivateView();
|
||||
}
|
||||
});
|
||||
});
|
||||
AppDialog.getModalOverlays().addListener((ListChangeListener<? super ModalOverlay>) c -> {
|
||||
if (c.getList().size() > 0) {
|
||||
dockModel.toggleView(false);
|
||||
dockModel.deactivateView();
|
||||
} else {
|
||||
dockModel.toggleView(viewActive.get());
|
||||
if (viewActive.get()) {
|
||||
dockModel.activateView();
|
||||
} else {
|
||||
dockModel.deactivateView();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@ import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.platform.win32.*;
|
||||
import com.sun.jna.win32.StdCallLibrary;
|
||||
import com.sun.jna.win32.W32APIOptions;
|
||||
import io.xpipe.app.util.User32Ex;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -74,10 +73,4 @@ public class AppWindowsLock {
|
||||
}
|
||||
}
|
||||
|
||||
public interface User32Ex extends W32APIOptions {
|
||||
|
||||
User32Ex INSTANCE = Native.load("user32", User32Ex.class, DEFAULT_OPTIONS);
|
||||
|
||||
int SetWindowLongPtr(WinDef.HWND hWnd, int nIndex, WinMsgProc callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.xpipe.app.platform;
|
||||
|
||||
import io.xpipe.app.util.Rect;
|
||||
|
||||
import io.xpipe.app.util.User32Ex;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
@@ -22,7 +23,6 @@ import java.util.List;
|
||||
@EqualsAndHashCode
|
||||
public class NativeWinWindowControl {
|
||||
|
||||
private static final int WS_EX_NOACTIVATE = 0x08000000;
|
||||
private static final int WS_EX_APPWINDOW = 0x00040000;
|
||||
|
||||
public static NativeWinWindowControl MAIN_WINDOW;
|
||||
@@ -78,16 +78,20 @@ public class NativeWinWindowControl {
|
||||
User32.INSTANCE.SetWindowLong(windowHandle, User32.GWL_STYLE, mod);
|
||||
}
|
||||
|
||||
public void disableActivate() {
|
||||
public void takeOwnership(WinDef.HWND owner) {
|
||||
var style = User32.INSTANCE.GetWindowLong(windowHandle, User32.GWL_EXSTYLE);
|
||||
var mod = style | WS_EX_NOACTIVATE | WS_EX_APPWINDOW;
|
||||
var mod = style | WS_EX_APPWINDOW;
|
||||
User32.INSTANCE.SetWindowLong(windowHandle, User32.GWL_EXSTYLE, mod);
|
||||
|
||||
User32Ex.INSTANCE.SetWindowLongPtr(getWindowHandle(), User32.GWL_HWNDPARENT, owner);
|
||||
}
|
||||
|
||||
public void enableActivate() {
|
||||
public void releaseOwnership() {
|
||||
var style = User32.INSTANCE.GetWindowLong(windowHandle, User32.GWL_EXSTYLE);
|
||||
var mod = style & ~(WS_EX_NOACTIVATE | WS_EX_APPWINDOW);
|
||||
var mod = style & ~(WS_EX_APPWINDOW);
|
||||
User32.INSTANCE.SetWindowLong(windowHandle, User32.GWL_EXSTYLE, mod);
|
||||
|
||||
User32Ex.INSTANCE.SetWindowLongPtr(getWindowHandle(), User32.GWL_HWNDPARENT, (WinDef.HWND) null);
|
||||
}
|
||||
|
||||
public boolean isIconified() {
|
||||
@@ -98,12 +102,7 @@ public class NativeWinWindowControl {
|
||||
return User32.INSTANCE.IsWindowVisible(windowHandle);
|
||||
}
|
||||
|
||||
public void alwaysInFront() {
|
||||
orderRelative(new WinDef.HWND(new Pointer(0xFFFFFFFFFFFFFFFFL)));
|
||||
}
|
||||
|
||||
public void defaultOrder() {
|
||||
orderRelative(new WinDef.HWND(new Pointer(-2)));
|
||||
public void moveToFront() {
|
||||
orderRelative(new WinDef.HWND(new Pointer(0)));
|
||||
}
|
||||
|
||||
@@ -126,7 +125,7 @@ public class NativeWinWindowControl {
|
||||
|
||||
public void move(Rect bounds) {
|
||||
User32.INSTANCE.SetWindowPos(
|
||||
windowHandle, null, bounds.getX(), bounds.getY(), bounds.getW(), bounds.getH(), User32.SWP_NOACTIVATE);
|
||||
windowHandle, null, bounds.getX(), bounds.getY(), bounds.getW(), bounds.getH(), User32.SWP_NOACTIVATE | User32.SWP_NOZORDER);
|
||||
}
|
||||
|
||||
public Rect getBounds() {
|
||||
|
||||
@@ -14,19 +14,19 @@ public abstract class ControllableTerminalSession extends TerminalView.TerminalS
|
||||
super(terminalProcess);
|
||||
}
|
||||
|
||||
public abstract void own();
|
||||
|
||||
public abstract void disown();
|
||||
|
||||
public abstract void removeBorders();
|
||||
|
||||
public abstract void show();
|
||||
|
||||
public abstract void minimize();
|
||||
|
||||
public abstract void alwaysInFront();
|
||||
|
||||
public abstract void back();
|
||||
|
||||
public abstract void frontOfMainWindow();
|
||||
|
||||
public abstract void moveToFront();
|
||||
public abstract void backOfMainWindow();
|
||||
|
||||
public abstract void focus();
|
||||
|
||||
|
||||
@@ -100,24 +100,10 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
if (newValue) {
|
||||
model.onWindowMinimize();
|
||||
} else {
|
||||
model.onWindowActivate();
|
||||
model.onWindowShow();
|
||||
}
|
||||
}
|
||||
};
|
||||
var focus = new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
GlobalTimer.delay(
|
||||
() -> {
|
||||
if (newValue) {
|
||||
model.onFocusGain();
|
||||
} else {
|
||||
model.onFocusLost();
|
||||
}
|
||||
},
|
||||
Duration.ofMillis(100));
|
||||
}
|
||||
};
|
||||
var show = new EventHandler<WindowEvent>() {
|
||||
@Override
|
||||
public void handle(WindowEvent event) {
|
||||
@@ -139,7 +125,6 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
s.widthProperty().removeListener(update);
|
||||
s.heightProperty().removeListener(update);
|
||||
s.iconifiedProperty().removeListener(iconified);
|
||||
s.focusedProperty().removeListener(focus);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
if (parent.get() != null) {
|
||||
@@ -152,7 +137,6 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
s.widthProperty().addListener(update);
|
||||
s.heightProperty().addListener(update);
|
||||
s.iconifiedProperty().addListener(iconified);
|
||||
s.focusedProperty().addListener(focus);
|
||||
s.addEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.addEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
// As in practice this node is wrapped in another stack pane
|
||||
|
||||
@@ -9,13 +9,10 @@ import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Bounds;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.WindowEvent;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
@@ -62,26 +59,7 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
model.onWindowMinimize();
|
||||
} else {
|
||||
Platform.runLater(() -> {
|
||||
model.onWindowActivate();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
var focus = new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
if (newValue) {
|
||||
var selected = s.getScene().getRoot().lookup(".icon-button-comp:hover");
|
||||
if (selected instanceof Button b
|
||||
&& b.getGraphic() instanceof FontIcon fi
|
||||
&& !fi.getIconLiteral().equals("mdi2c-connection")) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.onFocusGain();
|
||||
} else {
|
||||
Platform.runLater(() -> {
|
||||
model.onFocusLost();
|
||||
model.onWindowShow();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -107,7 +85,6 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
s.widthProperty().removeListener(update);
|
||||
s.heightProperty().removeListener(update);
|
||||
s.iconifiedProperty().removeListener(iconified);
|
||||
s.focusedProperty().removeListener(focus);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
if (parent.get() != null) {
|
||||
@@ -120,7 +97,6 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
s.widthProperty().addListener(update);
|
||||
s.heightProperty().addListener(update);
|
||||
s.iconifiedProperty().addListener(iconified);
|
||||
s.focusedProperty().addListener(focus);
|
||||
s.addEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.addEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
// As in practice this node is wrapped in another stack pane
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.core.window.AppMainWindow;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.NativeWinWindowControl;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
@@ -18,6 +19,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
@@ -72,7 +74,7 @@ public class TerminalDockHubManager {
|
||||
|
||||
TerminalView.get().addListener(INSTANCE.createListener());
|
||||
|
||||
GlobalTimer.scheduleUntil(Duration.ofSeconds(1), false, () -> {
|
||||
GlobalTimer.scheduleUntil(Duration.ofMillis(500), false, () -> {
|
||||
INSTANCE.refreshDockStatus();
|
||||
return false;
|
||||
});
|
||||
@@ -97,7 +99,12 @@ public class TerminalDockHubManager {
|
||||
: new Rect(rect.getX(), rect.getY() - topAdjust, rect.getW(), rect.getH() + topAdjust);
|
||||
});
|
||||
private final AppLayoutModel.QueueEntry queueEntry = new AppLayoutModel.QueueEntry(
|
||||
AppI18n.observable("toggleTerminalDock"), new LabelGraphic.IconGraphic("mdi2c-console"), () -> {
|
||||
AppI18n.observable("toggleTerminalDock"), new LabelGraphic.NodeGraphic(() -> {
|
||||
var fi = new FontIcon("mdi2c-console");
|
||||
fi.getStyleClass().add("graphic");
|
||||
fi.getStyleClass().add("terminal-dock-button");
|
||||
return fi;
|
||||
}), () -> {
|
||||
refreshDockStatus();
|
||||
|
||||
if (!enabled.get()) {
|
||||
@@ -177,7 +184,9 @@ public class TerminalDockHubManager {
|
||||
controllable.get().removeBorders();
|
||||
}
|
||||
}
|
||||
dockModel.trackTerminal(controllable.get(), !detached.get());
|
||||
|
||||
var dock = !detached.get();
|
||||
dockModel.trackTerminal(controllable.get(), dock);
|
||||
dockModel.closeOtherTerminals(session.getRequest());
|
||||
enableDock();
|
||||
}
|
||||
@@ -218,7 +227,7 @@ public class TerminalDockHubManager {
|
||||
}
|
||||
|
||||
minimized.set(dockModel.isMinimized());
|
||||
detached.set(dockModel.isCustomBounds() || dockModel.isMinimized());
|
||||
detached.set(!dockModel.isMinimized() && (dockModel.isCustomBounds() || AppMainWindow.get().getStage().isIconified()));
|
||||
}
|
||||
|
||||
public void openTerminal(UUID request) {
|
||||
@@ -256,7 +265,7 @@ public class TerminalDockHubManager {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(true);
|
||||
dockModel.activateView();
|
||||
enabled.set(true);
|
||||
showing.set(true);
|
||||
|
||||
@@ -271,7 +280,7 @@ public class TerminalDockHubManager {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(false);
|
||||
dockModel.deactivateView();
|
||||
enabled.set(false);
|
||||
showing.set(false);
|
||||
|
||||
@@ -286,7 +295,7 @@ public class TerminalDockHubManager {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(true);
|
||||
dockModel.activateView();
|
||||
showing.set(true);
|
||||
AppLayoutModel.get().selectConnections();
|
||||
});
|
||||
@@ -298,7 +307,7 @@ public class TerminalDockHubManager {
|
||||
return;
|
||||
}
|
||||
|
||||
dockModel.toggleView(false);
|
||||
dockModel.deactivateView();
|
||||
showing.set(false);
|
||||
});
|
||||
}
|
||||
@@ -306,5 +315,6 @@ public class TerminalDockHubManager {
|
||||
public void attach() {
|
||||
dockModel.attach();
|
||||
detached.set(false);
|
||||
minimized.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,34 +46,37 @@ public class TerminalDockView {
|
||||
}
|
||||
|
||||
public synchronized void updateCustomBounds() {
|
||||
terminalInstances.forEach(terminal -> terminal.updateBoundsState());
|
||||
terminalInstances.forEach(terminal -> {
|
||||
terminal.updateBoundsState();
|
||||
if (terminal.isCustomBounds()) {
|
||||
terminal.disown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized void trackTerminal(ControllableTerminalSession terminal, boolean dock) {
|
||||
if (!terminalInstances.add(terminal)) {
|
||||
return;
|
||||
if (viewActive && dock && viewBounds != null) {
|
||||
// The window might be minimized
|
||||
// We always want to show the terminal though
|
||||
terminal.show();
|
||||
|
||||
terminal.own();
|
||||
|
||||
terminal.updatePosition(windowBoundsFunction.apply(viewBounds));
|
||||
updateCustomBounds();
|
||||
}
|
||||
|
||||
// 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
|
||||
terminal.frontOfMainWindow();
|
||||
if (dock && viewBounds != null) {
|
||||
terminal.updatePosition(windowBoundsFunction.apply(viewBounds));
|
||||
|
||||
var wasAdded = terminalInstances.add(terminal);
|
||||
if (wasAdded && viewActive && dock && viewBounds != null) {
|
||||
// Ugly fix for Windows Terminal instances using size constraints on first resize
|
||||
// This will cause the dock to interpret is as detached if we don't fix it again
|
||||
if (AppPrefs.get().terminalType().getValue() instanceof WindowsTerminalType) {
|
||||
GlobalTimer.delay(
|
||||
() -> {
|
||||
terminal.updatePosition(windowBoundsFunction.apply(viewBounds));
|
||||
updateCustomBounds();
|
||||
},
|
||||
Duration.ofMillis(100));
|
||||
GlobalTimer.delay(
|
||||
() -> {
|
||||
terminal.updatePosition(windowBoundsFunction.apply(viewBounds));
|
||||
},
|
||||
Duration.ofMillis(1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,30 +103,13 @@ public class TerminalDockView {
|
||||
terminalInstances.remove(terminal);
|
||||
}
|
||||
|
||||
public synchronized void toggleView(boolean active) {
|
||||
TrackEvent.withTrace("Terminal view toggled").tag("active", active).handle();
|
||||
if (viewActive == active) {
|
||||
public synchronized void activateView() {
|
||||
TrackEvent.withTrace("Terminal view activated").handle();
|
||||
if (viewActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewActive = active;
|
||||
if (active) {
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
terminalInstance.frontOfMainWindow();
|
||||
terminalInstance.focus();
|
||||
});
|
||||
updatePositions();
|
||||
} else {
|
||||
terminalInstances.forEach(terminalInstance -> terminalInstance.back());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void onFocusGain() {
|
||||
if (!viewActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackEvent.withTrace("Terminal view focus gained").handle();
|
||||
this.viewActive = true;
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
if (!terminalInstance.isActive()) {
|
||||
return;
|
||||
@@ -134,34 +120,33 @@ public class TerminalDockView {
|
||||
return;
|
||||
}
|
||||
|
||||
terminalInstance.show();
|
||||
terminalInstance.alwaysInFront();
|
||||
terminalInstance.own();
|
||||
terminalInstance.focus();
|
||||
});
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
public synchronized void onFocusLost() {
|
||||
public synchronized void deactivateView() {
|
||||
TrackEvent.withTrace("Terminal view deactivated").handle();
|
||||
if (!viewActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackEvent.withTrace("Terminal view focus lost").handle();
|
||||
this.viewActive = false;
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
if (!terminalInstance.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminalInstance.updateBoundsState();
|
||||
if (terminalInstance.isCustomBounds()) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminalInstance.frontOfMainWindow();
|
||||
terminalInstance.disown();
|
||||
terminalInstance.backOfMainWindow();
|
||||
});
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
public synchronized void onWindowActivate() {
|
||||
TrackEvent.withTrace("Terminal view window activated").handle();
|
||||
public synchronized void onWindowShow() {
|
||||
TrackEvent.withTrace("Terminal view window shown").handle();
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
if (terminalInstance.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminalInstance.updateBoundsState();
|
||||
if (terminalInstance.isCustomBounds()) {
|
||||
return;
|
||||
@@ -169,10 +154,11 @@ public class TerminalDockView {
|
||||
|
||||
terminalInstance.show();
|
||||
if (viewActive) {
|
||||
terminalInstance.frontOfMainWindow();
|
||||
terminalInstance.own();
|
||||
terminalInstance.focus();
|
||||
} else {
|
||||
terminalInstance.back();
|
||||
terminalInstance.disown();
|
||||
terminalInstance.backOfMainWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -233,9 +219,9 @@ public class TerminalDockView {
|
||||
|
||||
terminalInstances.forEach(terminalInstance -> {
|
||||
terminalInstance.show();
|
||||
terminalInstance.frontOfMainWindow();
|
||||
terminalInstance.focus();
|
||||
terminalInstance.updatePosition(windowBoundsFunction.apply(viewBounds));
|
||||
terminalInstance.own();
|
||||
terminalInstance.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import com.sun.jna.platform.win32.User32;
|
||||
import io.xpipe.app.platform.NativeWinWindowControl;
|
||||
import io.xpipe.app.util.Rect;
|
||||
|
||||
import io.xpipe.app.util.User32Ex;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
@@ -41,6 +43,16 @@ public final class WindowsTerminalSession extends ControllableTerminalSession {
|
||||
return super.isRunning() && control.isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void own() {
|
||||
control.takeOwnership(NativeWinWindowControl.MAIN_WINDOW.getWindowHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disown() {
|
||||
control.releaseOwnership();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBorders() {
|
||||
control.removeBorders();
|
||||
@@ -57,27 +69,13 @@ public final class WindowsTerminalSession extends ControllableTerminalSession {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alwaysInFront() {
|
||||
this.control.alwaysInFront();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void back() {
|
||||
control.defaultOrder();
|
||||
NativeWinWindowControl.MAIN_WINDOW.alwaysInFront();
|
||||
NativeWinWindowControl.MAIN_WINDOW.defaultOrder();
|
||||
public void backOfMainWindow() {
|
||||
getControl().orderRelative(NativeWinWindowControl.MAIN_WINDOW.getWindowHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void frontOfMainWindow() {
|
||||
this.control.alwaysInFront();
|
||||
this.control.defaultOrder();
|
||||
NativeWinWindowControl.MAIN_WINDOW.orderRelative(control.getWindowHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveToFront() {
|
||||
this.control.defaultOrder();
|
||||
this.control.moveToFront();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,6 +115,10 @@ public final class WindowsTerminalSession extends ControllableTerminalSession {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastBounds != null && (lastBounds.getX() == -32000 || lastBounds.getY() == -32000)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastBounds != null && !lastBounds.equals(bounds)) {
|
||||
customBounds = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.xpipe.app.util;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.platform.win32.WinDef;
|
||||
import com.sun.jna.win32.W32APIOptions;
|
||||
import io.xpipe.app.core.AppWindowsLock;
|
||||
|
||||
public interface User32Ex extends W32APIOptions {
|
||||
|
||||
User32Ex INSTANCE = Native.load("user32", User32Ex.class, DEFAULT_OPTIONS);
|
||||
|
||||
int SetWindowLongPtr(WinDef.HWND hWnd, int nIndex, AppWindowsLock.WinMsgProc callback);
|
||||
|
||||
int SetWindowLongPtr(WinDef.HWND hWnd, int nIndex, WinDef.HWND w);
|
||||
}
|
||||
Vendored
+4
-1
@@ -10,7 +10,9 @@ Here is a Windows Terminal instance with 4 split tabs that were launched through
|
||||
|
||||

|
||||
|
||||
A docked terminal is embedded into the XPipe window but can also be detached from it. If you want to disable the terminal docking completely, you can do so in the settings menu.
|
||||
A docked terminal is embedded into the XPipe window but can also be detached from it. If you want to disable the terminal docking completely, you can do so in the settings menu:
|
||||
|
||||

|
||||
|
||||
## Cisco switch integration
|
||||
|
||||
@@ -92,6 +94,7 @@ The scripting system has been completely reworked with the goal of becoming simp
|
||||
- Fix predefined categories being able to be moved and causing breakages
|
||||
- Fix terminal session titles not applying for Konsole
|
||||
- Fix rbash shell detection not working
|
||||
- Fix SFTP failing with files with single quotes in their name
|
||||
- Fix WinSCP open action requiring an existing ppk key and only working with external key files, not in-place keys
|
||||
- Fix batch mode selection not working for incomplete connections, like newly added VMs without credentials
|
||||
- Fix batch action confirmation setting requiring a double confirmation for each individual connection
|
||||
|
||||
Reference in New Issue
Block a user