From 9214c7de2782bb5fe3d3c9c1e323d602a6e57388 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 13 Oct 2025 12:26:49 +0200 Subject: [PATCH] add feed setting that controls if items are opened in the internal webview --- .../main/kotlin/de/unixwork/rssreader/App.kt | 10 ++--- .../kotlin/de/unixwork/rssreader/Database.kt | 5 +++ .../de/unixwork/rssreader/FeedCollection.kt | 1 + .../kotlin/de/unixwork/rssreader/FeedList.kt | 30 +++++++------ .../de/unixwork/rssreader/MainWindow.kt | 44 +++++++++---------- .../java/de/unixwork/ui/ContainerBuilder.java | 13 +++++- .../src/main/java/de/unixwork/ui/Event.java | 8 ++-- .../main/java/de/unixwork/ui/UiInteger.java | 8 ++++ .../kotlin/de/unixwork/ui/kotlin/Toplevel.kt | 10 +++++ 9 files changed, 84 insertions(+), 45 deletions(-) diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt index 351944c..2ad97de 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt @@ -1,8 +1,6 @@ package de.unixwork.rssreader import de.unixwork.ui.Application -import de.unixwork.ui.Container.grid -import de.unixwork.ui.Text.textfield import de.unixwork.ui.ToolbarPosition import de.unixwork.ui.Toolkit import de.unixwork.ui.kotlin.ToolkitDispatcher @@ -12,10 +10,8 @@ import de.unixwork.ui.kotlin.toolbarAppMenu import de.unixwork.ui.kotlin.toolbarToggleItem import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.IO import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import org.h2.api.H2Type.row import java.io.IOException import java.time.LocalDate @@ -72,9 +68,12 @@ object App : Application { markCurrentFeedAsRead() } - toolbarToggleItem(name = "starred", icon = "starred", varname = "starred", tooltip = "star the current item") { event -> + toolbarToggleItem(name = "starred", icon = "starred", varname = "starred", tooltip = "Star the current item") { event -> setBookmark(event.intValue == 1) } + toolbarToggleItem(name = "preview", icon = "view-reveal", varname = "browser", tooltip = "Open the current item uri in the internal webview") { event -> + window?.feedList?.updateWebView() + } toolbarAppMenu { menuItem(label = "Update All") { @@ -107,6 +106,7 @@ object App : Application { addToolbarDefault("reloadFeed", ToolbarPosition.LEFT) addToolbarDefault("markCurrentFeed", ToolbarPosition.LEFT) addToolbarDefault("starred", ToolbarPosition.RIGHTPANEL_LEFT) + addToolbarDefault("preview", position = ToolbarPosition.RIGHTPANEL_LEFT) } override fun startup() { diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/Database.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/Database.kt index 5099931..1cac918 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/Database.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/Database.kt @@ -63,6 +63,7 @@ object Database { feedcollection_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, group_id INT NOT NULL REFERENCES groups(group_id) ON DELETE CASCADE, pos INT default 0, + internal_browser INT default 0, name VARCHAR, update_interval INT, max_item_age INT DEFAULT 0, @@ -121,6 +122,7 @@ object Database { f.name as feed_name, f.update_interval, f.item_state_mode, + f.internal_browser, c.unread_count from groups g left join feedcollections f on g.group_id = f.group_id @@ -136,6 +138,7 @@ object Database { val feedId = rs.getInt("feedcollection_id") val feedName = rs.getString("feed_name") val updateInterval = rs.getLong("update_interval") + val internalBrowser = rs.getBoolean("internal_browser") val itemStateMode = rs.getInt("item_state_mode") val unreadCount = rs.getInt("unread_count") @@ -148,6 +151,7 @@ object Database { val feed = FeedCollection(feedId, feedName) feed.updateInterval = updateInterval feed.itemStateMode = itemStateMode + feed.internalBrowser = internalBrowser feed.unreadItemsCount = unreadCount currentGroup.feeds.add(feed) } @@ -187,6 +191,7 @@ object Database { user: String? = null, password: String? = null, cert: String? = null, + internalBrowser: Boolean = false, updateInterval: Long = 0, maxItemAge: Int = 0, itemStateMode: Int = 0) : FeedCollection diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedCollection.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedCollection.kt index 538161f..f7704f1 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedCollection.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedCollection.kt @@ -8,6 +8,7 @@ class FeedCollection(id: Int, name: String) { var updateInterval: Long = 0 var itemStateMode = 0 var unreadItemsCount = 0 + var internalBrowser = false var items = mutableListOf() var itemsLoaded = false diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedList.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedList.kt index fa89888..c310032 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedList.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/FeedList.kt @@ -5,11 +5,7 @@ import de.unixwork.ui.UiLinkData import de.unixwork.ui.kotlin.ToolkitDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.IO import kotlinx.coroutines.launch -import java.awt.Desktop -import java.net.URI -import java.net.URISyntaxException class FeedList(window: MainWindow) : Document() { val window = window @@ -25,7 +21,7 @@ class FeedList(window: MainWindow) : Document() { val webview = webview("webview") val tabview = integer("tabview") val starred = integer("starred") - val preview = integer("preview") + val browser = integer("browser") // Feed that is currently shown var currentFeed: FeedCollection? = null @@ -107,11 +103,16 @@ class FeedList(window: MainWindow) : Document() { } else { starred.setIntValue(0) } - val content = item.getContent() - webview.loadContent(item.link, content.text, content.type, "utf-8") - tabview.setIntValue(1) - preview.setIntValue(0) // reset preview toggle button + tabview.setIntValue(1) // select item page + + currentFeed?.let { feed -> + browser.setBooleanValue(feed.internalBrowser) + } ?: { + browser.setBooleanValue(false) + } + + updateWebView() // Update read status if(!item.isRead) { @@ -124,13 +125,16 @@ class FeedList(window: MainWindow) : Document() { } } - fun togglePreview() { + fun updateWebView() { currentItem?.let { item -> - if(preview.intValue() == 0) { + if(browser.booleanValue()) { + webview.loadUrl(item.link) + // TODO: enable when more browser features (go prev/next) are implemented + //window.window.ui.setState(MainWindow.ITEM_INTERNAL_BROWSER) + } else { val content = item.getContent() webview.loadContent(item.link, content.text, content.type, "utf-8") - } else { - webview.loadUrl(item.link) + window.window.ui.unsetState(MainWindow.ITEM_INTERNAL_BROWSER) } } } diff --git a/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt b/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt index 7ed8106..519348c 100644 --- a/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt +++ b/rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt @@ -10,7 +10,6 @@ import de.unixwork.ui.UiList import de.unixwork.ui.UiString import de.unixwork.ui.UiText import de.unixwork.ui.kotlin.Toplevel -import de.unixwork.ui.kotlin.sidebarWindow import de.unixwork.ui.kotlin.dialogWindow import de.unixwork.ui.kotlin.openFileDialog import de.unixwork.ui.kotlin.setDefaultWindowSize @@ -23,6 +22,7 @@ class MainWindow() { companion object { const val ITEM_HAS_AUTHOR = 1000 const val ITEM_HAS_CATEGORY = 1001 + const val ITEM_INTERNAL_BROWSER = 1002 } val window : Toplevel @@ -36,6 +36,8 @@ class MainWindow() { var currentSublistIndex = -1 var currentFeedIndex = -1 + var browserSetting = 0 // 0: use browser setting from feed, 1: always use internal browser, 2: never use internal browser + init { setDefaultWindowSize(1600, 900) window = splitViewWindow(title = "RSS Reader", sidebar = true) { @@ -93,9 +95,9 @@ class MainWindow() { varname = "items", fill = true, onSelection = { event -> - if(event.set != 0) { + if(event.set) { // Don't handle set events: in some toolkit implementations, updating the - // list triggers a onSelection event + // list triggers an onSelection event return@table } feedList.items.selected?.let { @@ -170,20 +172,17 @@ class MainWindow() { rlabel("Link:", hfill = true) linkbutton(varname = "link", styleClass = "ui-nopadding"); } + row { - hbox(spacing = 4, colspan = 2) { - linkbutton(label = "Browser", varname = "link2", type = LinkButtonType.BUTTON) - togglebutton (label = "Preview", tooltip = "Open article in the internal webview", varname = "preview") { event -> - if(event.set == 0) { - feedList.togglePreview() - } + vbox(hfill = true, vfill = true, hexpand = true, vexpand = true, colspan = 2) { + hbox(spacing = 4, visibilityStates = intArrayOf(ITEM_INTERNAL_BROWSER)) { + button(icon = "go-previous") + button(icon = "go-next") + textfield(varname = "web-uri", fill = true) } + webview(varname = "webview", fill = true, colspan = 2) } } - - row { - webview(varname = "webview", hfill = true, vfill = true, hexpand = true, vexpand = true, colspan = 2) - } } } } @@ -243,15 +242,16 @@ class MainWindow() { parent?.let { try { val feedCol = Database.newFeeds( - it, - feedName, - uris, - user.toString(), - password.toString(), - cert.toString(), - 0, // TODO - maxItemAge, - itemStateMode + parent = it, + name = feedName, + uris = uris, + user = user.toString(), + password = password.toString(), + cert = cert.toString(), + internalBrowser = false, + updateInterval = 0, // TODO + maxItemAge = maxItemAge, + itemStateMode = itemStateMode ) parent.feeds.update() diff --git a/ui-java/src/main/java/de/unixwork/ui/ContainerBuilder.java b/ui-java/src/main/java/de/unixwork/ui/ContainerBuilder.java index 9f71679..576dac0 100644 --- a/ui-java/src/main/java/de/unixwork/ui/ContainerBuilder.java +++ b/ui-java/src/main/java/de/unixwork/ui/ContainerBuilder.java @@ -28,6 +28,8 @@ public class ContainerBuilder extends AbstractWidgetBuilder{ private int columnspacing; private int rowspacing; + private int[] visibilityStates; + public ContainerBuilder(UiObject obj, MethodHandle widgetConstructor) { this.obj = obj; this.widgetConstructor = widgetConstructor; @@ -142,6 +144,11 @@ public class ContainerBuilder extends AbstractWidgetBuilder{ return this; } + public ContainerBuilder visibilityStates(int[] states) { + this.visibilityStates = states; + return this; + } + @Override public MemorySegment createArgs(Arena arena) throws Throwable { ArgFuncs ui = ArgFuncs.getInstance(); @@ -228,6 +235,10 @@ public class ContainerBuilder extends AbstractWidgetBuilder{ Container container = createContainer(); ui.callback(); container.close(); - return container.getWidget(); + UiWidget w = container.getWidget(); + if(visibilityStates != null) { + w.setVisibilityStates(visibilityStates); + } + return w; } } diff --git a/ui-java/src/main/java/de/unixwork/ui/Event.java b/ui-java/src/main/java/de/unixwork/ui/Event.java index f83bac2..8964692 100644 --- a/ui-java/src/main/java/de/unixwork/ui/Event.java +++ b/ui-java/src/main/java/de/unixwork/ui/Event.java @@ -8,7 +8,7 @@ public class Event { private Object windowData; private Object eventData; private int intval; - private int set; + private boolean set; private MemorySegment windowPtr; private MemorySegment documentPtr; @@ -35,7 +35,7 @@ public class Event { document = toolkit.getDocument(documentPtr.address()); intval = (int)ui.event_get_int.invoke(eventPtr); - set = (int)ui.event_get_set.invoke(eventPtr); + set = ((int)ui.event_get_set.invoke(eventPtr)) != 0; eventDataPtr = (MemorySegment)ui.event_get_eventdata.invoke(eventPtr); int type = (int)ui.event_get_eventdatatype.invoke(eventPtr); @@ -120,9 +120,9 @@ public class Event { this.intval = intval; } - public int getSet() { return set; } + public boolean getSet() { return set; } - public void setSet(int set) { this.set = set; } + public void setSet(boolean set) { this.set = set; } public MemorySegment getWindowPtr() { return windowPtr; diff --git a/ui-java/src/main/java/de/unixwork/ui/UiInteger.java b/ui-java/src/main/java/de/unixwork/ui/UiInteger.java index ab688f8..dd74eef 100644 --- a/ui-java/src/main/java/de/unixwork/ui/UiInteger.java +++ b/ui-java/src/main/java/de/unixwork/ui/UiInteger.java @@ -46,4 +46,12 @@ public class UiInteger { public void setIntValue(int value) { setLongValue(value); } + + public boolean booleanValue() { + return longValue() != 0; + } + + public void setBooleanValue(boolean value) { + setLongValue(value ? 1 : 0); + } } diff --git a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt index 4f56acc..ca8e552 100644 --- a/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt +++ b/ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt @@ -76,6 +76,7 @@ class Toplevel(obj: UiObject) { spacing: Int = -1, columnspacing: Int = -1, rowspacing: Int = -1, + visibilityStates: IntArray? = null, ui: ContainerUI? = null ): UiWidget { if(fill) { @@ -138,6 +139,9 @@ class Toplevel(obj: UiObject) { if(rowspacing > 0) { container.rowspacing(rowspacing) } + if(visibilityStates != null) { + container.visibilityStates(visibilityStates) + } return container.create(ui) } @@ -159,6 +163,7 @@ class Toplevel(obj: UiObject) { name: String? = null, styleClass: String? = null, spacing: Int = -1, + visibilityStates: IntArray? = null, ui: ContainerUI? = null ): UiWidget { return createContainer( @@ -179,6 +184,7 @@ class Toplevel(obj: UiObject) { name = name, styleClass = styleClass, spacing = spacing, + visibilityStates = visibilityStates, ui = ui ) } @@ -201,6 +207,7 @@ class Toplevel(obj: UiObject) { name: String? = null, styleClass: String? = null, spacing: Int = -1, + visibilityStates: IntArray? = null, ui: ContainerUI? = null ): UiWidget { return createContainer( @@ -221,6 +228,7 @@ class Toplevel(obj: UiObject) { name = name, styleClass = styleClass, spacing = spacing, + visibilityStates = visibilityStates, ui = ui ) } @@ -249,6 +257,7 @@ class Toplevel(obj: UiObject) { spacing: Int = -1, columnspacing: Int = -1, rowspacing: Int = -1, + visibilityStates: IntArray? = null, ui: ContainerUI? = null ): UiWidget { return createContainer( @@ -275,6 +284,7 @@ class Toplevel(obj: UiObject) { spacing = spacing, columnspacing = columnspacing, rowspacing = rowspacing, + visibilityStates = visibilityStates, ui = ui ) } -- 2.47.3