diff --git a/.github/workflows/changelog-print.yml b/.github/workflows/changelog-print.yml deleted file mode 100644 index a3009e3a..00000000 --- a/.github/workflows/changelog-print.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: changelogPrint - -on: - push: - branches: [main] - -jobs: - build: - runs-on: ubuntu-latest - name: changelogPrint - steps: - - uses: actions/checkout@v3 - - name: jdk 11 - uses: actions/setup-java@v3 - with: - java-version: 11 - distribution: 'temurin' - - name: gradle caching - uses: gradle/gradle-build-action@v2 - with: - gradle-home-cache-cleanup: true - - run: ./gradlew changelogPrint diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml deleted file mode 100644 index 2c02ce0f..00000000 --- a/.github/workflows/gradle-wrapper-validation.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: "Validate Gradle Wrapper" -on: - push: - paths: - - 'gradlew' - - 'gradlew.bat' - - 'gradle/wrapper/' - pull_request: - paths: - - 'gradlew' - - 'gradlew.bat' - - 'gradle/wrapper/' - -permissions: - contents: read - -jobs: - validation: - name: "Validation" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 diff --git a/.gitignore b/.gitignore index 12308e51..61ac0877 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # gradle stuff .gradle/ build/ +.kotlin/ # IntelliJ .idea diff --git a/CHANGES.md b/CHANGES.md index 9c92bd15..d146e715 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,9 @@ ## [Unreleased] ### Changed +- **BREAKING** `SwtMisc.systemFontWidth` now returns `double` instead of `int`. + - added new methods `systemFontWidthTimes(int)` and `systemFontWidthTimes(String)` to make this easier to deal with +- **BREAKING** remove RxJava completely in favor of Kotlin Flow. - Bump required java from 11 to 17. ## [4.3.1] - 2024-07-05 diff --git a/build.gradle b/build.gradle index e1d8ede8..84ffc6c0 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,6 @@ subprojects { subProject -> "https://javadoc.io/doc/com.diffplug.durian/durian-concurrent/${VER_DURIAN}", "https://javadoc.io/doc/com.diffplug.durian/durian-debug/${VER_DURIAN_DEBUG}", "https://javadoc.io/doc/com.diffplug.durian/durian-rx/${VER_DURIAN_RX}", - "https://javadoc.io/doc/io.reactivex.rxjava2/rxjava/${VER_RXJAVA}", 'https://docs.oracle.com/javase/8/docs/api/' ].join(' ') diff --git a/durian-swt/build.gradle b/durian-swt/build.gradle index 47a3a822..eaba880e 100644 --- a/durian-swt/build.gradle +++ b/durian-swt/build.gradle @@ -22,7 +22,6 @@ p2deps { dependencies { api project(':durian-swt.os') api "com.diffplug.durian:durian-rx:$VER_DURIAN_RX" - api "io.reactivex.rxjava2:rxjava:$VER_RXJAVA" implementation "com.diffplug.durian:durian-core:$VER_DURIAN" implementation "com.diffplug.durian:durian-collect:$VER_DURIAN" implementation "com.diffplug.durian:durian-concurrent:$VER_DURIAN" diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/Shells.kt b/durian-swt/src/main/java/com/diffplug/common/swt/Shells.kt index 79b1820d..0a4f8695 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/Shells.kt +++ b/durian-swt/src/main/java/com/diffplug/common/swt/Shells.kt @@ -21,8 +21,7 @@ import com.diffplug.common.swt.os.WS import com.diffplug.common.tree.TreeIterable import com.diffplug.common.tree.TreeQuery import com.diffplug.common.tree.TreeStream -import io.reactivex.disposables.Disposable -import io.reactivex.disposables.Disposables +import kotlinx.coroutines.Job import org.eclipse.swt.SWT import org.eclipse.swt.graphics.Image import org.eclipse.swt.graphics.Point @@ -36,7 +35,7 @@ import java.util.* /** A fluent builder for creating SWT [Shell]s. */ class Shells private constructor(private val style: Int, private val coat: Coat) { - private var title: String = "" + private var title: String? = null private var image: Image? = null private var alpha = SWT.DEFAULT private val size = Point(SWT.DEFAULT, SWT.DEFAULT) @@ -434,7 +433,7 @@ class Shells private constructor(private val style: Int, private val coat: Coat) /** Prevents the given shell from closing without prompting. Returns a Subscription which can cancel this blocking. */ @JvmStatic - fun confirmClose(shell: Shell, title: String, question: String, runOnClose: Runnable): Disposable { + fun confirmClose(shell: Shell, title: String, question: String, runOnClose: Runnable): Job { val listener = Listener { e: Event -> e.doit = SwtMisc.blockForQuestion(title, question, shell) if (e.doit) { @@ -442,9 +441,11 @@ class Shells private constructor(private val style: Int, private val coat: Coat) } } shell.addListener(SWT.Close, listener) - return Disposables.fromRunnable { - SwtExec.immediate().guardOn(shell).execute { - shell.removeListener(SWT.Close, listener) + return Job().apply { + invokeOnCompletion { + SwtExec.immediate().guardOn(shell).execute { + shell.removeListener(SWT.Close, listener) + } } } } diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/SwtExec.java b/durian-swt/src/main/java/com/diffplug/common/swt/SwtExec.java index 2ebabb36..91e9a787 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/SwtExec.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/SwtExec.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2022 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,30 +27,19 @@ import com.diffplug.common.util.concurrent.MoreExecutors; import com.diffplug.common.util.concurrent.Runnables; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.reactivex.Scheduler; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; -import io.reactivex.disposables.Disposables; -import io.reactivex.schedulers.Schedulers; -import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.Delayed; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RunnableFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; import java.util.function.Supplier; import kotlin.coroutines.CoroutineContext; @@ -332,7 +321,7 @@ public RxExecutor getRxExecutor() { } SwtExec() { - this(exec -> Rx.callbackOn(exec, new SwtScheduler(exec), new SwtDispatcher(exec))); + this(exec -> Rx.callbackOn(exec, new SwtDispatcher(exec))); } SwtExec(Function rxExecutorCreator) { @@ -722,206 +711,6 @@ public MainCoroutineDispatcher getImmediate() { } } - /** Scheduler that runs tasks on Swt's event dispatch thread. */ - static final class SwtScheduler extends Scheduler { - final SwtExec exec; - - public SwtScheduler(SwtExec exec) { - this.exec = exec; - } - - @Override - public Worker createWorker() { - return new SwtWorker(exec); - } - - static final class SwtWorker extends Scheduler.Worker { - final SwtExec exec; - - volatile boolean unsubscribed; - - /** Set of active tasks, guarded by this. */ - Set tasks; - - public SwtWorker(SwtExec exec) { - this.exec = exec; - this.tasks = new HashSet<>(); - } - - @Override - public void dispose() { - if (unsubscribed) { - return; - } - unsubscribed = true; - - Set set; - synchronized (this) { - set = tasks; - tasks = null; - } - - if (set != null) { - for (SwtScheduledAction a : set) { - a.cancelFuture(); - } - } - } - - void remove(SwtScheduledAction a) { - if (unsubscribed) { - return; - } - synchronized (this) { - if (unsubscribed) { - return; - } - - tasks.remove(a); - } - } - - @Override - public boolean isDisposed() { - return unsubscribed; - } - - @Override - public Disposable schedule(Runnable action) { - if (unsubscribed) { - return Disposables.disposed(); - } - - SwtScheduledAction a = new SwtScheduledAction(action, this); - - synchronized (this) { - if (unsubscribed) { - return Disposables.disposed(); - } - - tasks.add(a); - } - - exec.execute(a); - - if (unsubscribed) { - a.cancel(); - return Disposables.disposed(); - } - - return a; - } - - @Override - public Disposable schedule(Runnable action, long delayTime, TimeUnit unit) { - if (unsubscribed) { - return Disposables.disposed(); - } - - SwtScheduledAction a = new SwtScheduledAction(action, this); - - synchronized (this) { - if (unsubscribed) { - return Disposables.disposed(); - } - - tasks.add(a); - } - - Future f = exec.schedule(a, delayTime, unit); - - if (unsubscribed) { - a.cancel(); - f.cancel(true); - return Disposables.disposed(); - } - - a.setFuture(f); - - return a; - } - - /** - * Represents a cancellable asynchronous Runnable that wraps an action - * and manages the associated Worker lifecycle. - */ - static final class SwtScheduledAction implements Runnable, Disposable { - final Runnable action; - - final SwtWorker parent; - - volatile Future future; - @SuppressWarnings("rawtypes") - static final AtomicReferenceFieldUpdater FUTURE = AtomicReferenceFieldUpdater.newUpdater(SwtScheduledAction.class, Future.class, "future"); - - static final Future CANCELLED = new FutureTask<>(() -> {}, null); - - static final Future FINISHED = new FutureTask<>(() -> {}, null); - - volatile int state; - static final AtomicIntegerFieldUpdater STATE = AtomicIntegerFieldUpdater.newUpdater(SwtScheduledAction.class, "state"); - - static final int STATE_ACTIVE = 0; - static final int STATE_FINISHED = 1; - static final int STATE_CANCELLED = 2; - - public SwtScheduledAction(Runnable action, SwtWorker parent) { - this.action = action; - this.parent = parent; - } - - @Override - public void run() { - if (!parent.unsubscribed && state == STATE_ACTIVE) { - try { - action.run(); - } finally { - FUTURE.lazySet(this, FINISHED); - if (STATE.compareAndSet(this, STATE_ACTIVE, STATE_FINISHED)) { - parent.remove(this); - } - } - } - } - - @Override - public boolean isDisposed() { - return state != STATE_ACTIVE; - } - - @Override - public void dispose() { - if (STATE.compareAndSet(this, STATE_ACTIVE, STATE_CANCELLED)) { - parent.remove(this); - } - cancelFuture(); - } - - void setFuture(Future f) { - if (FUTURE.compareAndSet(this, null, f)) { - if (future != FINISHED) { - f.cancel(true); - } - } - } - - void cancelFuture() { - Future f = future; - if (f != CANCELLED && f != FINISHED) { - f = FUTURE.getAndSet(this, CANCELLED); - if (f != null && f != CANCELLED && f != FINISHED) { - f.cancel(true); - } - } - } - - void cancel() { - state = STATE_CANCELLED; - } - } - } - } - /** Global executor for actions which should only execute immediately on the SWT thread. */ private static SwtExec swtOnly; @@ -937,7 +726,7 @@ void cancel() { @SuppressFBWarnings(value = "LI_LAZY_INIT_STATIC", justification = "This race condition is fine, see comment in SwtExec.blocking()") public static SwtExec swtOnly() { if (swtOnly == null) { - swtOnly = new SwtExec(exec -> Rx.callbackOn(exec, new SwtOnlyScheduler(), new SwtOnlyDispatcher())) { + swtOnly = new SwtExec(exec -> Rx.callbackOn(exec, new SwtOnlyDispatcher())) { @Override public void execute(Runnable runnable) { requireNonNull(runnable); @@ -968,55 +757,6 @@ public void dispatch(@NotNull CoroutineContext coroutineContext, @NotNull Runnab } } - /** - * Copied straight from rx.schedulers.ImmediateScheduler, - * but checks for the SWT thread before running stuff, - * and handles future-scheduling correctly. - */ - static final class SwtOnlyScheduler extends Scheduler { - @Override - public Worker createWorker() { - return new InnerImmediateScheduler(); - } - - private static final class InnerImmediateScheduler extends Scheduler.Worker { - final Disposable innerSubscription = Disposables.empty(); - - @Override - public Disposable schedule(Runnable action, long delayTime, TimeUnit unit) { - CompositeDisposable sub = new CompositeDisposable(); - Future future = SwtExec.async().schedule(() -> { - if (!sub.isDisposed()) { - action.run(); - sub.dispose(); - } - }, delayTime, unit); - sub.add(Disposables.fromFuture(future)); - return sub; - } - - @Override - public Disposable schedule(Runnable action) { - if (Thread.currentThread() == swtThread) { - action.run(); - } else { - SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); - } - return Disposables.disposed(); - } - - @Override - public void dispose() { - innerSubscription.dispose(); - } - - @Override - public boolean isDisposed() { - return innerSubscription.isDisposed(); - } - } - } - private static class SameThreadCoroutineDispatcher extends CoroutineDispatcher { @Override public void dispatch(@NotNull CoroutineContext coroutineContext, @NotNull Runnable runnable) { @@ -1024,7 +764,7 @@ public void dispatch(@NotNull CoroutineContext coroutineContext, @NotNull Runnab } } - private static final SwtExec sameThread = new SwtExec(exec -> Rx.callbackOn(MoreExecutors.directExecutor(), Schedulers.trampoline(), new SameThreadCoroutineDispatcher())) { + private static final SwtExec sameThread = new SwtExec(exec -> Rx.callbackOn(MoreExecutors.directExecutor(), new SameThreadCoroutineDispatcher())) { @Override public void execute(Runnable runnable) { requireNonNull(runnable); diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/SwtMisc.java b/durian-swt/src/main/java/com/diffplug/common/swt/SwtMisc.java index 1d94ac8e..d3817e86 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/SwtMisc.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/SwtMisc.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt; - import com.diffplug.common.base.Box; import com.diffplug.common.base.Errors; import com.diffplug.common.base.Preconditions; @@ -358,7 +357,7 @@ public static int blockForMessageBox(String title, String message, int style) { /////////////////// /** The cached height of the system font. */ static int systemFontHeight = 0; - static int systemFontWidth = 0; + static double systemFontWidth = 0; /** Populates the height and width of the system font. */ private static void populateSystemFont() { @@ -368,7 +367,7 @@ private static void populateSystemFont() { FontMetrics metrics = gc.getFontMetrics(); systemFontHeight = metrics.getHeight(); - systemFontWidth = metrics.getAverageCharWidth(); + systemFontWidth = metrics.getAverageCharacterWidth(); if (OS.getNative().isMac()) { // add 20% width on Mac systemFontWidth = (systemFontWidth * 12) / 10; @@ -387,13 +386,21 @@ public static int systemFontHeight() { } /** Returns the width of the system font. */ - public static int systemFontWidth() { + public static double systemFontWidth() { if (systemFontWidth == 0) { populateSystemFont(); } return systemFontWidth; } + public static int systemFontWidthTimes(int numChars) { + return (int) Math.round(systemFontWidth() * numChars); + } + + public static int systemFontWidthTimes(String str) { + return systemFontWidthTimes(str.length()); + } + /** Returns a distance which is a snug fit for a line of text in the system font. */ public static int systemFontSnug() { return systemFontHeight() + Layouts.defaultMargin(); @@ -401,12 +408,12 @@ public static int systemFontSnug() { /** Returns the default width of a button, scaled for the system font. */ public static int defaultButtonWidth() { - return systemFontWidth() * " Cancel ".length(); + return systemFontWidthTimes(" Cancel "); } /** Returns the default width of a dialog. */ public static int defaultDialogWidth() { - return 50 * systemFontWidth(); + return systemFontWidthTimes(50); } /** Returns a size which is scaled by the system font's height. */ @@ -421,7 +428,7 @@ public static int scaleByFontHeight(int rows) { /** Returns a point that represents the size of a (cols x rows) grid of characters printed in the standard system font. */ public static Point scaleByFont(int cols, int rows) { - return new Point(cols * systemFontWidth(), rows * systemFontHeight()); + return new Point(systemFontWidthTimes(cols), rows * systemFontHeight()); } /** Returns a dimension which is guaranteed to be comfortable for the given string. */ diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/SwtRx.java b/durian-swt/src/main/java/com/diffplug/common/swt/SwtRx.java index 1243314c..4212a772 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/SwtRx.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/SwtRx.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2022 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt; - import com.diffplug.common.base.Preconditions; import com.diffplug.common.collect.ImmutableList; import com.diffplug.common.primitives.Ints; @@ -51,19 +50,19 @@ public class SwtRx { /** Subscribes to the given widget and pipes the events to an {@link Flow}<{@link Event}>. */ public static @SwtThread Flow addListener(Widget widget, Stream events) { - MutableSharedFlow observable = Rx.INSTANCE.createEmitFlow(); + MutableSharedFlow observable = Rx.createEmitFlow(); events.forEach(event -> widget.addListener(event, e -> { - Rx.INSTANCE.emit(observable, e); + Rx.emit(observable, e); })); return observable; } /** Returns an {@link Flow}<{@link Point}> of the right-click mouse-up on the given control, in global coordinates. */ public static @SwtThread Flow rightClickGlobal(Control ctl) { - MutableSharedFlow observable = Rx.INSTANCE.createEmitFlow(); + MutableSharedFlow observable = Rx.createEmitFlow(); ctl.addListener(MouseClick.RIGHT_CLICK_EVENT, e -> { if (e.button == MouseClick.RIGHT.code()) { - Rx.INSTANCE.emit(observable, ctl.toDisplay(e.x, e.y)); + Rx.emit(observable, ctl.toDisplay(e.x, e.y)); } }); return observable; @@ -71,10 +70,10 @@ public class SwtRx { /** Returns an {@link Flow}<{@link Point}> of the right-click mouse-up on the given control, in local coordinates. */ public static @SwtThread Flow rightClickLocal(Control ctl) { - MutableSharedFlow observable = Rx.INSTANCE.createEmitFlow(); + MutableSharedFlow observable = Rx.createEmitFlow(); ctl.addListener(MouseClick.RIGHT_CLICK_EVENT, e -> { if (e.button == MouseClick.RIGHT.code()) { - Rx.INSTANCE.emit(observable, new Point(e.x, e.y)); + Rx.emit(observable, new Point(e.x, e.y)); } }); return observable; diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/SwtThread.java b/durian-swt/src/main/java/com/diffplug/common/swt/SwtThread.java index 0dc37930..80c2a23c 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/SwtThread.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/SwtThread.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.diffplug.common.swt; - +import com.diffplug.common.rx.IFlowable; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,7 +35,7 @@ * * When annotated on a parameter or field, it * must be either an {@link rx.Observable}, - * {@link com.diffplug.common.rx.IObservable}, or a + * {@link IFlowable}, or a * {@link java.util.concurrent.CompletionStage}, and * it indicates that the given code should only be * set and listened to from SWT. diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/jface/ViewerMisc.java b/durian-swt/src/main/java/com/diffplug/common/swt/jface/ViewerMisc.java index 6d831053..4076c7c0 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/jface/ViewerMisc.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/jface/ViewerMisc.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt.jface; - import com.diffplug.common.base.Converter; import com.diffplug.common.base.Preconditions; import com.diffplug.common.collect.ImmutableList; @@ -55,7 +54,7 @@ public static void singleSelection(StructuredViewer viewer, RxBox { + SwtExec.immediate().guardOn(viewer.getControl()).subscribe(box, optional -> { if (optional.isPresent()) { viewer.setSelection(new StructuredSelection(optional.get())); } else { diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/widgets/NoBorderBtn.java b/durian-swt/src/main/java/com/diffplug/common/swt/widgets/NoBorderBtn.java index 7c3e8c3a..8c8b2d2a 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/widgets/NoBorderBtn.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/widgets/NoBorderBtn.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,10 @@ */ package com.diffplug.common.swt.widgets; - +import com.diffplug.common.rx.Rx; import com.diffplug.common.swt.ControlWrapper; -import io.reactivex.Observable; -import io.reactivex.subjects.PublishSubject; +import kotlinx.coroutines.flow.Flow; +import kotlinx.coroutines.flow.MutableSharedFlow; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; @@ -38,7 +38,7 @@ public class NoBorderBtn extends ControlWrapper.AroundControl { private Image img = null; private Rectangle imgBounds; private boolean enabled = true; - private PublishSubject selection = PublishSubject.create(); + private MutableSharedFlow selection = Rx.createEmitFlow(); public NoBorderBtn(Composite parent) { super(new Canvas(parent, SWT.NONE)); @@ -61,17 +61,13 @@ public NoBorderBtn(Composite parent) { // send a selection event on each click (if we aren't disabled) wrapped.addListener(SWT.MouseDown, e -> { if (enabled) { - selection.onNext(this); + Rx.emit(selection, this); } }); - // send a "completed" event when we finish - wrapped.addListener(SWT.Dispose, e -> { - selection.onComplete(); - }); } /** Returns an Observable which responds to clicks. */ - public Observable clicked() { + public Flow clicked() { return selection; } diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/widgets/RadioGroup.java b/durian-swt/src/main/java/com/diffplug/common/swt/widgets/RadioGroup.java index b614c21f..592dd244 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/widgets/RadioGroup.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/widgets/RadioGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt.widgets; - import com.diffplug.common.base.Preconditions; import com.diffplug.common.collect.BiMap; import com.diffplug.common.collect.HashBiMap; @@ -84,7 +83,7 @@ public RxBox buildOn(Composite cmp) { // when the selection is changed, set the button Button firstBtn = Iterables.get(mapping.keySet(), 0); - SwtExec.immediate().guardOn(firstBtn).subscribe(selection.asObservable(), obj -> { + SwtExec.immediate().guardOn(firstBtn).subscribe(selection, obj -> { Button selectedBtn = mapping.inverse().get(obj); for (Button btn : mapping.keySet()) { btn.setSelection(btn == selectedBtn); diff --git a/durian-swt/src/main/java/com/diffplug/common/swt/widgets/ScaleCtl.java b/durian-swt/src/main/java/com/diffplug/common/swt/widgets/ScaleCtl.java index 24677bf7..79567e36 100644 --- a/durian-swt/src/main/java/com/diffplug/common/swt/widgets/ScaleCtl.java +++ b/durian-swt/src/main/java/com/diffplug/common/swt/widgets/ScaleCtl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt.widgets; - import com.diffplug.common.base.Preconditions; import com.diffplug.common.rx.RxBox; import com.diffplug.common.swt.ControlWrapper; @@ -136,7 +135,7 @@ public ScaleCtl(Composite parent, Builder builder, RxBox box) { } text = new Text(wrapped, SWT.BORDER | SWT.SINGLE); - Layouts.setGridData(text).grabHorizontal().widthHint(SwtMisc.systemFontWidth() * 4); + Layouts.setGridData(text).grabHorizontal().widthHint(SwtMisc.systemFontWidthTimes(4)); SwtExec.async().guardOn(text).execute(text::selectAll); Listener selectAllListener = SwtMisc.asListener(SwtExec.async().guardOn(text).wrap(text::selectAll)); diff --git a/durian-swt/src/test/java/com/diffplug/common/swt/ShellsTest.java b/durian-swt/src/test/java/com/diffplug/common/swt/ShellsTest.java index 098fae50..999d7e16 100644 --- a/durian-swt/src/test/java/com/diffplug/common/swt/ShellsTest.java +++ b/durian-swt/src/test/java/com/diffplug/common/swt/ShellsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt; - import java.util.function.Consumer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; @@ -27,7 +26,7 @@ @Category(InteractiveTest.class) public class ShellsTest { - private static final int UNIT = SwtMisc.systemFontWidth() * 20; + private static final int UNIT = SwtMisc.systemFontWidthTimes(20); @Test public void testPack() { diff --git a/durian-swt/src/test/java/com/diffplug/common/swt/SwtExecProfile.java b/durian-swt/src/test/java/com/diffplug/common/swt/SwtExecProfile.java index 3fedadd3..2a618850 100644 --- a/durian-swt/src/test/java/com/diffplug/common/swt/SwtExecProfile.java +++ b/durian-swt/src/test/java/com/diffplug/common/swt/SwtExecProfile.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,12 @@ import com.diffplug.common.debug.JuxtaProfiler; import com.diffplug.common.debug.LapTimer; import com.diffplug.common.rx.Rx; -import io.reactivex.disposables.Disposable; -import io.reactivex.subjects.PublishSubject; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.RejectedExecutionException; import java.util.function.Consumer; +import kotlinx.coroutines.Job; +import kotlinx.coroutines.flow.MutableSharedFlow; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Widget; import org.junit.Test; @@ -131,18 +131,17 @@ public void addSwtExec(String name, SwtExec underTest) { public void run(Widget guard) { JuxtaProfiler profiler = new JuxtaProfiler(); profiler.addTestNanoWrap2Sec("control", () -> { - PublishSubject subject = PublishSubject.create(); - subject.subscribe(val -> {}); + MutableSharedFlow subject = Rx.createEmitFlow(); drain(subject); }); toProfile.forEach((name, underTest) -> { profiler.addTest(name, new JuxtaProfiler.InitTimedCleanup(LapTimer.createNanoWrap2Sec()) { - PublishSubject subject; - Disposable sub; + MutableSharedFlow subject; + Job sub; @Override protected void init() throws Throwable { - subject = PublishSubject.create(); + subject = Rx.createEmitFlow(); sub = underTest.guardOn(guard).subscribeDisposable(subject, val -> {}); } @@ -153,7 +152,7 @@ protected void timed() throws Throwable { @Override protected void cleanup() throws Throwable { - sub.dispose(); + sub.cancel(null); subject = null; sub = null; } @@ -162,11 +161,10 @@ protected void cleanup() throws Throwable { profiler.runRandomTrials(NUM_TRIALS); } - private static void drain(PublishSubject subject) { + private static void drain(MutableSharedFlow subject) { for (int i = 0; i < EVENTS_TO_PUSH; ++i) { - subject.onNext(0); + Rx.emit(subject, 0); } - subject.onComplete(); } } diff --git a/durian-swt/src/test/java/com/diffplug/common/swt/jface/ViewerMiscTest.java b/durian-swt/src/test/java/com/diffplug/common/swt/jface/ViewerMiscTest.java index 65a3376c..9a0455b0 100644 --- a/durian-swt/src/test/java/com/diffplug/common/swt/jface/ViewerMiscTest.java +++ b/durian-swt/src/test/java/com/diffplug/common/swt/jface/ViewerMiscTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt.jface; - import com.diffplug.common.base.Errors; import com.diffplug.common.base.StringPrinter; import com.diffplug.common.collect.ImmutableList; @@ -175,7 +174,7 @@ public void testLazyContentProviderFile() { .setLabelProviderText(File::getName); format.addColumn().setText("Last Modified") .setLabelProviderText(file -> new Date(file.lastModified()).toString()) - .setLayoutPixel(SwtMisc.systemFontWidth() * new Date().toString().length()); + .setLayoutPixel(SwtMisc.systemFontWidthTimes(new Date().toString())); // create the tree viewer TreeViewer viewer = format.buildTree(cmp); @@ -203,7 +202,7 @@ public void testLazyContentProviderPath() { .setLabelProviderText(path -> path.getFileName().toString()); format.addColumn().setText("Last Modified") .setLabelProviderText(path -> Errors.suppress().getWithDefault(() -> Files.getLastModifiedTime(path).toString(), "")) - .setLayoutPixel(SwtMisc.systemFontWidth() * new Date().toString().length()); + .setLayoutPixel(SwtMisc.systemFontWidthTimes(new Date().toString())); // create the tree viewer TreeViewer viewer = format.buildTree(cmp); diff --git a/durian-swt/src/test/java/com/diffplug/common/swt/widgets/RadioGroupTest.java b/durian-swt/src/test/java/com/diffplug/common/swt/widgets/RadioGroupTest.java index 4911e9e1..cb62148d 100644 --- a/durian-swt/src/test/java/com/diffplug/common/swt/widgets/RadioGroupTest.java +++ b/durian-swt/src/test/java/com/diffplug/common/swt/widgets/RadioGroupTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 DiffPlug + * Copyright (C) 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.diffplug.common.swt.widgets; - import com.diffplug.common.rx.RxBox; import com.diffplug.common.swt.InteractiveTest; import com.diffplug.common.swt.Layouts; @@ -65,7 +64,7 @@ public void test() { readLbl.setText("Read selection: "); Text readTxt = new Text(debugParent, SWT.BORDER | SWT.SINGLE | SWT.READ_ONLY); - SwtExec.immediate().guardOn(readTxt).subscribe(selection.asObservable(), option -> { + SwtExec.immediate().guardOn(readTxt).subscribe(selection, option -> { readTxt.setText(option.name()); }); diff --git a/gradle.properties b/gradle.properties index 58e53c64..e3547083 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,9 +13,8 @@ VER_FINDBUGS=3.0.1 # Dependencies VER_DURIAN=1.2.0 -VER_DURIAN_RX=4.0.0 +VER_DURIAN_RX=5.0.0 VER_DURIAN_DEBUG=1.0.0 -VER_RXJAVA=2.0.0 # SWT Dependencies from P2 SWT_VERSION=4.21 SWT_VERSION_X86=4.7