diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionComp.java index da9ccfd25..9c9d7dd3d 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFullSessionComp.java @@ -8,7 +8,7 @@ import io.xpipe.app.comp.SimpleRegionBuilder; import io.xpipe.app.comp.base.*; import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppLayoutModel; -import io.xpipe.app.core.window.AppMainWindow; +import io.xpipe.app.core.AppSizeBreakpoints; import io.xpipe.app.ext.ShellStore; import io.xpipe.app.hub.comp.StoreEntryWrapper; import io.xpipe.app.hub.comp.StoreFilter; @@ -80,7 +80,7 @@ public class BrowserFullSessionComp extends SimpleRegionBuilder { loadingStack.apply(struc -> struc.setPickOnBounds(false)); var delayedStack = new DelayedInitComp( left, () -> StoreViewState.get() != null && StoreViewState.get().isInitialized()); - delayedStack.hide(AppMainWindow.get().getStage().widthProperty().lessThan(1000)); + delayedStack.hide(AppSizeBreakpoints.compactMode()); var splitPane = new LeftSplitPaneComp(delayedStack, loadingStack) .withInitialWidth(AppLayoutModel.get().getSavedState().getBrowserConnectionsWidth()) .withOnDividerChange(d -> { diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java index 00fd2fba7..03edc736b 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java @@ -6,9 +6,9 @@ import io.xpipe.app.comp.SimpleRegionBuilder; import io.xpipe.app.comp.base.LoadingIconComp; import io.xpipe.app.comp.base.PrettyImageHelper; import io.xpipe.app.comp.base.StackComp; -import io.xpipe.app.core.App; import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppSizeBreakpoints; import io.xpipe.app.platform.LabelGraphic; import io.xpipe.app.platform.MenuHelper; import io.xpipe.app.platform.PlatformThread; @@ -269,26 +269,24 @@ public class BrowserSessionTabsComp extends SimpleRegionBuilder { .paddingProperty() .bind(Bindings.createObjectBinding( () -> { - var w = App.getApp().getStage().getWidth(); - if (w >= 1000) { + if (!AppSizeBreakpoints.compactMode().get()) { return new Insets(2, 0, 4, -leftPadding.get() + 3); } else { return new Insets(2, 0, 4, -leftPadding.get() - 4); } }, - App.getApp().getStage().widthProperty(), + AppSizeBreakpoints.compactMode(), leftPadding)); tabs.paddingProperty() .bind(Bindings.createObjectBinding( () -> { - var w = App.getApp().getStage().getWidth(); - if (w >= 1000) { + if (!AppSizeBreakpoints.compactMode().get()) { return new Insets(0, 0, 0, -5); } else { return new Insets(0, 0, 0, 5); } }, - App.getApp().getStage().widthProperty())); + AppSizeBreakpoints.compactMode())); headerHeight.bind(headerArea.heightProperty()); }); } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java index c319ce0b1..6e8db66f0 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java @@ -5,6 +5,7 @@ import io.xpipe.app.comp.RegionDescriptor; import io.xpipe.app.comp.SimpleRegionBuilder; import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppSizeBreakpoints; import io.xpipe.app.ext.FileEntry; import io.xpipe.app.ext.FileInfo; import io.xpipe.app.ext.FileKind; @@ -193,7 +194,7 @@ public final class BrowserFileListComp extends SimpleRegionBuilder { TableColumn sizeCol) { table.widthProperty().subscribe((newValue) -> { if (fileList.getFileSystemModel().getFileSystem().supportsOwnerColumn()) { - ownerCol.setVisible(newValue.doubleValue() > 1000); + ownerCol.setVisible(!AppSizeBreakpoints.compactMode().get()); } if (fileList.getFileSystemModel().getFileSystem().supportsModeColumn()) { diff --git a/app/src/main/java/io/xpipe/app/comp/base/IntroComp.java b/app/src/main/java/io/xpipe/app/comp/base/IntroComp.java index ead39bc61..25ffd21c7 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/IntroComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/IntroComp.java @@ -5,14 +5,13 @@ import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppI18n; import io.xpipe.app.platform.LabelGraphic; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; +import javafx.scene.layout.*; import atlantafx.base.theme.Styles; +import javafx.scene.paint.Color; import lombok.Setter; import org.kordamp.ikonli.javafx.FontIcon; @@ -69,13 +68,12 @@ public class IntroComp extends SimpleRegionBuilder { v.getChildren().add(buttonPane); } - v.setMinWidth(Region.USE_PREF_SIZE); - v.setMaxWidth(Region.USE_PREF_SIZE); - v.setMinHeight(Region.USE_PREF_SIZE); - v.setMaxHeight(Region.USE_PREF_SIZE); + v.setMinWidth(0); + v.setMinHeight(0); v.setSpacing(20); v.getStyleClass().add("intro"); + v.setPadding(new Insets(40, 20, 40, 20)); return v; } } diff --git a/app/src/main/java/io/xpipe/app/comp/base/IntroListComp.java b/app/src/main/java/io/xpipe/app/comp/base/IntroListComp.java index c3462ffa9..a7a5bc973 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/IntroListComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/IntroListComp.java @@ -4,8 +4,10 @@ import io.xpipe.app.comp.SimpleRegionBuilder; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.scene.layout.Background; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; import java.util.List; @@ -20,12 +22,7 @@ public class IntroListComp extends SimpleRegionBuilder { @Override public Region createSimple() { var v = new VerticalComp(intros).build(); - v.setSpacing(80); - v.setMinWidth(Region.USE_PREF_SIZE); - v.setMaxWidth(Region.USE_PREF_SIZE); - v.setMinHeight(Region.USE_PREF_SIZE); v.setMaxHeight(Region.USE_PREF_SIZE); - var sp = new StackPane(v); sp.setPadding(new Insets(40, 0, 0, 0)); sp.setAlignment(Pos.CENTER); diff --git a/app/src/main/java/io/xpipe/app/core/AppSizeBreakpoints.java b/app/src/main/java/io/xpipe/app/core/AppSizeBreakpoints.java new file mode 100644 index 000000000..ad5fcff4b --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/AppSizeBreakpoints.java @@ -0,0 +1,31 @@ +package io.xpipe.app.core; + +import io.xpipe.app.core.window.AppMainWindow; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.value.ObservableBooleanValue; + +public class AppSizeBreakpoints { + + private static final BooleanProperty compactMode = new SimpleBooleanProperty(); + private static final BooleanProperty portraitMode = new SimpleBooleanProperty(); + + public static ObservableBooleanValue compactMode() { + return compactMode; + } + + public static ObservableBooleanValue portraitMode() { + return portraitMode; + } + + public static void init() { + compactMode.bind(Bindings.createBooleanBinding(() -> { + return AppMainWindow.get().getStage().getWidth() <= 1000; + }, AppMainWindow.get().getStage().widthProperty())); + portraitMode.bind(Bindings.createBooleanBinding(() -> { + return AppMainWindow.get().getStage().getWidth() <= 700 && + AppMainWindow.get().getStage().getHeight() > AppMainWindow.get().getStage().getWidth(); + }, AppMainWindow.get().getStage().widthProperty(), AppMainWindow.get().getStage().heightProperty())); + } +} diff --git a/app/src/main/java/io/xpipe/app/core/mode/AppOperationMode.java b/app/src/main/java/io/xpipe/app/core/mode/AppOperationMode.java index 61f949cd4..67f2e2dd3 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/AppOperationMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/AppOperationMode.java @@ -144,6 +144,7 @@ public abstract class AppOperationMode { ThreadHelper.runAsync(() -> { PlatformInit.init(true); AppMainWindow.init(AppOperationMode.getStartupMode() == XPipeDaemonMode.GUI); + AppSizeBreakpoints.init(); }); TrackEvent.info("Finished initial setup"); } catch (Throwable ex) { diff --git a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java index 2c62adb35..44e463aee 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java @@ -88,8 +88,8 @@ public class AppMainWindow { } var stage = App.getApp().getStage(); - stage.setMinWidth(500); - stage.setMinHeight(400); + stage.setMinWidth(300); + stage.setMinHeight(300); INSTANCE = new AppMainWindow(stage); AppModifiedStage.prepareStage(stage); @@ -116,6 +116,7 @@ public class AppMainWindow { AppWindowStyle.addStylesheets(stage.getScene()); AppWindowStyle.addClickShield(stage); AppWindowStyle.addMaximizedPseudoClass(stage); + AppWindowStyle.addSizePseudoClasses(stage); AppWindowStyle.addFontSize(scene); AppTheme.initThemeHandlers(stage); diff --git a/app/src/main/java/io/xpipe/app/core/window/AppWindowStyle.java b/app/src/main/java/io/xpipe/app/core/window/AppWindowStyle.java index cbe4a5ccb..086ee8818 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppWindowStyle.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppWindowStyle.java @@ -38,6 +38,17 @@ public class AppWindowStyle { AppPrefs.get().theme())); } + public static void addSizePseudoClasses(Stage stage) { + stage.getScene().rootProperty().subscribe(root -> { + AppSizeBreakpoints.compactMode().subscribe(v -> { + root.pseudoClassStateChanged(PseudoClass.getPseudoClass("compact"), v); + }); + AppSizeBreakpoints.portraitMode().subscribe(v -> { + root.pseudoClassStateChanged(PseudoClass.getPseudoClass("portrait"), v); + }); + }); + } + public static void addMaximizedPseudoClass(Stage stage) { stage.getScene().rootProperty().subscribe(root -> { stage.maximizedProperty().subscribe(v -> { diff --git a/app/src/main/java/io/xpipe/app/hub/comp/DenseStoreEntryComp.java b/app/src/main/java/io/xpipe/app/hub/comp/DenseStoreEntryComp.java index 42dd07eda..c1b5633f4 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/DenseStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/DenseStoreEntryComp.java @@ -4,6 +4,7 @@ import io.xpipe.app.comp.BaseRegionBuilder; import io.xpipe.app.comp.RegionBuilder; import io.xpipe.app.core.AppFontSizes; +import io.xpipe.app.core.AppSizeBreakpoints; import javafx.beans.binding.Bindings; import javafx.geometry.HPos; import javafx.geometry.Insets; @@ -68,7 +69,9 @@ public class DenseStoreEntryComp extends StoreEntryComp { protected Region createContent() { var grid = new GridPane(); - grid.setHgap(8); + grid.hgapProperty().bind(Bindings.createDoubleBinding(() -> { + return AppSizeBreakpoints.portraitMode().get() ? 2.0 : 6.0; + }, AppSizeBreakpoints.portraitMode())); var tags = createTags().build(); var index = createOrderIndex().build(); diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StandardStoreEntryComp.java b/app/src/main/java/io/xpipe/app/hub/comp/StandardStoreEntryComp.java index 05e3f5901..3a953e173 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StandardStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StandardStoreEntryComp.java @@ -3,8 +3,10 @@ package io.xpipe.app.hub.comp; import io.xpipe.app.comp.BaseRegionBuilder; import io.xpipe.app.comp.RegionBuilder; import io.xpipe.app.core.AppFontSizes; +import io.xpipe.app.core.AppSizeBreakpoints; import io.xpipe.app.util.OsType; +import javafx.beans.binding.Bindings; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -36,7 +38,9 @@ public class StandardStoreEntryComp extends StoreEntryComp { var pinIcon = createPinIcon().build(); var grid = new GridPane(); - grid.setHgap(6); + grid.hgapProperty().bind(Bindings.createDoubleBinding(() -> { + return AppSizeBreakpoints.portraitMode().get() ? 2.0 : 6.0; + }, AppSizeBreakpoints.portraitMode())); grid.setVgap(OsType.ofLocal() == OsType.MACOS ? 2 : 0); var selection = createBatchSelection(); diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreLayoutComp.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreLayoutComp.java index 563ad9801..ceb6cf329 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreLayoutComp.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreLayoutComp.java @@ -5,7 +5,7 @@ import io.xpipe.app.comp.SimpleRegionBuilder; import io.xpipe.app.comp.base.DelayedInitComp; import io.xpipe.app.comp.base.LeftSplitPaneComp; import io.xpipe.app.core.AppLayoutModel; -import io.xpipe.app.core.window.AppMainWindow; +import io.xpipe.app.core.AppSizeBreakpoints; import io.xpipe.app.platform.InputHelper; import io.xpipe.app.terminal.TerminalDockHubComp; import io.xpipe.app.terminal.TerminalDockHubManager; @@ -30,7 +30,7 @@ public class StoreLayoutComp extends SimpleRegionBuilder { private Region createContent() { var filterTrigger = new ObservableSubscriber(); var left = new StoreSidebarComp(filterTrigger); - left.hide(AppMainWindow.get().getStage().widthProperty().lessThan(1000)); + left.hide(AppSizeBreakpoints.compactMode()); left.minWidth(285); left.maxWidth(500); left.minHeight(0); diff --git a/app/src/main/java/io/xpipe/app/util/RemoteDesktopWindow.java b/app/src/main/java/io/xpipe/app/util/RemoteDesktopWindow.java index 5cc0df490..c399244fb 100644 --- a/app/src/main/java/io/xpipe/app/util/RemoteDesktopWindow.java +++ b/app/src/main/java/io/xpipe/app/util/RemoteDesktopWindow.java @@ -112,6 +112,7 @@ public class RemoteDesktopWindow { AppWindowStyle.addStylesheets(stage.getScene()); AppWindowStyle.addClickShield(stage); AppWindowStyle.addMaximizedPseudoClass(stage); + AppWindowStyle.addSizePseudoClasses(stage); AppWindowStyle.addFontSize(scene); AppWindowStyle.addNavigationPseudoClasses(scene); AppTheme.initThemeHandlers(stage); diff --git a/app/src/main/resources/io/xpipe/app/resources/style/prefs.css b/app/src/main/resources/io/xpipe/app/resources/style/prefs.css index 026d6b424..15e6c4ee2 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/prefs.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/prefs.css @@ -2,6 +2,10 @@ -fx-padding: 0 40 0 80; } +.root:portrait .prefs-box { + -fx-padding: 0 30 0 10; +} + .prefs-container .options-comp .name { -fx-padding: 1.8em 0 0 0; -fx-font-weight: BOLD; diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index 9dce026b3..026c2c30a 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -153,6 +153,11 @@ -fx-padding: 5px 0 0 17px; } +.root:portrait .store-entry-section-comp > .children-content { + -fx-padding: 5px 0 0 8; +} + + .store-entry-section-comp > .separator .line { -fx-padding: 0; -fx-border-insets: 0px;