]> uap-core.de Git - rssreader.git/commitdiff
add feed setting that controls if items are opened in the internal webview
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 13 Oct 2025 10:26:49 +0000 (12:26 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 13 Oct 2025 10:26:49 +0000 (12:26 +0200)
rss-application/src/main/kotlin/de/unixwork/rssreader/App.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/Database.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/FeedCollection.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/FeedList.kt
rss-application/src/main/kotlin/de/unixwork/rssreader/MainWindow.kt
ui-java/src/main/java/de/unixwork/ui/ContainerBuilder.java
ui-java/src/main/java/de/unixwork/ui/Event.java
ui-java/src/main/java/de/unixwork/ui/UiInteger.java
ui-kotlin/src/main/kotlin/de/unixwork/ui/kotlin/Toplevel.kt

index 351944ce059138a3adeedd8f33b2d424ed402011..2ad97de93d8699b98efc610090b76a805c74340e 100644 (file)
@@ -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() {
index 5099931f31be5dc2c7b0704d0a441608153d6d1f..1cac918ef0fe89ce480cdf3f777f58a939fc6adc 100644 (file)
@@ -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
index 538161f4695b01b6d42dd08e12a5d9aba3fb3053..f7704f1258024e525d421441443995bb16cf2d79 100644 (file)
@@ -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<Item>()
     var itemsLoaded = false
index fa898880a321ff93bc4738ec0bc6a6f9f38e40c5..c3100323d42e261da1bfb19374ef24976788ee7f 100644 (file)
@@ -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)
             }
         }
     }
index 7ed810665d331524d26c3b76dd89e229f32c1b7a..519348c1642aaeee007c67e7bbb74740557cdfec 100644 (file)
@@ -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()
index 9f71679a8307716bb0cdb60873bc444956005ea5..576dac0f21da781e9e3e788dac12e1dfce222fc1 100644 (file)
@@ -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;
     }
 }
index f83bac20c3028c5e373579fad13608e1a7a6a61d..8964692d9543c39d31096b76e12997df0e045c4d 100644 (file)
@@ -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;
index ab688f8fe19c8235660635625e1d41b300c4ec1a..dd74eef162914a12e296b09c6975fbd3ee4e36ec 100644 (file)
@@ -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);
+    }
 }
index 4f56acc4485026974d022bbd8eece23c45cb10a5..ca8e5525a8c8fd0fca16142235637d22d73942b4 100644 (file)
@@ -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
         )
     }