Remove IconItem class
This commit is contained in:
parent
bdc3b08027
commit
b859b34a67
9 changed files with 3 additions and 592 deletions
|
@ -31,8 +31,6 @@ set(SRCS
|
|||
src/popupwindow.cpp
|
||||
|
||||
src/fakewindow.cpp
|
||||
src/iconitem.cpp
|
||||
src/managedtexturenode.cpp
|
||||
)
|
||||
|
||||
set(RESOURCES
|
||||
|
|
|
@ -50,7 +50,7 @@ Item {
|
|||
dragStarted = false
|
||||
}
|
||||
|
||||
IconItem {
|
||||
Meui.IconItem {
|
||||
id: icon
|
||||
anchors.centerIn: parent
|
||||
width: control.iconSize
|
||||
|
|
427
src/iconitem.cpp
427
src/iconitem.cpp
|
@ -1,427 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: cutefish <cutefishos@foxmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "iconitem.h"
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QPaintEngine>
|
||||
#include <QApplication>
|
||||
#include <QQuickWindow>
|
||||
#include <QPixmap>
|
||||
#include <QImageReader>
|
||||
|
||||
#include "managedtexturenode.h"
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if <!std::is_integral<T>(), bool>::type almost_equal(T x, T y, int ulp)
|
||||
{
|
||||
return std::abs(x - y) <std::numeric_limits<T>::epsilon() * std::abs(x + y) * ulp
|
||||
|| std::abs(x - y) <std::numeric_limits<T>::min();
|
||||
}
|
||||
|
||||
class IconItemSource
|
||||
{
|
||||
public:
|
||||
explicit IconItemSource(IconItem *iconItem)
|
||||
: m_iconItem(iconItem)
|
||||
{
|
||||
}
|
||||
virtual ~IconItemSource()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool isValid() const = 0;
|
||||
virtual const QSize size() const = 0;
|
||||
virtual QPixmap pixmap(const QSize &size) = 0;
|
||||
|
||||
protected:
|
||||
QQuickWindow *window() {
|
||||
return m_iconItem->window();
|
||||
}
|
||||
|
||||
IconItem *m_iconItem;
|
||||
};
|
||||
|
||||
class NullSource : public IconItemSource
|
||||
{
|
||||
public:
|
||||
explicit NullSource(IconItem *iconItem)
|
||||
: IconItemSource(iconItem)
|
||||
{
|
||||
}
|
||||
|
||||
bool isValid() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QSize size() const override
|
||||
{
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QPixmap pixmap(const QSize &size) override
|
||||
{
|
||||
Q_UNUSED(size)
|
||||
return QPixmap();
|
||||
}
|
||||
};
|
||||
|
||||
class QIconSource : public IconItemSource
|
||||
{
|
||||
public:
|
||||
explicit QIconSource(const QIcon &icon, IconItem *iconItem)
|
||||
: IconItemSource(iconItem)
|
||||
{
|
||||
m_icon = icon;
|
||||
}
|
||||
|
||||
bool isValid() const override
|
||||
{
|
||||
return !m_icon.isNull();
|
||||
}
|
||||
|
||||
const QSize size() const override
|
||||
{
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QPixmap pixmap(const QSize &size) override
|
||||
{
|
||||
QPixmap result = m_icon.pixmap(window(), m_icon.actualSize(size));
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
QIcon m_icon;
|
||||
};
|
||||
|
||||
class QImageSource : public IconItemSource
|
||||
{
|
||||
public:
|
||||
explicit QImageSource(const QImage &imageIcon, IconItem *iconItem)
|
||||
: IconItemSource(iconItem)
|
||||
{
|
||||
m_imageIcon = imageIcon;
|
||||
}
|
||||
|
||||
bool isValid() const override
|
||||
{
|
||||
return !m_imageIcon.isNull();
|
||||
}
|
||||
|
||||
const QSize size() const override
|
||||
{
|
||||
const QSize s = m_imageIcon.size();
|
||||
if (s.isValid()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QPixmap pixmap(const QSize &size) override
|
||||
{
|
||||
Q_UNUSED(size)
|
||||
return QPixmap::fromImage(m_imageIcon);
|
||||
}
|
||||
|
||||
private:
|
||||
QImage m_imageIcon;
|
||||
};
|
||||
|
||||
class SvgSource : public IconItemSource
|
||||
{
|
||||
public:
|
||||
explicit SvgSource(const QString &sourceString, IconItem *iconItem)
|
||||
: IconItemSource(iconItem)
|
||||
{
|
||||
m_reader.setFileName(sourceString);
|
||||
}
|
||||
|
||||
bool isValid() const override {
|
||||
return m_reader.canRead();
|
||||
}
|
||||
|
||||
const QSize size() const override {
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QPixmap pixmap(const QSize &size) override {
|
||||
m_reader.setScaledSize(size * devicePixelRatio());
|
||||
return QPixmap::fromImage(m_reader.read());
|
||||
}
|
||||
|
||||
private:
|
||||
qreal devicePixelRatio() {
|
||||
return window() ? window()->devicePixelRatio() : qApp->devicePixelRatio();
|
||||
}
|
||||
|
||||
QImageReader m_reader;
|
||||
QString m_svgIconName;
|
||||
};
|
||||
|
||||
IconItem::IconItem(QQuickItem *parent)
|
||||
: QQuickItem(parent)
|
||||
, m_iconItemSource(new NullSource(this))
|
||||
, m_active(false)
|
||||
, m_animated(false)
|
||||
, m_roundToIconSize(true)
|
||||
, m_textureChanged(false)
|
||||
, m_sizeChanged(false)
|
||||
{
|
||||
setFlag(ItemHasContents, true);
|
||||
setSmooth(true);
|
||||
}
|
||||
|
||||
void IconItem::setSource(const QVariant &source)
|
||||
{
|
||||
if (source == m_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool oldValid = isValid();
|
||||
|
||||
m_source = source;
|
||||
QString sourceString = source.toString();
|
||||
|
||||
// If the QIcon was created with QIcon::fromTheme(), try to load it as svg
|
||||
if (source.canConvert<QIcon>() && !source.value<QIcon>().name().isEmpty()) {
|
||||
sourceString = source.value<QIcon>().name();
|
||||
}
|
||||
|
||||
if (!sourceString.isEmpty()) {
|
||||
// If a file:// URL or a absolute path is passed, take the image pointed by that from disk
|
||||
QString localFile;
|
||||
if (sourceString.startsWith(QLatin1String("file:"))) {
|
||||
localFile = QUrl(sourceString).toLocalFile();
|
||||
} else if (sourceString.startsWith(QLatin1Char('/'))) {
|
||||
localFile = sourceString;
|
||||
} else if (sourceString.startsWith("qrc:/")) {
|
||||
localFile = sourceString.remove(0, 3);
|
||||
} else if (sourceString.startsWith(":/")) {
|
||||
localFile = sourceString;
|
||||
}
|
||||
|
||||
if (!localFile.isEmpty()) {
|
||||
if (sourceString.endsWith(QLatin1String(".svg"))
|
||||
|| sourceString.endsWith(QLatin1String(".svgz"))
|
||||
|| sourceString.endsWith(QLatin1String(".ico"))) {
|
||||
QIcon icon = QIcon(localFile);
|
||||
m_iconItemSource.reset(new QIconSource(icon, this));
|
||||
} else {
|
||||
QImage imageIcon = QImage(localFile);
|
||||
m_iconItemSource.reset(new QImageSource(imageIcon, this));
|
||||
}
|
||||
} else {
|
||||
// if (sourceString.startsWith("qrc:/"))
|
||||
// m_iconItemSource.reset(new SvgSource(sourceString.remove(0, 3), this));
|
||||
// else if (sourceString.startsWith(":/"))
|
||||
// m_iconItemSource.reset(new SvgSource(sourceString, this));
|
||||
|
||||
if (!m_iconItemSource->isValid()) {
|
||||
// if we started with a QIcon use that.
|
||||
QIcon icon = source.value<QIcon>();
|
||||
if (icon.isNull()) {
|
||||
icon = QIcon::fromTheme(sourceString, QIcon::fromTheme("application-x-desktop"));
|
||||
}
|
||||
m_iconItemSource.reset(new QIconSource(icon, this));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (source.canConvert<QIcon>()) {
|
||||
m_iconItemSource.reset(new QIconSource(source.value<QIcon>(), this));
|
||||
} else if (source.canConvert<QImage>()) {
|
||||
m_iconItemSource.reset(new QImageSource(source.value<QImage>(), this));
|
||||
} else {
|
||||
m_iconItemSource.reset(new NullSource(this));
|
||||
}
|
||||
|
||||
if (width() > 0 && height() > 0) {
|
||||
schedulePixmapUpdate();
|
||||
}
|
||||
|
||||
updateImplicitSize();
|
||||
|
||||
emit sourceChanged();
|
||||
|
||||
if (isValid() != oldValid) {
|
||||
Q_EMIT validChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant IconItem::source() const
|
||||
{
|
||||
return m_source;
|
||||
}
|
||||
|
||||
void IconItem::updateImplicitSize()
|
||||
{
|
||||
if (m_iconItemSource->isValid()) {
|
||||
const QSize s = m_iconItemSource->size();
|
||||
|
||||
if (s.isValid()) {
|
||||
if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) {
|
||||
setImplicitSize(s.width(), s.height());
|
||||
} else if (!m_implicitWidthSetByUser) {
|
||||
setImplicitWidth(s.width());
|
||||
} else if (!m_implicitHeightSetByUser) {
|
||||
setImplicitHeight(s.height());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to initializing implicit size to the Dialog size.
|
||||
const int implicitSize = 16;
|
||||
|
||||
if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) {
|
||||
setImplicitSize(implicitSize, implicitSize);
|
||||
} else if (!m_implicitWidthSetByUser) {
|
||||
setImplicitWidth(implicitSize);
|
||||
} else if (!m_implicitHeightSetByUser) {
|
||||
setImplicitHeight(implicitSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool IconItem::isValid() const
|
||||
{
|
||||
return m_iconItemSource->isValid();
|
||||
}
|
||||
|
||||
int IconItem::paintedWidth() const
|
||||
{
|
||||
return boundingRect().size().toSize().width();
|
||||
}
|
||||
|
||||
int IconItem::paintedHeight() const
|
||||
{
|
||||
return boundingRect().size().toSize().height();
|
||||
}
|
||||
|
||||
void IconItem::updateIcon()
|
||||
{
|
||||
updatePolish();
|
||||
}
|
||||
|
||||
void IconItem::updatePolish()
|
||||
{
|
||||
QQuickItem::updatePolish();
|
||||
loadPixmap();
|
||||
}
|
||||
|
||||
QSGNode *IconItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
|
||||
{
|
||||
Q_UNUSED(updatePaintNodeData)
|
||||
|
||||
if (m_iconPixmap.isNull() || width() == 0.0 || height() == 0.0) {
|
||||
delete oldNode;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ManagedTextureNode *textureNode = dynamic_cast<ManagedTextureNode *>(oldNode);
|
||||
|
||||
if (!textureNode || m_textureChanged) {
|
||||
delete oldNode;
|
||||
textureNode = new ManagedTextureNode;
|
||||
textureNode->setTexture(QSharedPointer<QSGTexture>(window()->createTextureFromImage(m_iconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas)));
|
||||
m_sizeChanged = true;
|
||||
m_textureChanged = false;
|
||||
}
|
||||
textureNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
|
||||
|
||||
if (m_sizeChanged) {
|
||||
const QSize newSize = QSize(paintedWidth(), paintedHeight());
|
||||
const QRect destRect(QPointF(boundingRect().center() - QPointF(newSize.width(), newSize.height()) / 2).toPoint(), newSize);
|
||||
textureNode->setRect(destRect);
|
||||
m_sizeChanged = false;
|
||||
}
|
||||
return textureNode;
|
||||
}
|
||||
|
||||
void IconItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
|
||||
{
|
||||
QQuickItem::itemChange(change, value);
|
||||
}
|
||||
|
||||
void IconItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
|
||||
{
|
||||
if (newGeometry.size() != oldGeometry.size()) {
|
||||
m_sizeChanged = true;
|
||||
|
||||
if (newGeometry.width() > 1 && newGeometry.height() > 1) {
|
||||
schedulePixmapUpdate();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
|
||||
const auto oldSize = qMin(oldGeometry.size().width(), oldGeometry.size().height());
|
||||
const auto newSize = qMin(newGeometry.size().width(), newGeometry.size().height());
|
||||
|
||||
if (!almost_equal(oldSize, newSize, 2)) {
|
||||
emit paintedSizeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QQuickItem::geometryChanged(newGeometry, oldGeometry);
|
||||
}
|
||||
|
||||
void IconItem::componentComplete()
|
||||
{
|
||||
QQuickItem::componentComplete();
|
||||
schedulePixmapUpdate();
|
||||
}
|
||||
|
||||
void IconItem::schedulePixmapUpdate()
|
||||
{
|
||||
polish();
|
||||
}
|
||||
|
||||
void IconItem::loadPixmap()
|
||||
{
|
||||
if (!isComponentComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int size = qMin(qRound(width()), qRound(height()));
|
||||
QPixmap result;
|
||||
|
||||
if (size <= 0) {
|
||||
m_iconPixmap = QPixmap();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_iconItemSource->isValid()) {
|
||||
result = m_iconItemSource->pixmap(QSize(size * qApp->devicePixelRatio(),
|
||||
size * qApp->devicePixelRatio()));
|
||||
result.setDevicePixelRatio(qApp->devicePixelRatio());
|
||||
} else {
|
||||
m_iconPixmap = QPixmap();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
m_oldIconPixmap = m_iconPixmap;
|
||||
m_iconPixmap = result;
|
||||
m_textureChanged = true;
|
||||
|
||||
update();
|
||||
}
|
100
src/iconitem.h
100
src/iconitem.h
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: cutefish <cutefishos@foxmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ICONITEM_H
|
||||
#define ICONITEM_H
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QIcon>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
class IconItemSource;
|
||||
class IconItem : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged)
|
||||
Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
|
||||
Q_PROPERTY(int paintedWidth READ paintedWidth NOTIFY paintedSizeChanged)
|
||||
Q_PROPERTY(int paintedHeight READ paintedHeight NOTIFY paintedSizeChanged)
|
||||
|
||||
public:
|
||||
explicit IconItem(QQuickItem *parent = nullptr);
|
||||
|
||||
void setSource(const QVariant &source);
|
||||
QVariant source() const;
|
||||
|
||||
void updateImplicitSize();
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
int paintedWidth() const;
|
||||
int paintedHeight() const;
|
||||
|
||||
Q_INVOKABLE void updateIcon();
|
||||
|
||||
void updatePolish() override;
|
||||
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override;
|
||||
|
||||
void itemChange(ItemChange change, const ItemChangeData &value) override;
|
||||
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||
|
||||
void componentComplete() override;
|
||||
|
||||
signals:
|
||||
void overlaysChanged();
|
||||
void activeChanged();
|
||||
void sourceChanged();
|
||||
void animatedChanged();
|
||||
void roundToIconSizeChanged();
|
||||
void validChanged();
|
||||
void colorGroupChanged();
|
||||
void paintedSizeChanged();
|
||||
void statusChanged();
|
||||
void implicitHeightChanged2();
|
||||
void implicitWidthChanged2();
|
||||
|
||||
private slots:
|
||||
void schedulePixmapUpdate();
|
||||
|
||||
private:
|
||||
void loadPixmap();
|
||||
|
||||
private:
|
||||
QSharedPointer<IconItemSource> m_iconItemSource;
|
||||
QVariant m_source;
|
||||
|
||||
bool m_active;
|
||||
bool m_animated;
|
||||
bool m_roundToIconSize;
|
||||
|
||||
bool m_textureChanged;
|
||||
bool m_sizeChanged;
|
||||
bool m_allowNextAnimation;
|
||||
bool m_blockNextAnimation;
|
||||
bool m_implicitHeightSetByUser;
|
||||
bool m_implicitWidthSetByUser;
|
||||
|
||||
QPixmap m_iconPixmap;
|
||||
QPixmap m_oldIconPixmap;
|
||||
};
|
||||
|
||||
#endif // ICONITEM_H
|
|
@ -26,7 +26,6 @@
|
|||
#include "applicationmodel.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "iconitem.h"
|
||||
#include "popupwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -35,7 +34,6 @@ int main(int argc, char *argv[])
|
|||
QApplication app(argc, argv);
|
||||
|
||||
qmlRegisterType<DockSettings>("Cutefish.Dock", 1, 0, "DockSettings");
|
||||
qmlRegisterType<IconItem>("Cutefish.Dock", 1, 0, "IconItem");
|
||||
qmlRegisterType<PopupWindow>("Cutefish.Dock", 1, 0, "PopupWindow");
|
||||
|
||||
QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-dock/translations/").arg(QLocale::system().name());
|
||||
|
|
|
@ -39,7 +39,7 @@ MainWindow::MainWindow(QQuickView *parent)
|
|||
, m_appModel(new ApplicationModel)
|
||||
, m_fakeWindow(nullptr)
|
||||
{
|
||||
// setDefaultAlphaBuffer(false);
|
||||
setDefaultAlphaBuffer(false);
|
||||
setColor(Qt::transparent);
|
||||
|
||||
setFlags(Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "managedtexturenode.h"
|
||||
|
||||
ManagedTextureNode::ManagedTextureNode()
|
||||
{
|
||||
}
|
||||
|
||||
void ManagedTextureNode::setTexture(QSharedPointer<QSGTexture> texture)
|
||||
{
|
||||
m_texture = texture;
|
||||
QSGSimpleTextureNode::setTexture(texture.data());
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef MANAGEDTEXTURENODE_H
|
||||
#define MANAGEDTEXTURENODE_H
|
||||
|
||||
#include <QSGSimpleTextureNode>
|
||||
#include <QSGTexture>
|
||||
#include <QSharedPointer>
|
||||
#include <qglobal.h>
|
||||
|
||||
/**
|
||||
* @class ManagedTextureNode managedtexturenode.h KQuickAddons/ManagedTextureNode
|
||||
*
|
||||
* @short Node that contains a reference counted texture
|
||||
*
|
||||
* Usually when assigning textures within a node, we'll want to delete the
|
||||
* texture with the node. This class will take a shared texture and display it
|
||||
* within the node.
|
||||
*
|
||||
* It's especially interesting to use this class together with the ImageTexturesCache
|
||||
* that will offer us shareable textures and cache them transparently, when asking
|
||||
* it to create the texture.
|
||||
*
|
||||
* @see ImageTexturesCache
|
||||
*/
|
||||
|
||||
class ManagedTextureNode : public QSGSimpleTextureNode
|
||||
{
|
||||
Q_DISABLE_COPY(ManagedTextureNode)
|
||||
|
||||
public:
|
||||
ManagedTextureNode();
|
||||
|
||||
void setTexture(QSharedPointer<QSGTexture> texture);
|
||||
|
||||
private:
|
||||
QSharedPointer<QSGTexture> m_texture;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#define TOPLEVELMENU_H
|
||||
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
|
||||
class PopupWindow : public QQuickWindow
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue