diff --git a/.config/ags/modules/.commonwidgets/cairo_navigationindicator.js b/.config/ags/modules/.commonwidgets/cairo_navigationindicator.js index 173c13b51..fd8d5990f 100644 --- a/.config/ags/modules/.commonwidgets/cairo_navigationindicator.js +++ b/.config/ags/modules/.commonwidgets/cairo_navigationindicator.js @@ -6,7 +6,7 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js'; // background-color/color for background/indicator color // padding for pad of indicator // font-size for selected index (0-based) -export const NavigationIndicator = (count, vertical, props) => Widget.DrawingArea({ +export const NavigationIndicator = ({count, vertical, ...props}) => Widget.DrawingArea({ ...props, setup: (area) => { const styleContext = area.get_style_context(); diff --git a/.config/ags/modules/.commonwidgets/tabcontainer.js b/.config/ags/modules/.commonwidgets/tabcontainer.js new file mode 100644 index 000000000..1719686ab --- /dev/null +++ b/.config/ags/modules/.commonwidgets/tabcontainer.js @@ -0,0 +1,87 @@ +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +const { Box, Button, Label, Overlay, Stack } = Widget; +import { MaterialIcon } from './materialicon.js'; +import { NavigationIndicator } from './cairo_navigationindicator.js'; +import { setupCursorHover } from '../.widgetutils/cursorhover.js'; + +export const TabContainer = ({ icons, names, children, className = '', setup = () => {}, ...rest }) => { + const shownIndex = Variable(0); + let previousShownIndex = 0; + const count = Math.min(icons.length, names.length, children.length); + const tabs = Box({ + homogeneous: true, + children: Array.from({ length: count }, (_, i) => Button({ // Tab button + className: 'tab-btn', + onClicked: () => shownIndex.value = i, + setup: setupCursorHover, + child: Box({ + hpack: 'center', + vpack: 'center', + className: 'spacing-h-5 txt-small', + children: [ + MaterialIcon(icons[i], 'norm'), + Label({ + label: names[i], + }) + ] + }) + })), + setup: (self) => self.hook(shownIndex, (self) => { + self.children[previousShownIndex].toggleClassName('tab-btn-active', false); + self.children[shownIndex.value].toggleClassName('tab-btn-active', true); + previousShownIndex = shownIndex.value; + }), + }); + const tabIndicatorLine = Box({ + hexpand: true, + vertical: true, + homogeneous: true, + setup: (self) => self.hook(shownIndex, (self) => { + self.children[0].css = `font-size: ${shownIndex.value}px;`; + }), + children: [NavigationIndicator({ + className: 'tab-indicator', + count: count, + css: `font-size: ${shownIndex.value}px;`, + })], + }); + const tabSection = Box({ + vertical: true, + hexpand: true, + children: [ + tabs, + tabIndicatorLine + ] + }); + const contentStack = Stack({ + transition: 'slide_left_right', + children: children.reduce((acc, currentValue, index) => { + acc[index] = currentValue; + return acc; + }, {}), + setup: (self) => self.hook(shownIndex, (self) => { + self.shown = `${shownIndex.value}`; + }), + }); + const mainBox = Box({ + attribute: { + children: children, + shown: shownIndex, + names: names, + }, + vertical: true, + className: `spacing-v-5 ${className}`, + setup: (self) => { + self.pack_start(tabSection, false, false, 0); + self.pack_end(contentStack, true, true, 0); + setup(self); + }, + ...rest, + }); + mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1); + mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0); + mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count; + + return mainBox; +} diff --git a/.config/ags/modules/sideleft/apiwidgets.js b/.config/ags/modules/sideleft/apiwidgets.js index c950c8a05..216dc9008 100644 --- a/.config/ags/modules/sideleft/apiwidgets.js +++ b/.config/ags/modules/sideleft/apiwidgets.js @@ -4,7 +4,7 @@ import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; const { Box, Button, CenterBox, Entry, EventBox, Icon, Label, Overlay, Revealer, Scrollable, Stack } = Widget; const { execAsync, exec } = Utils; import { setupCursorHover, setupCursorHoverInfo } from '../.widgetutils/cursorhover.js'; -import { contentStack } from './sideleft.js'; +import { widgetContent } from './sideleft.js'; // APIs import GPTService from '../../services/gpt.js'; import Gemini from '../../services/gemini.js'; @@ -81,13 +81,11 @@ export const chatEntry = TextView({ // Global keybinds if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && event.get_keyval()[1] === Gdk.KEY_Page_Down) { - const toSwitchTab = contentStack.get_visible_child(); - toSwitchTab.attribute.nextTab(); + widgetContent.nextTab(); } else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && event.get_keyval()[1] === Gdk.KEY_Page_Up) { - const toSwitchTab = contentStack.get_visible_child(); - toSwitchTab.attribute.prevTab(); + widgetContent.prevTab(); } }) , diff --git a/.config/ags/modules/sideleft/sideleft.js b/.config/ags/modules/sideleft/sideleft.js index 87a1b70ed..5ae293849 100644 --- a/.config/ags/modules/sideleft/sideleft.js +++ b/.config/ags/modules/sideleft/sideleft.js @@ -6,10 +6,10 @@ const { Box, Button, EventBox, Label, Revealer, Scrollable, Stack } = Widget; const { execAsync, exec } = Utils; import { MaterialIcon } from '../.commonwidgets/materialicon.js'; import { setupCursorHover } from '../.widgetutils/cursorhover.js'; -import { NavigationIndicator } from '../.commonwidgets/cairo_navigationindicator.js'; import toolBox from './toolbox.js'; import apiWidgets from './apiwidgets.js'; import { chatEntry } from './apiwidgets.js'; +import { TabContainer } from '../.commonwidgets/tabcontainer.js'; const contents = [ { @@ -25,78 +25,6 @@ const contents = [ friendlyName: 'Tools', }, ] -let currentTabId = 0; - -export const contentStack = Stack({ - vexpand: true, - transition: 'slide_left_right', - transitionDuration: 160, - children: contents.reduce((acc, item) => { - acc[item.name] = item.content; - return acc; - }, {}), -}) - -function switchToTab(id) { - const allTabs = navTabs.get_children(); - const tabButton = allTabs[id]; - allTabs[currentTabId].toggleClassName('sidebar-selector-tab-active', false); - allTabs[id].toggleClassName('sidebar-selector-tab-active', true); - contentStack.shown = contents[id].name; - if (tabButton) { - // Fancy highlighter line width - const buttonWidth = tabButton.get_allocated_width(); - const highlightWidth = tabButton.get_children()[0].get_allocated_width(); - navIndicator.css = ` - font-size: ${id}px; - padding: 0px ${(buttonWidth - highlightWidth) / 2}px; - `; - } - currentTabId = id; -} -const SidebarTabButton = (navIndex) => Widget.Button({ - // hexpand: true, - className: 'sidebar-selector-tab', - onClicked: (self) => { - switchToTab(navIndex); - }, - child: Box({ - hpack: 'center', - className: 'spacing-h-5', - children: [ - MaterialIcon(contents[navIndex].materialIcon, 'larger'), - Label({ - className: 'txt txt-smallie', - label: `${contents[navIndex].friendlyName}`, - }) - ] - }), - setup: (button) => Utils.timeout(1, () => { - setupCursorHover(button); - button.toggleClassName('sidebar-selector-tab-active', currentTabId == navIndex); - }), -}); - -const navTabs = Box({ - homogeneous: true, - children: contents.map((item, id) => - SidebarTabButton(id, item.materialIcon, item.friendlyName) - ), -}); - -const navIndicator = NavigationIndicator(2, false, { // The line thing - className: 'sidebar-selector-highlight', - css: 'font-size: 0px; padding: 0rem 4.160rem;', // Shushhhh -}); - -const navBar = Box({ - vertical: true, - hexpand: true, - children: [ - navTabs, - navIndicator, - ] -}); const pinButton = Button({ attribute: { @@ -131,6 +59,17 @@ const pinButton = Button({ }, }) +export const widgetContent = TabContainer({ + icons: contents.map((item) => item.materialIcon), + names: contents.map((item) => item.friendlyName), + children: contents.map((item) => item.content), + className: 'sidebar-left spacing-v-10', + setup: (self) => self.hook(App, (self, currentName, visible) => { + if (currentName === 'sideleft') + self.toggleClassName('sidebar-pinned', pinButton.attribute.enabled && visible); + }), +}); + export default () => Box({ // vertical: true, vexpand: true, @@ -142,27 +81,7 @@ export default () => Box({ onSecondaryClick: () => App.closeWindow('sideleft'), onMiddleClick: () => App.closeWindow('sideleft'), }), - Box({ - vertical: true, - vexpand: true, - className: 'sidebar-left spacing-v-10', - children: [ - Box({ - className: 'spacing-h-10', - children: [ - navBar, - pinButton, - ] - }), - contentStack, - ], - setup: (self) => self - .hook(App, (self, currentName, visible) => { - if (currentName === 'sideleft') - self.toggleClassName('sidebar-pinned', pinButton.attribute.enabled && visible); - }) - , - }), + widgetContent, ], setup: (self) => self .on('key-press-event', (widget, event) => { // Handle keybinds @@ -172,13 +91,13 @@ export default () => Box({ pinButton.attribute.toggle(pinButton); // Switch sidebar tab else if (event.get_keyval()[1] === Gdk.KEY_Tab) - switchToTab((currentTabId + 1) % contents.length); + widgetContent.cycleTab(); else if (event.get_keyval()[1] === Gdk.KEY_Page_Up) - switchToTab(Math.max(currentTabId - 1, 0)); + widgetContent.prevTab(); else if (event.get_keyval()[1] === Gdk.KEY_Page_Down) - switchToTab(Math.min(currentTabId + 1, contents.length - 1)); + widgetContent.nextTab(); } - if (contentStack.shown == 'apis') { // If api tab is focused + if (widgetContent.attribute.names[widgetContent.attribute.shown.value] == 'APIs') { // If api tab is focused // Focus entry when typing if (( !(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && @@ -196,12 +115,12 @@ export default () => Box({ // Switch API type else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && event.get_keyval()[1] === Gdk.KEY_Page_Down) { - const toSwitchTab = contentStack.get_visible_child(); + const toSwitchTab = widgetContent.attribute.children[widgetContent.attribute.shown.value]; toSwitchTab.attribute.nextTab(); } else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && event.get_keyval()[1] === Gdk.KEY_Page_Up) { - const toSwitchTab = contentStack.get_visible_child(); + const toSwitchTab = widgetContent.attribute.children[widgetContent.attribute.shown.value]; toSwitchTab.attribute.prevTab(); } } diff --git a/.config/ags/modules/sideright/calendar.js b/.config/ags/modules/sideright/calendar.js index 817d0b2df..43847e890 100644 --- a/.config/ags/modules/sideright/calendar.js +++ b/.config/ags/modules/sideright/calendar.js @@ -200,3 +200,4 @@ export const ModuleCalendar = () => Box({ box.pack_end(contentStack, false, false, 0); } }) + diff --git a/.config/ags/modules/sideright/todolist.js b/.config/ags/modules/sideright/todolist.js index b84bed2a6..7cc417a4a 100644 --- a/.config/ags/modules/sideright/todolist.js +++ b/.config/ags/modules/sideright/todolist.js @@ -2,6 +2,7 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; const { Box, Button, Label, Revealer } = Widget; import { MaterialIcon } from '../.commonwidgets/materialicon.js'; +import { TabContainer } from '../.commonwidgets/tabcontainer.js'; import Todo from "../../services/todo.js"; import { setupCursorHover } from '../.widgetutils/cursorhover.js'; import { NavigationIndicator } from '../.commonwidgets/cairo_navigationindicator.js'; @@ -199,81 +200,11 @@ const UndoneTodoList = () => { }); } -const todoItemsBox = Widget.Stack({ - vpack: 'fill', - transition: 'slide_left_right', - children: { - 'undone': UndoneTodoList(), - 'done': todoItems(true), - }, -}); - -export const TodoWidget = () => { - const TodoTabButton = (isDone, navIndex) => Widget.Button({ - hexpand: true, - className: 'sidebar-selector-tab', - onClicked: (button) => { - todoItemsBox.shown = `${isDone ? 'done' : 'undone'}`; - const kids = button.get_parent().get_children(); - for (let i = 0; i < kids.length; i++) { - if (kids[i] != button) kids[i].toggleClassName('sidebar-selector-tab-active', false); - else button.toggleClassName('sidebar-selector-tab-active', true); - } - // Fancy highlighter line width - const buttonWidth = button.get_allocated_width(); - const highlightWidth = button.get_children()[0].get_allocated_width(); - navIndicator.css = ` - font-size: ${navIndex}px; - padding: 0px ${(buttonWidth - highlightWidth) / 2}px; - `; - }, - child: Box({ - hpack: 'center', - className: 'spacing-h-5', - children: [ - MaterialIcon(`${isDone ? 'task_alt' : 'format_list_bulleted'}`, 'larger'), - Label({ - className: 'txt txt-smallie', - label: `${isDone ? 'Done' : 'Unfinished'}`, - }) - ] - }), - setup: (button) => Utils.timeout(1, () => { - setupCursorHover(button); - button.toggleClassName('sidebar-selector-tab-active', defaultTodoSelected === `${isDone ? 'done' : 'undone'}`); - }), - }); - const undoneButton = TodoTabButton(false, 0); - const doneButton = TodoTabButton(true, 1); - const navIndicator = NavigationIndicator(2, false, { // The line thing - className: 'sidebar-selector-highlight', - css: 'font-size: 0px; padding: 0rem 1.636rem;', // Shush - }) - return Widget.Box({ - hexpand: true, - vertical: true, - className: 'spacing-v-10', - setup: (box) => { // undone/done selector rail - box.pack_start(Widget.Box({ - vertical: true, - children: [ - Widget.Box({ - className: 'sidebar-selectors spacing-h-5', - homogeneous: true, - setup: (box) => { - box.pack_start(undoneButton, false, true, 0); - box.pack_start(doneButton, false, true, 0); - } - }), - Widget.Box({ - className: 'sidebar-selector-highlight-offset', - homogeneous: true, - children: [navIndicator] - }) - ] - }), false, false, 0); - box.pack_end(todoItemsBox, true, true, 0); - }, - }); -}; - +export const TodoWidget = () => TabContainer({ + icons: ['format_list_bulleted', 'task_alt'], + names: ['Unfinished', 'Done'], + children: [ + UndoneTodoList(), + todoItems(true), + ] +}) diff --git a/.config/ags/scss/_common.scss b/.config/ags/scss/_common.scss index f7530f59b..00a509efd 100644 --- a/.config/ags/scss/_common.scss +++ b/.config/ags/scss/_common.scss @@ -231,4 +231,27 @@ popover { .gap-h-15 { min-width: 1.023rem; +} + +.tab-btn { + @include small-rounding; + @include element_decel; + min-height: 2.5rem; + color: $onSurface; +} + +.tab-btn:hover, +.tab-btn:focus { + background-color: $hovercolor; +} + +.tab-btn-active>box>label { + color: $primary; +} + +.tab-indicator { + transition: 180ms ease-in-out; // Doesn't look that good, but it syncs with the GtkStack + min-height: 0.205rem; + padding: 0rem 1.023rem; + color: $primary; } \ No newline at end of file diff --git a/.config/ags/scss/_sidebars.scss b/.config/ags/scss/_sidebars.scss index b6ecac92d..6ed2bab3a 100644 --- a/.config/ags/scss/_sidebars.scss +++ b/.config/ags/scss/_sidebars.scss @@ -301,38 +301,6 @@ $onChatgpt: $onPrimary; color: mix($onSurfaceVariant, $surfaceVariant, 85%); } -.sidebar-selector-tab { - @include small-rounding; - @include element_decel; - min-height: 2.5rem; - color: $onSurface; -} - -.sidebar-selector-tab:hover, -.sidebar-selector-tab:focus { - background-color: $hovercolor; -} - -.sidebar-selector-tab:active { - background-color: $activecolor; -} - -.sidebar-selector-tab-active>box>label { - color: $primary; -} - -.sidebar-selector-highlight-offset { - margin-top: -0.205rem; - margin-bottom: 0.205rem; -} - -.sidebar-selector-highlight { - transition: 180ms ease-in-out; // Doesn't look that good, but it syncs with the GtkStack - color: $primary; - // padding: 0rem 2.045rem; - min-height: 0.205rem; -} - .sidebar-todo-item { padding-right: 0.545rem; } @@ -373,10 +341,10 @@ $onChatgpt: $onPrimary; .sidebar-todo-new { @include full-rounding; @include element_decel; - color: $onSecondaryContainer; + background-color: $textboxColor; + color: $onSurfaceVariant; margin: 0.341rem; padding: 0.205rem 0.545rem; - border: 0.068rem solid $onSurface; } .sidebar-todo-new,