Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlinify ControlWrapper and Shells #27

Merged
merged 4 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# DurianSwt releases

## [Unreleased]
### Changed
- Convert `ControlWrapper` and `Shells` to kotlin for better nullability. ([#27](https://github.com/diffplug/durian-swt/pull/27))

## [4.3.0] - 2023-12-08
### Added
Expand Down
145 changes: 0 additions & 145 deletions durian-swt/src/main/java/com/diffplug/common/swt/ControlWrapper.java

This file was deleted.

126 changes: 126 additions & 0 deletions durian-swt/src/main/java/com/diffplug/common/swt/ControlWrapper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright 2020 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.common.swt

import org.eclipse.swt.widgets.Composite
import org.eclipse.swt.widgets.Control
import org.eclipse.swt.widgets.Shell


/**
* Wraps an SWT [Control] to encapsulate its API.
*
*
* The traditional way to make a custom class is this: `class CustomControl extends [Composite]`
*
*
* This has three main problems:
*
* 1. Users can add random widgets to your "Control" because it exposes the [Composite] interface.
* 1. Users can set the layout to your "Control" because it exposes the [Composite] interface.
* 1. Users can add random listeners to your "Control", and overridding [Widget.addListener][org.eclipse.swt.widgets.Widget.addListener] to intercept them is a **very dangerous plan**.
*
*
*
* ControlWrapper fixes this by providing an low-overhead skeleton which encapsulates the
* SWT Control that you're using as the base of your custom control, which allows you to only
* expose the APIs that are appropriate.
*/
interface ControlWrapper {
var layoutData: Any?
/** Returns the LayoutData for this control. */
get() = rootControl.layoutData
/** Sets the LayoutData for this control. */
set(layoutData) {
rootControl.layoutData = layoutData
}

val parent: Composite
/** Returns the parent of this Control. */
get() = rootControl.parent

val shell: Shell
/** Returns the parent Shell of this Control. */
get() = rootControl.shell

/** Disposes the underlying control. */
fun dispose() {
rootControl.dispose()
}

val isDisposed: Boolean
/** Returns true iff the underlying control is disposed. */
get() = rootControl.isDisposed

/**
* Changes the parent of the widget to be the one provided.
* Returns `true` if the parent is successfully changed
*/
fun setParent(parent: Composite): Boolean {
return rootControl.setParent(parent)
}

/**
* Returns the wrapped [Control] (only appropriate for limited purposes!).
*
*
* The implementor of this ControlWrapper is free to change the wrapped Control
* as she sees fit, and she doesn't have to tell you about it! You shouldn't rely
* on this control being anything in particular.
*
*
* You *can* rely on this Control for:
*
* 1. Managing lifetimes: `wrapped.getRootControl().addListener(SWT.Dispose, ...`
*
*
*
* But that's all. If you use it for something else, it's on you when it breaks.
*/
val rootControl: Control

/** Default implementation of a [ControlWrapper] which wraps a [Control]. */
open class AroundControl<T : Control>
/** Creates a ControlWrapper which wraps the given control. */(
/** The wrapped control. */
@JvmField protected val wrapped: T
) : ControlWrapper {
override val rootControl: T
get() = wrapped
}

/** Default implementation of a [ControlWrapper] which wraps some other form of `ControlWrapper` with a new interface. */
open class AroundWrapper<T : ControlWrapper>(
@JvmField
protected val wrapped: T
) : ControlWrapper {
override val rootControl: Control
get() = wrapped.rootControl
}

/** Creates a ControlWrapper which wraps the given control. */
class Transparent<T : Control>(
override val rootControl: T
) : ControlWrapper

companion object {
/** Most-efficient way to transparently pass a Control to a ControlWrapper API. */
@JvmStatic
fun <T : Control> transparent(control: T): Transparent<T> {
return Transparent(control)
}
}
}
Loading
Loading