Flutter:flame, ComposedComponent cant be mixed onto PositionComponent - flutter

I am making a game in flutter, and i found this link in github https://github.com/g0rdan/Flutter.Bird and i tried to run it in my computer and i encounter this error error: 'ComposedComponent' can't be mixed onto 'PositionComponent' because 'PositionComponent' doesn't implement 'HasGameRef'. (mixin_application_not_implemented_interface at [myfirstgame] lib\game\bird.dart:16)
enum BirdStatus { waiting, flying}
enum BirdFlyingStatus { up, down, none }
class Bird extends PositionComponent with ComposedComponent { == from this line
int _counter = 0;
int _movingUpSteps = 15;
Size _screenSize;
double _heightDiff = 0.0;
double _stepDiff = 0.0;
BirdGround ground;
BirdStatus status = BirdStatus.waiting;
BirdFlyingStatus flyingStatus = BirdFlyingStatus.none;
Bird(Image spriteImage, Size screenSize)
{
_screenSize = screenSize;
List<Sprite> sprites = [
Sprite.fromImage(
spriteImage,
width: SpriteDimensions.birdWidth,
height: SpriteDimensions.birdHeight,
y: SpritesPostions.birdSprite1Y,
x: SpritesPostions.birdSprite1X,
),
Sprite.fromImage(
spriteImage,
width: SpriteDimensions.birdWidth,
height: SpriteDimensions.birdHeight,
y: SpritesPostions.birdSprite2Y,
x: SpritesPostions.birdSprite2X,
),
Sprite.fromImage(
spriteImage,
width: SpriteDimensions.birdWidth,
height: SpriteDimensions.birdHeight,
y: SpritesPostions.birdSprite3Y,
x: SpritesPostions.birdSprite3X,
)
];
var animatedBird = new Animation.spriteList(sprites, stepTime: 0.15);
this.ground = BirdGround(animatedBird);
this..add(ground);
}
void setPosition(double x, double y) {
this.ground.x = x;
this.ground.y = y;
}
void update(double t) {
if (status == BirdStatus.flying) {
_counter++;
if (_counter <= _movingUpSteps) {
flyingStatus = BirdFlyingStatus.up;
this.ground.showAnimation = true;
this.ground.angle -= 0.01;
this.ground.y -= t * 100 * getSpeedRatio(flyingStatus, _counter);
}
else {
flyingStatus = BirdFlyingStatus.down;
this.ground.showAnimation = false;
if (_heightDiff == 0)
_heightDiff = (_screenSize.height - this.ground.y);
if (_stepDiff == 0)
_stepDiff = this.ground.angle.abs() / (_heightDiff / 10);
this.ground.angle += _stepDiff;
this.ground.y += t * 100 * getSpeedRatio(flyingStatus, _counter);
}
this.ground.update(t);
}
}
double getSpeedRatio(BirdFlyingStatus flyingStatus, int counter){
if (flyingStatus == BirdFlyingStatus.up) {
var backwardCounter = _movingUpSteps - counter;
return backwardCounter / 10.0;
}
if (flyingStatus == BirdFlyingStatus.down) {
var diffCounter = counter - _movingUpSteps;
return diffCounter / 10.0;
}
return 0.0;
}
void jump() {
Flame.audio.play('wing.wav');
status = BirdStatus.flying;
_counter = 0;
this.ground.angle = 0;
}
}
class BirdGround extends AnimationComponent {
bool showAnimation = true;
BirdGround(Animation animation)
: super(ComponentDimensions.birdWidth, ComponentDimensions.birdHeight, animation);
#override
void update(double t){
if (showAnimation) {
super.update(t);
}
}
}

This uses a very old Flame version, so I would recommend not building anything on top of it.
But to your problem, it is missing the HasGameRef mixin on your component, so if you write something like this it should work:
class Bird extends PositionComponent with HasGameRef<YourGameClass>, ComposedComponent { ...

Related

Flutter Flame priority value is not working

I have SpriteComponent named Obstacle. I set the priority value to 0 in the super method. But the Obstacle component is regenerated with the update method. It disappears when it exits the screen. When it reoccurs, the priorty value becomes invalid. I am sharing sample screenshots.
first img
second img
The player stays behind the obstacle. GameOverPanel always at the back.
#override
Future<void> onLoad() async {
add(ScreenHitbox());
add(gameRef.homeBackgroundParallaxComponent);
add(gameRef.homeBaseParallax);
add(gameRef.obstacleManager..priority = 0);
add(gameRef.playerManager..priority = 1);
add(gameRef.gameOverPanel..priority = 2);
}
#override
void update(double dt) {
super.update(dt);
gameRef.obstacleManager.updateObstacle(GameConst.gameSpeed * dt);
gameRef.playerManager.changePositionDown(value: GameConst.gravity * dt);
}
Game Over Panel
class GameOverPanel extends Component with HasGameRef<HopyBirdGame> {
GameOverPanel() : super(priority: 4);
#override
Future<void> onLoad() async {
add(GameOverText());
}
#override
void renderTree(Canvas canvas) {
if (gameRef.gameOver) {
super.renderTree(canvas);
}
}
}
class GameOverText extends SpriteComponent with HasGameRef<HopyBirdGame> {
GameOverText() : super(size: Vector2(300, 80), anchor: Anchor.center);
#override
Future<void>? onLoad() async {
sprite = await gameRef.loadSprite(Assets.gameOver.path);
position = Vector2(gameRef.screenWidth / 2, gameRef.screenHeight / 2);
return super.onLoad();
}
}
-Obstacle
class Obstacle extends SpriteComponent
with HasGameRef<HopyBirdGame>, CollisionCallbacks {
final ObstacleType obstacleType;
Obstacle({
required this.obstacleType,
super.position,
super.priority,
}) : super(
size: Vector2(GameConst.obstacleWidth, GameConst.obstacleHeight),
anchor: Anchor.center,
);
Obstacle.empty({this.obstacleType = ObstacleType.empty});
#override
Future<void> onLoad() async {
switch (obstacleType) {
case ObstacleType.up:
sprite = await gameRef.loadSprite(
Assets.redPipeUp.path,
);
break;
case ObstacleType.down:
sprite = await gameRef.loadSprite(
Assets.redPipeDown.path,
);
break;
case ObstacleType.empty:
return;
}
add(RectangleHitbox());
}
}
class Obstacles extends Component with HasGameRef<HopyBirdGame> {
final Obstacle upObstacle;
final Obstacle downObstacle;
bool isCreateNextObstacles = true;
Obstacles({
required this.upObstacle,
required this.downObstacle,
super.priority,
});
#override
Future<void>? onLoad() {
upObstacle.priority = 1;
downObstacle.priority = 1;
createChildren();
return super.onLoad();
}
void createChildren() {
final children = [upObstacle, downObstacle];
addAll(children);
}
}
Obstacle Manager
class ObstacleManager extends Component with HasGameRef<HopyBirdGame> {
ObstacleManager() : super(priority: 1);
ListQueue<Obstacles> history = ListQueue();
#override
Future<void> onLoad() async {
createObstacles();
}
void updateObstacle(double value) {
if (!gameRef.gameOver) {
if (history.isNotEmpty) {
history.last.upObstacle.position.x -= value;
history.last.downObstacle.position.x -= value;
if (history.last.upObstacle.position.x <= gameRef.screenWidth / 2) {
createObstacles();
final isThereAnyObstaclesOutsideScreen = history.any(
(element) =>
element.downObstacle.position.x <=
-(GameConst.obstacleWidth / 2),
);
if (isThereAnyObstaclesOutsideScreen) {
final obstaclesOutsideScreen = history
.lastWhere((element) => element.downObstacle.position.x <= 0);
obstaclesOutsideScreen.removeFromParent();
gameRef.remove(obstaclesOutsideScreen);
}
}
if (history.length >= 2) {
history.elementAt(history.length - 2).upObstacle.position.x -= value;
history.elementAt(history.length - 2).downObstacle.position.x -=
value;
}
}
}
}
void createObstacles() {
final obstaclesGap =
gameRef.screenHeight - gameRef.difficulty.spaceForPlayers;
final gapAboveTheObstacleOnScreen =
((2 * GameConst.obstacleHeight) - obstaclesGap) / 2;
final randomNumber = _randomGenerator(
min: -gameRef.difficulty.distanceAxisYToGoUpAndDown,
max: gameRef.difficulty.distanceAxisYToGoUpAndDown,
);
final axisYOfUpObstacle = (GameConst.obstacleHeight / 2) -
gapAboveTheObstacleOnScreen +
randomNumber;
final axisYOfDownObstacle = axisYOfUpObstacle +
GameConst.obstacleHeight +
gameRef.difficulty.spaceForPlayers;
final obstacles = Obstacles(
upObstacle: Obstacle(
obstacleType: ObstacleType.up,
position: Vector2(
gameRef.screenWidth + (GameConst.obstacleWidth / 2),
axisYOfUpObstacle,
),
),
downObstacle: Obstacle(
obstacleType: ObstacleType.down,
position: Vector2(
gameRef.screenWidth + (GameConst.obstacleWidth / 2),
axisYOfDownObstacle,
),
),
);
history.add(obstacles);
gameRef.add(obstacles);
}
double _randomGenerator({required double min, required double max}) {
final gap = (max - min).toInt();
return min + Random().nextInt(gap);
}
}
EDIT: The same problem applies in game over panel.
Game Over Panel
class GameOverPanel extends Component with HasGameRef<HopyBirdGame> {
GameOverPanel() : super();
#override
Future<void> onLoad() async {
add(GameOverText());
}
#override
void renderTree(Canvas canvas) {
if (gameRef.gameOver) {
super.renderTree(canvas);
}
}
}
class GameOverText extends SpriteComponent with HasGameRef<HopyBirdGame> {
GameOverText()
: super(
size: Vector2(300, 80),
anchor: Anchor.center,
);
#override
Future<void>? onLoad() async {
sprite = await gameRef.loadSprite(Assets.gameOver.path);
position = Vector2(gameRef.screenWidth / 2, gameRef.screenHeight / 2);
return super.onLoad();
}
}
Similarly, the first assignment is made in the Flame Game class.
I'm guessing (since the code for the ObstacleManager isn't present) that you are adding the obstacles directly to the game from the ObstacleManager to the component tree and that you aren't adding the priority to the actual Obstacle components.
If this is not the case, please update the question with the code for the ObstacleManager.
EDIT: After code upload.
It doesn't help that you are giving priorities to the manager classes when the components are not added as children to them, but to the gameRef. Since the player manager class isn't included in the question you either have to set the Obstacles to priority -1, or you have to give the player priority 1 (and obstacles will default to 0).
gameRef.add(obstacles..priority = -1);
This will make the obstacles be behind the background, so you have to also change the background parallaxes:
add(gameRef.homeBackgroundParallaxComponent..priority = -3);
add(gameRef.homeBaseParallax..priority = -2);
So it will be better to just set the player priority to 1 where you are adding that one to the game, because then you don't have to change all the other classes.

PolygonShape created at different position

I'm trying to create a polygon at the center of the screen with a mouse joint, very simple.
A CircleShape works great. Also the mouse joint behaves strangely and I couldn't find a pattern.
All code is in the main file here. I kept the code to a minimum.
Vector2 vec2Median(List<Vector2> vecs) {
var sum = Vector2(0, 0);
for (final v in vecs) {
sum += v;
}
return sum / vecs.length.toDouble();
}
void main() {
final game = MyGame();
runApp(GameWidget(game: game));
}
class MyGame extends Forge2DGame with MultiTouchDragDetector, HasTappables {
MouseJoint? mouseJoint;
static late BodyComponent grabbedBody;
late Body groundBody;
MyGame() : super(gravity: Vector2(0, -10.0));
#override
Future<void> onLoad() async {
final boundaries = createBoundaries(this); //Adding boundries
boundaries.forEach(add);
groundBody = world.createBody(BodyDef());
final center = screenToWorld(camera.viewport.effectiveSize / 2);
final poly = Polygon([
center + Vector2(0, 0),
center + Vector2(0, 5),
center + Vector2(5, 0),
center + Vector2(5, 5)
], bodyType: BodyType.dynamic);
add(poly);
grabbedBody = poly;
}
#override
bool onDragUpdate(int pointerId, DragUpdateInfo details) {
final mouseJointDef = MouseJointDef()
..maxForce = 3000 * grabbedBody.body.mass * 10 //Not neccerly needed
..dampingRatio = 1
..frequencyHz = 5
..target.setFrom(grabbedBody.body.position)
..collideConnected = false //Maybe set to true
..bodyA = groundBody
..bodyB = grabbedBody.body;
mouseJoint ??= world.createJoint(mouseJointDef) as MouseJoint;
mouseJoint?.setTarget(details.eventPosition.game);
return false;
}
#override
bool onDragEnd(int pointerId, DragEndInfo details) {
if (mouseJoint == null) {
return true;
}
world.destroyJoint(mouseJoint!);
mouseJoint = null;
return false;
}
}
abstract class TappableBodyComponent extends BodyComponent with Tappable {
final Vector2 position;
final BodyType bodyType;
TappableBodyComponent(this.position, {this.bodyType = BodyType.dynamic});
#override
bool onTapDown(_) {
MyGame.grabbedBody = this;
return false;
}
Body tappableBCreateBody(Shape shape) {
final fixtureDef = FixtureDef(shape)
..restitution = 0.8
..density = 1.0
..friction = 0.4;
final bodyDef = BodyDef()
// To be able to determine object in collision
..userData = this
..angularDamping = 0.8
..position = position
..type = bodyType;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
class Polygon extends TappableBodyComponent {
final List<Vector2> vertecies;
Polygon(this.vertecies, {BodyType bodyType = BodyType.dynamic})
: super(vec2Median(vertecies), bodyType: bodyType);
#override
Body createBody() {
final shape = PolygonShape()..set(vertecies);
return tappableBCreateBody(shape);
}
}
tappableBCreateBody encapsulate Tappable and body creation methods, Polygon is the object I'm trying to create, vec2Median returns the center of the polygon (by vertices).
Thank you very much!
I think that you have to remove center from the vertices and only add that as the position of the BodyComponent instead, like you already do in the super call of your Polygon class.

Process camera image in flutter

I'm trying to process the camera image input in Flutter but I can't seem to get it to work.
I have a listener on the camera feed that launch a function that is supposed to process the images btu everytime I end up doing anything in the function the program interface freeze. My guess is that I have too many frames to process and thus the program freezes but I have no idea how to ignore old frames.
Here is my code so far
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:image/image.dart' as imglib;
import 'package:flutter/scheduler.dart';
import 'user_data_container.dart';
class Camera extends StatefulWidget {
Camera();
#override
_CameraState createState() => new _CameraState();
}
class _CameraState extends State<Camera> {
CameraController controller;
bool isDetecting = false;
double redavg;
imglib.Image last_image;
int count = 0;
Image _image_display;
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
_initializeApp();
});
}
void _initializeApp() async {
var cameras = UserDataContainer.of(context).data.cameras;
if (cameras == null || cameras.length < 1) {
print('No camera is found');
} else {
controller = new CameraController(
cameras[0],
ResolutionPreset.high,
);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
controller.startImageStream((CameraImage img) async {
print(count.toString() + "Stream. detecting: " + isDetecting.toString());
if (!isDetecting) {
isDetecting = true;
int startTime = new DateTime.now().millisecondsSinceEpoch;
_processCameraImage(img);
// int endTime = new DateTime.now().millisecondsSinceEpoch;
// print("Detection done in " + (endTime - startTime).toString());
// isDetecting = false;
}
else{
print("It's detecting");
}
});
});
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (controller == null || !controller.value.isInitialized) {
return Container();
}
return
Scaffold(
body: Column(
children: <Widget>[
Text("red: "+ redavg.toString() + " "+ count.toString()),
Expanded(child: last_image == null ? Container() : _image_display),
],
),
);
// );
}
void _processCameraImage(CameraImage image) async {
count = count +1;
print("p: " + image.planes.length.toString());
// if (isDetecting) {
// print("Already detecting;");
// return;
// }
// else{
// isDetecting = true;
try {
await processFrame(image);
} catch (e) {
// await handleExepction(e)
} finally {
print("Done detecting :)");
// isDetecting = false;
}
// }
}
void processFrame(CameraImage image) async {
print ("convert");
imglib.Image convertedImage = await _convertCameraImage(image);
last_image = convertedImage;
imglib.PngEncoder pngEncoder = new imglib.PngEncoder(level: 0, filter: 0);
// Convert to png
List<int> png = pngEncoder.encodeImage(last_image);
_image_display = Image.memory(png);
// var colors = colorAverage(convertedImage);
setState(() {
// redavg = colors[2];
// print(colors.toString());
});
}
imglib.Image _convertCameraImage(CameraImage image) {
int width = image.width;
int height = image.height;
var img = imglib.Image(image.planes[0].bytesPerRow, height); // Create Image buffer
const int hexFF = 0xFF000000;
final int uvyButtonStride = image.planes[1].bytesPerRow;
final int uvPixelStride = image.planes[1].bytesPerPixel;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final int uvIndex =
uvPixelStride * (x / 2).floor() + uvyButtonStride * (y / 2).floor();
final int index = y * width + x;
final yp = image.planes[0].bytes[index];
final up = image.planes[1].bytes[uvIndex];
final vp = image.planes[2].bytes[uvIndex];
// Calculate pixel color
int r = (yp + vp * 1436 / 1024 - 179).round().clamp(0, 255);
int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91)
.round()
.clamp(0, 255);
int b = (yp + up * 1814 / 1024 - 227).round().clamp(0, 255);
// color: 0x FF FF FF FF
// A B G R
img.data[index] = hexFF | (b << 16) | (g << 8) | r;
}
}
// Rotate 90 degrees to upright
// var img1 = imglib.copyRotate(img, 90);
imglib.PngEncoder pngEncoder = new imglib.PngEncoder(level: 0, filter: 0);
// Convert to png
List<int> png = pngEncoder.encodeImage(img);
_image_display = Image.memory(png);
return img;
}
}

My Program Creates 1 Ball not 2

I made a program that creates a frame and then creates 2 balls and moves them around seperately, my problem is that somehow the first or second ball is getting the coordinates of the other, and therefore are being painted on each other, sorry for the bad indentation this is my first time posting a question .
Main Class:
public class Game extends JPanel {
Ball ball01 = new Ball();
Ball ball02 = new Ball();
int ball01x = 0,ball01y = 0,ball02x = 0,ball02y = 0;
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.fillOval(ball01x, ball01y, 10, 10);
g2d.setColor(Color.RED);
g2d.fillOval(ball02x, ball02y, 10, 10);
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Sample Frame");
Game game = new Game();
frame.add(game);
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.createBalls();
while (true) {
game.getCoords();
game.updateBalls();
game.repaint();
System.out.println("Ball01 x: " + game.ball01x + " Ball02 x " + game.ball02x);
Thread.sleep(10);
}
}
public void getCoords(){
ball01x = ball01.getX();
ball01y = ball01.getY();
ball02x = ball02.getX();
ball02y = ball02.getY();
}
public void createBalls(){
ball01.create(300,300);
ball02.create(50,20);
}
public void updateBalls(){
ball01.Ball();
ball02.Ball();
}
}
Ball Class:
public class Ball{
private static int x;
private static int y;
private static int xSize = 30;
private static int ySize = 30;
static boolean xright = true,ydown = true;
static boolean grow = false;
public void Ball(){
this.moveBall();
this.ballSize();
}
public void create(int startX, int startY){
this.x = startX;
this.y = startY;
}
private void moveBall() {
if(this.x >= 370){
xright = false;
}
if(this.x <= 0){
xright = true;
}
if(this.y >= 370){
ydown = false;
}
if(this.y < 0){
ydown = true;
}
if(xright == true){
this.x = this.x + 1;
}else if (xright == false){
this.x = this.x - 1;
}
if(ydown == true){
this.y = this.y + 1;
}else if (ydown == false){
this.y = this.y - 1;
}
}
private void ballSize(){
if (xSize <= 5 && ySize <= 5){
grow = true;
}else if (xSize >= 10 && ySize >= 10){
grow = false;
}
if (grow == true){
xSize = xSize + 1;
ySize = ySize + 1;
//System.out.println("Debug");
}else if (grow == false){
xSize--;
ySize--;
//System.out.println("Debug");
}
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}

Check manually a JMenuItem in a JPopUpMenu?

I have a JPopUpMenu with several JCheckBoxMenuItem's on it.
Actually what I would like to do is basicly to select an item of the JPopUpMenu with a specific index.
For exemple, a method like myPopUpMenu.setSelected(2), which would select "Algérie" in my JPopUpMenu.
The problem is that I don't know any method which would allow me to check an item manually...
Here's the code of my JPopUpMenu :
MainVue.java:
public class MainVue extends JFrame implements ActionListener {
private static final JScrollPopupMenu menuProduit = new JScrollPopupMenu();
private static final JScrollPopupMenu menuPays = new JScrollPopupMenu();
private static List<String> listeFiltres = new ArrayList<String>();
private String listeDeFiltres;
private String[] tableauFiltrePermanent;
private String listeFiltrePermanent;
private String[] tableauPays = { "Autres", "Afrique du sud", "Algérie", "Allemagne", "Arabie Saoudite", "Argentine",
"Australie", "Bangladesh", "Belgique", "Brésil", "Bulgarie", "Canada", "Chine", "Corée du sud", "Egypte",
"Emirats-Arabes Unis", "Espagne", "Etats-Unis", "Ethiopie", "Europe", "France", "Hongrie", "Inde",
"Indonésie", "Irak", "Iran", "Israél", "Italie", "Japon", "Jordanie", "Kazakhstan", "Koweit", "Liban",
"Libye", "Malaisie", "Maroc", "Mexique", "Monde", "Oman", "Pakistan", "Pays-Bas", "Philippines", "Poligne",
"Portugal", "Qatar", "République tchéque", "Roumanie", "Russie", "Taïwan", "Tunisie", "Turquie",
"Ukraine" };
private String[] tableauProduit = { "Blé", "Colza", "Mais", "Orge", "Orge de Brasserie", "Palme", "Soja",
"Tournesol", "Tourteaux De Colza", "Tourteaux de Soja", "Huile de Soja", "Huile De Colza" };
private List<JCheckBoxMenuItem> listJCBProduit = new ArrayList<JCheckBoxMenuItem>();
private List<JCheckBoxMenuItem> listJCBPays = new ArrayList<JCheckBoxMenuItem>();
public static PropertiesConfiguration prop;
public MainVue(Modele modele, Controleur controleur) throws ClassNotFoundException, SQLException, IOException {
prop = new PropertiesConfiguration("config.properties");
for (int i = 0; i < tableauProduit.length; i++) {
listJCBProduit.add(new JCheckBoxMenuItem(tableauProduit[i]));
}
for (int j = 0; j < listJCBProduit.size(); j++) {
JCheckBoxMenuItem produitActuel = listJCBProduit.get(j);
menuProduit.add(produitActuel);
produitActuel.addActionListener(new OpenAction(menuProduit, boutonProduit));
}
for (int i = 0; i < tableauPays.length; i++) {
listJCBPays.add(new JCheckBoxMenuItem(tableauPays[i]));
}
for (int j = 0; j < listJCBPays.size(); j++) {
JCheckBoxMenuItem paysActuel = listJCBPays.get(j);
menuPays.add(paysActuel);
paysActuel.addActionListener(new OpenAction(menuPays, boutonPays));
}
listeDeFiltres = "";
for (int p = 0; p < listeFiltres.size(); p++) {
String filtreActuel = listeFiltres.get(p);
if (listeDeFiltres == "") {
listeDeFiltres += filtreActuel;
} else {
listeDeFiltres += "," + filtreActuel;
}
}
prop.setProperty("listeFiltres", listeDeFiltres);
}
}
Here's the JScrollPopUpMenu component :
JScrollPopUpMenu.java:
package fr.views;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
public class JScrollPopupMenu extends JPopupMenu {
protected int maximumVisibleRows = 10;
public JScrollPopupMenu() {
this(null);
}
public JScrollPopupMenu(String label) {
super(label);
setLayout(new ScrollPopupMenuLayout());
super.add(getScrollBar());
addMouseWheelListener(new MouseWheelListener() {
#Override public void mouseWheelMoved(MouseWheelEvent event) {
JScrollBar scrollBar = getScrollBar();
int amount = (event.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
? event.getUnitsToScroll() * scrollBar.getUnitIncrement()
: (event.getWheelRotation() < 0 ? -1 : 1) * scrollBar.getBlockIncrement();
scrollBar.setValue(scrollBar.getValue() + amount);
event.consume();
}
});
}
private JScrollBar popupScrollBar;
protected JScrollBar getScrollBar() {
if(popupScrollBar == null) {
popupScrollBar = new JScrollBar(JScrollBar.VERTICAL);
popupScrollBar.addAdjustmentListener(new AdjustmentListener() {
#Override public void adjustmentValueChanged(AdjustmentEvent e) {
doLayout();
repaint();
}
});
popupScrollBar.setVisible(false);
}
return popupScrollBar;
}
public int getMaximumVisibleRows() {
return maximumVisibleRows;
}
public void setMaximumVisibleRows(int maximumVisibleRows) {
this.maximumVisibleRows = maximumVisibleRows;
}
public void paintChildren(Graphics g){
Insets insets = getInsets();
g.clipRect(insets.left, insets.top, getWidth(), getHeight() - insets.top - insets.bottom);
super.paintChildren(g);
}
protected void addImpl(Component comp, Object constraints, int index) {
super.addImpl(comp, constraints, index);
if(maximumVisibleRows < getComponentCount()-1) {
getScrollBar().setVisible(true);
}
}
public void remove(int index) {
// can't remove the scrollbar
++index;
super.remove(index);
if(maximumVisibleRows >= getComponentCount()-1) {
getScrollBar().setVisible(false);
}
}
public void show(Component invoker, int x, int y){
JScrollBar scrollBar = getScrollBar();
if(scrollBar.isVisible()){
int extent = 0;
int max = 0;
int i = 0;
int unit = -1;
int width = 0;
for(Component comp : getComponents()) {
if(!(comp instanceof JScrollBar)) {
Dimension preferredSize = comp.getPreferredSize();
width = Math.max(width, preferredSize.width);
if(unit < 0){
unit = preferredSize.height;
}
if(i++ < maximumVisibleRows) {
extent += preferredSize.height;
}
max += preferredSize.height;
}
}
Insets insets = getInsets();
int widthMargin = insets.left + insets.right;
int heightMargin = insets.top + insets.bottom;
scrollBar.setUnitIncrement(unit);
scrollBar.setBlockIncrement(extent);
scrollBar.setValues(0, heightMargin + extent, 0, heightMargin + max);
width += scrollBar.getPreferredSize().width + widthMargin;
int height = heightMargin + extent;
setPopupSize(new Dimension(width, height));
}
super.show(invoker, x, y);
}
protected static class ScrollPopupMenuLayout implements LayoutManager{
#Override public void addLayoutComponent(String name, Component comp) {}
#Override public void removeLayoutComponent(Component comp) {}
#Override public Dimension preferredLayoutSize(Container parent) {
int visibleAmount = Integer.MAX_VALUE;
Dimension dim = new Dimension();
for(Component comp :parent.getComponents()){
if(comp.isVisible()) {
if(comp instanceof JScrollBar){
JScrollBar scrollBar = (JScrollBar) comp;
visibleAmount = scrollBar.getVisibleAmount();
}
else {
Dimension pref = comp.getPreferredSize();
dim.width = Math.max(dim.width, pref.width);
dim.height += pref.height;
}
}
}
Insets insets = parent.getInsets();
dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
return dim;
}
#Override public Dimension minimumLayoutSize(Container parent) {
int visibleAmount = Integer.MAX_VALUE;
Dimension dim = new Dimension();
for(Component comp : parent.getComponents()) {
if(comp.isVisible()){
if(comp instanceof JScrollBar) {
JScrollBar scrollBar = (JScrollBar) comp;
visibleAmount = scrollBar.getVisibleAmount();
}
else {
Dimension min = comp.getMinimumSize();
dim.width = Math.max(dim.width, min.width);
dim.height += min.height;
}
}
}
Insets insets = parent.getInsets();
dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
return dim;
}
#Override public void layoutContainer(Container parent) {
Insets insets = parent.getInsets();
int width = parent.getWidth() - insets.left - insets.right;
int height = parent.getHeight() - insets.top - insets.bottom;
int x = insets.left;
int y = insets.top;
int position = 0;
for(Component comp : parent.getComponents()) {
if((comp instanceof JScrollBar) && comp.isVisible()) {
JScrollBar scrollBar = (JScrollBar) comp;
Dimension dim = scrollBar.getPreferredSize();
scrollBar.setBounds(x + width-dim.width, y, dim.width, height);
width -= dim.width;
position = scrollBar.getValue();
}
}
y -= position;
for(Component comp : parent.getComponents()) {
if(!(comp instanceof JScrollBar) && comp.isVisible()) {
Dimension pref = comp.getPreferredSize();
comp.setBounds(x, y, width, pref.height);
y += pref.height;
}
}
}
}
}
Thanks in advance for any help !
The index which you get from getIndex() method use as follows. You are adding ScrollBar in your JScrollPopupMenu at 0 index. So to remove casting error update your code as follow.
int index = getIndex("name");
((JCheckBoxMenuItem)menuProduit.getComponentAtIndex(index+1)).setState(true);