Skip to content

Commit

Permalink
Improved frame drag & sliding/maximizing for 1.2.x
Browse files Browse the repository at this point in the history
Most issues with dragging or clicking at the top of a non-native frame
should now be resolved, along with some sizing issues related to
maximizing.  Toggling maximize while a sidebar pane is active will
now apply to the most-recently-active pane in the main area, instead
of hiding it.

This was a major overhaul of the plugin's styling to support three
different frame styles (obsidian, hidden, and native) with and without
maximizing and sliding, and has only been tested on Windows.  It is
entirely possible I've missed a bug in a configuration or platform I
don't normally use (e.g. Mac, mobile, themes w/elaborate restyling,
etc.), so please open bug reports on Github if you experience any
issues.
  • Loading branch information
pjeby committed May 12, 2023
1 parent 3b816fb commit b3c8e2c
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 20 deletions.
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"id": "pane-relief",
"name": "Pane Relief",
"version": "0.4.2",
"minAppVersion": "0.15.9",
"version": "0.5.0",
"minAppVersion": "1.2.8",
"description": "Per-tab history, hotkeys for pane/tab movement, navigation, sliding workspace, and more",
"author": "PJ Eby",
"authorUrl": "https://github.com/pjeby",
Expand Down
2 changes: 1 addition & 1 deletion src/focus-lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class FocusLock extends Service {

}

function isMain(leaf: WorkspaceLeaf) {
export function isMain(leaf: WorkspaceLeaf) {
const root = leaf?.getRoot();
return !!(root && root !== app.workspace.leftSplit && root !== app.workspace.rightSplit);
}
Expand Down
33 changes: 30 additions & 3 deletions src/maximizing.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Service, toggleClass } from "@ophidian/core";
import { around } from "monkey-around";
import { debounce, requireApiVersion, WorkspaceLeaf, WorkspaceTabs } from "obsidian";
import { isMain } from "./focus-lock";

declare module "obsidian" {
interface Workspace {
requestActiveLeafEvents(): void
rightSidebarToggleButtonEl: HTMLDivElement
}
interface WorkspaceTabs {
scrollIntoView(tab: number): void;
Expand Down Expand Up @@ -43,7 +45,7 @@ export class Maximizer extends Service {
// Switching from maximized popover to non-popover; de-maximize it first
app.commands.executeCommandById("obsidian-hover-editor:restore-active-popover");
}
if (parent) self.refresh(parent, parent.hasClass("should-maximize") ? leaf.containerEl : null);
if (isMain(leaf) && parent) self.refresh(parent, parent.hasClass("should-maximize") ? leaf.containerEl : null);
return old.call(this, leaf, pushHistory, focus);
}}
}));
Expand All @@ -55,6 +57,30 @@ export class Maximizer extends Service {
}
}
}))

// Replace the right sidebar toggle that gets hidden during maximize
app.workspace.onLayoutReady(() => {
const toggle = app.workspace.rightSidebarToggleButtonEl.cloneNode(true) as HTMLDivElement;
toggle.id = "pr-maximize-sb-toggle";
toggle.addEventListener("click", () => app.workspace.rightSplit.toggle());
toggle.ariaLabel = i18next.t(app.workspace.rightSplit.collapsed ? "interface.sidebar-expand" : "interface.sidebar-collapse")
app.workspace.containerEl.parentElement.appendChild(toggle);
this.register(() => toggle.detach());
this.register(around(app.workspace.rightSplit.constructor.prototype, {
expand(old) {
return function() {
toggle.ariaLabel = i18next.t("interface.sidebar-collapse");
return old.call(this);
};
},
collapse(old) {
return function() {
toggle.ariaLabel = i18next.t("interface.sidebar-expand");
return old.call(this);
};
}
}));
})
}

onunload() {
Expand All @@ -63,6 +89,7 @@ export class Maximizer extends Service {
}

toggleMaximize(leaf = app.workspace.activeLeaf) {
if (!leaf || !isMain(leaf)) leaf = app.workspace.getMostRecentLeaf(app.workspace.rootSplit);
const parent = this.parentForLeaf(leaf);
if (!parent) return;
const popoverEl = parent.matchParent(".hover-popover");
Expand Down Expand Up @@ -135,15 +162,15 @@ export class Maximizer extends Service {
if (popovers) for (const popover of popovers) {
if (popover.rootSplit) parents.push(popover.rootSplit.containerEl)
}
return parents;
return parents.map(e => this.parentFor(e));
}

parentForLeaf(leaf: WorkspaceLeaf) {
return this.parentFor(leaf?.containerEl);
}

parentFor(el: Element) {
return el?.matchParent(".workspace-split.mod-root, .hover-popover > .popover-content > .workspace-split");
return el?.matchParent(".workspace, .hover-popover > .popover-content > .workspace-split");
}

}
36 changes: 35 additions & 1 deletion src/sliding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { use, addCommands, command, LayoutSetting, PerWindowComponent, WindowManager, LayoutItem, toggleClass, allWindows, PWCFactory } from "@ophidian/core";
import { Plugin, WorkspaceLeaf, WorkspaceRoot, WorkspaceWindow } from "obsidian";
import { around } from "monkey-around";
import { Plugin, WorkspaceLeaf, WorkspaceRoot, WorkspaceWindow, debounce } from "obsidian";

type SlideSettings = {
active: boolean;
Expand Down Expand Up @@ -31,6 +32,12 @@ export class SlidingPanes extends PerWindowComponent {

update(active: boolean) {
toggleClass(this.container.containerEl, "is-sliding", active);
const parent = this.container.containerEl.matchParent(".workspace");
if (parent) {
toggleClass(parent, "is-sliding", active);
} else {
this.register(this.container.containerEl.onNodeInserted(() => this.update(this.options.active), true));
}
}

activate(leaf: WorkspaceLeaf) {
Expand Down Expand Up @@ -61,6 +68,24 @@ declare module "obsidian" {
class SlidingPanesManager<T extends SlidingPanes> extends WindowManager<T> {
options = new LayoutSetting(this, "pane-relief:sliding-panes", {active: false} as SlideSettings);

// Due to a quirk of how electron handles titlebar draggability (and rendering of
// out-of-view scrolled panes), we need to overlay parts of the title bar to
// ensure they're handled correctly
overlay = app.workspace.containerEl.parentElement.createDiv("prsp-tb-overlay");

requestOverlayUpdate = debounce(() => {
if (!app.workspace.leftSplit.collapsed) {
const r = app.workspace.leftSplit.containerEl.find(".workspace-tabs.mod-top-left-space .workspace-tab-header-spacer").getBoundingClientRect();
this.overlay.style.setProperty("--pr-overlay-width", `${r.width}px`);
this.overlay.style.setProperty("--pr-overlay-left", `${r.left}px`);
}
}, 100, true);

onunload(): void {
super.onunload();
this.overlay.detach();
}

onload() {
super.onload();
window.CodeMirror.getMode({}, "XXX"); // force modes to load, prevents weird sliding
Expand All @@ -73,6 +98,15 @@ class SlidingPanesManager<T extends SlidingPanes> extends WindowManager<T> {
this.registerEvent(this.options.onSet(this.onChange, this));
this.registerEvent(this.options.store.onLoadItem(this.onChange, this));
this.registerEvent(this.onLeafChange(leaf => this.forLeaf(leaf).activate(leaf)));
app.workspace.onLayoutReady(() => {
this.registerEvent(app.workspace.on("layout-change", this.requestOverlayUpdate));
this.registerEvent(app.workspace.on("resize", this.requestOverlayUpdate));
const mgr = this;
this.register(around(app.workspace.leftSplit.constructor.prototype, {
expand(old) { return function() { mgr.requestOverlayUpdate(); return old.call(this); }; }
}));
this.requestOverlayUpdate();
});
}

onChange(item: LayoutItem) {
Expand Down
152 changes: 139 additions & 13 deletions src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,52 @@ settings:
default: 350px
*/

@use "sass:selector";

$win-hidden-frame: "body.is-hidden-frameless";
$win-native-frame: "body:not(.is-frameless)";
$win-obsidian-frame: "body.is-frameless:not(.is-hidden-frameless)";

$win-any: "body";
$win-main: "body:not(.is-popout-window)";
$win-popout: "body.is-popout-window";

$win-has-titlebar: $win-obsidian-frame;
$win-no-titlebar: "#{$win-hidden-frame}, #{$win-native-frame}";

$win-draggable-header: "body:not(.is-grabbing):not(.is-fullscreen)";


@function when($s1, $selectors...) {
$res: $s1;
@each $sel in $selectors {
$res: selector.unify($res, $sel);
}
@return $res;
}

#{when($win-any)} {
// Default: allow space for toggle + controls
--pr-right-frame-space: calc(var(--ribbon-width) + var(--frame-right-space));
}
#{when($win-popout)} {
// Popout: no toggle, just controls
--pr-right-frame-space: var(--frame-right-space);
}

#{when($win-any, $win-obsidian-frame)},
#{when($win-any, $win-native-frame)} {
// Visible title bar in popout - just toggle
--pr-right-frame-space: var(--ribbon-width);
}

#{when($win-popout, $win-obsidian-frame)},
#{when($win-popout, $win-native-frame)} {
// Visible title bar in popout - no controls
--pr-right-frame-space: 0;
}


/* Ensure popovers are above the menu */
.menu.pane-relief-history-menu ~ .popover.hover-popover {
z-index: var(--layer-menu);
Expand Down Expand Up @@ -58,7 +104,7 @@ body:not(.obsidian-themepocalypse).titlebar-button.mod-forward.mod-active:not(:h
{ opacity: 0.75; }

/* Maximizing */
.workspace-split.mod-root.should-maximize,
.workspace.should-maximize .workspace-split.mod-root,
body > .popover.hover-popover .workspace-split.should-maximize
{
.workspace-leaf:not(.is-maximized),
Expand All @@ -73,19 +119,54 @@ body > .popover.hover-popover .workspace-split.should-maximize
max-width: unset !important;
left: unset !important;
}
.workspace-tabs.has-maximized {
flex-basis: 100%;
}
.workspace-leaf.is-maximized {
flex-basis: calc(100% - 4px); // 4px is for scrollbar width
.view-header {
display: flex;
body.is-hidden-frameless:not(.is-fullscreen) & {
padding-left: var(--frame-left-space);
padding-right: var(--frame-right-space);
}
.view-header-icon { display: inherit; }
} // always show view header when maximized
}
}

#pr-maximize-sb-toggle {
display: none;
position: fixed;
background: var(--tab-container-background);
right: 0;
top: 0;
padding-right: var(--size-4-2);
padding-left: var(--size-4-2);
.workspace.should-maximize ~ & {
display: block;
#{$win-hidden-frame} & {
right: var(--frame-right-space);
}
#{$win-obsidian-frame} & {
top: var(--titlebar-height);
}
}
}


.workspace-leaf.is-maximized .view-header {
.workspace:not(.is-right-sidedock-open) & {
// leave space for relocated right ribbon toggle
padding-right: calc(var(--pr-right-frame-space) + var(--size-4-2));
}
#{when($win-draggable-header, $win-hidden-frame)} & {
.view-header-title-container {
/* allow dragging of maximized view header */
-webkit-app-region: drag;
& > * {
-webkit-app-region: no-drag;
}
}
}
}

/* Sliding Panes */

:root {
Expand All @@ -101,21 +182,71 @@ body.is-mobile {
--pr-sliding-panes-width: var(--pr-sliding-panes-mobile-width);
}



.prsp-tb-overlay {
/*
Overlay to ensure left-sidebar draggable area is draggable, even if
there's a non-draggable header underlaying it from a scrolled pane
*/
display: block;
position: fixed;
pointer-events: none;
top: 0;
#{$win-obsidian-frame} & { top: var(--titlebar-height); }
height: var(--header-height);
width: 0;
-webkit-app-region: drag;

#{$win-draggable-header} .workspace.is-left-sidedock-open.is-sliding:not(.should-maximize) ~ & {
width: var(--pr-overlay-width, 0);
left: var(--pr-overlay-left, 0);
}

#{$win-draggable-header} &::after {
/*
Overlay to ensure left-sidebar region is clickable, even if
there's a draggable header underlaying it from a scrolled pane
*/
width: var(--pr-overlay-left, var(--ribbon-width));
height: var(--header-height);
left: 0;
display: block;
position: fixed;
content: "";
-webkit-app-region: no-drag;
}

.workspace:not(.should-maximize) ~ &::before {
/*
Overlay to ensure right-side window controls are clickable, even if
there's a draggable header underlaying them from a scrolled pane
*/
right: 0;
display: block;
position: fixed;
width: var(--pr-right-frame-space);
height: var(--header-height);
content: "";
-webkit-app-region: no-drag;
}
}

.mod-root.is-sliding:not(.has-maximized) {
overflow-x: auto;
overflow-y: hidden;

/* Keep right sidebar toggle visible on 0.16, courtesy @ebullient */
div.workspace-tabs.mod-top.mod-top-right-space .workspace-tab-header-container {
#{$win-main} & div.workspace-tabs.mod-top.mod-top-right-space .workspace-tab-header-container {
padding-right: var(--ribbon-width);
body.is-hidden-frameless:not(.is-fullscreen) & {
padding-right: calc(var(--ribbon-width) + var(--frame-right-space));
padding-right: var(--pr-right-frame-space);
}
}

/* Allow enough space at top of right sidebar for right ribbon toggle to function */
.workspace.is-right-sidedock-open & + .mod-right-split .workspace-tabs.mod-top .workspace-tab-header-container::after {
width: calc(var(--ribbon-width) + var(--frame-right-space));
width: var(--pr-right-frame-space);
}

div.sidebar-toggle-button.mod-right {
Expand All @@ -133,11 +264,6 @@ body.is-mobile {
}
}

.workspace-tabs.mod-top .workspace-tab-header-spacer {
// Turn off draggable space that can block other things in sliding region
-webkit-app-region: no-drag;
}

&>*:not(:last-child:nth-child(2)) { // don't apply when only one pane
width: var(--pr-sliding-panes-width);
flex: none;
Expand Down
1 change: 1 addition & 0 deletions versions.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"0.5.0": "1.2.8",
"0.4.2": "0.15.9",
"0.3.5": "0.15.9",
"0.2.9": "0.15.9",
Expand Down

0 comments on commit b3c8e2c

Please sign in to comment.